<commit_before>var exec        = require('child_process').exec;
var deferred    = require('deferred');
var config      = require('../config');
var discogs     = require('./Discogs');

module.exports = function processMusic(error, stdout, stderr) {
    if (error) {
        console.log(stderr);
        return;
    }

    var artists     = stdout.split('\n');
    var musicData   = [];
    var responses   = [];

    artists.forEach(function (artist) {
        if (artist == '')
            return;

        console.log('ARTIST: ' + artist);
        var response = deferred();
        responses.push(response.promise);

        discogs.artist(artist)(
            function (data) {
                musicData.push(data);
                response.resolve();
            }
        );

        // exec('ls ' + config.musicpath + artist, function (error, stdout, stderr) { processAlbums(artist, error, stdout, stderr); });
    });

    return deferred.apply(null, responses)(function (data) { renderMusic(musicData); });
};

function processAlbums(artist, error, stdout, stderr) {
    if (error) {
        console.log(stderr);
        return;
    }

    var albums = stdout.split('\n');

    albums.forEach(function (album) {
        if (album == '')
            return;

        var title   = album.split(' ');
        var year    = title.pop().slice(1, 5);
        title       = title.join(' ');

        discogs.album(title, year, artist)(
            function (data) {
                console.log(data);
            }
        );
    });
}

function renderMusic(musicData) {
    console.log(musicData);

    var html = '';

    return html;
}
<commit_msg>Multiple artists with multiple albums now works correctly, successfully stors all relative information in an easily traversible array of objects<commit_after>var exec        = require('child_process').exec;
var deferred    = require('deferred');
var config      = require('../config');
var discogs     = require('./Discogs');

module.exports = function processMusic(error, stdout, stderr) {
    if (error) {
        console.log(stderr);
        return;
    }

    var artists     = stdout.split('\n');
    var musicData   = [];
    var responses   = [];

    artists.forEach(function (artist) {
        if (artist == '')
            return;
        var artistData  = {};
        var response    = deferred();
        var _response   = deferred();
        responses.push(response.promise);
        responses.push(_response.promise);

        discogs.artist(artist)(
            function (data) {
                artistData.artist = data;
                _response.resolve();
            }
        );

        processAlbums(artist)(
            function (data) {
                artistData.albums = data;
                musicData.push(artistData);
                response.resolve();
            }
        );
    });

    return deferred.apply(null, responses)(function (data) { renderMusic(musicData); });
};

function processAlbums(artist) {
    var response = deferred();

    exec('ls "' + config.musicpath + artist + '"', (error, stdout, stderr) => {
        if (error) {
            console.log(error);
            return;
        }

        var responses   = [];
        var albumData   = [];
        var albums      = stdout.split('\n');

        albums.forEach(function (album) {
            if (album == '')
                return;

            var response    = deferred();
            var title       = album.split(' ');
            var year        = title.pop().slice(1, 5);
            title           = title.join(' ');

            responses.push(response.promise);
            discogs.album(title, year, artist)(
                function (data) {
                    albumData.push(data);
                    response.resolve();
                }
            );
        });

        return deferred.apply(null, responses)(function (data) { return response.resolve(albumData); });
    });

    return response.promise;
}

function renderMusic(musicData) {
    console.log(musicData);

    var html = '';

    return html;
}
<|endoftext|>


<commit_before>
function Unit () {}

Unit.prototype.setByPath = function (collection, path, value) {
	var keys = path.split('.');
	var last = keys.length - 1;

	for (var i = 0, key; i < last; i++) {
		key = keys[i];
		if (collection[key] === undefined) collection[key] = {};
		collection = collection[key];
	}

	return collection[keys[last]] = value;
};

Unit.prototype.toCamelCase = function (data) {
	if (data.constructor.name === 'Array') data = data.join('-');
	return data.replace(/-[a-z]/g, function (match) {
		return match[1].toUpperCase();
	});
};

Unit.prototype.renderMethods = {
	on: function () {
		var eventName = this.attribute.cmds[1];
		this.element.removeEventListener(eventName, this.data, false);
		this.element.addEventListener(eventName, this.data, false);
	},
	each: function () {
		var self = this, animate;

		if (!self.clone) {

			self.variable = self.attribute.cmds.slice(1).join('.');
			self.clone = self.element.removeChild(self.element.children[0]).outerHTML;
			self.pattern = new RegExp('(((data-)?j(-(\\w)+)+="))' + self.variable + '(((\\.(\\w)+)+)?((\\s+)?\\|((\\s+)?(\\w)+)+)?(\\s+)?")', 'g');

			animate = function () {

				self.element.insertAdjacentHTML(
					'beforeend',
					self.clone.replace(
						self.pattern, '$1' + self.attribute.path + '.' + self.element.children.length + '$6'
					)
				);

				self.view.addOne(self.element.lastChild);
				self.view.addAll(self.element.lastChild.getElementsByTagName('*'));

				if (self.element.children.length < self.data.length) {
					window.requestAnimationFrame(animate);
				}

			};

			window.requestAnimationFrame(animate);

		} else if (self.element.children.length > self.data.length) {

			animate = function () {

				if (self.element.children.length > self.data.length) {

					self.view.removeAll(self.element.lastChild.getElementsByTagName('*'));
					self.view.removeOne(self.element.lastChild);
					self.element.removeChild(self.element.lastChild);

					if (self.element.children.length > self.data.length) {
						window.requestAnimationFrame(animate);
					}

				}

			};

			window.requestAnimationFrame(animate);

		} else if (self.element.children.length < self.data.length) {

			animate = function () {

				if (self.element.children.length < self.data.length) {

					self.element.insertAdjacentHTML(
						'beforeend',
						self.clone.replace(
							self.pattern, '$1' + self.attribute.path + '.' + self.element.children.length + '$6'
						)
					);

					self.view.addOne(self.element.lastChild);
					self.view.addAll(self.element.lastChild.getElementsByTagName('*'));

					if (self.element.children.length < self.data.length) {
						window.requestAnimationFrame(animate);
					}

				}

			};

			window.requestAnimationFrame(animate);

		}

	},
	value: function () {
		var self = this;

		if (self.change) return;
		if (self.element.type === 'button' || self.element.type === 'reset') return self.change = true;

		self.change = function () {
			self.data = self.element.type !== 'radio' && self.element.type !== 'checked' ? self.element.value : self.element.checked;
		};

		self.element.addEventListener('change', self.change.bind(self), true);
		self.element.addEventListener('keyup', self.change.bind(self), true);
	},
	html: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.innerHTML = self.data;
			self.view.addAll(self.element.getElementsByTagName('*'));
		});
	},
	css: function () {
		var self = this;
		var css = this.data;

		if (self.attribute.cmds.length > 1) {
			css = self.attribute.cmds.slice(1).join('-') + ': ' +  css + ';';
		}

		window.requestAnimationFrame(function () {
			self.element.style.cssText += css;
		});
	},
	class: function () {
		var self = this;
		var className = self.attribute.cmds.slice(1).join('-');

		window.requestAnimationFrame(function () {
			self.element.classList.toggle(className, self.data);
		});
	},
	text: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.innerText = self.data;
		});
	},
	enable: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.disabled = !self.data;
		});
	},
	disable: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.disabled = self.data;
		});
	},
	show: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.hidden = !self.data;
		});
	},
	hide: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.hidden = self.data;
		});
	},
	write: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.readOnly = !self.data;
		});
	},
	read: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.readOnly = self.data;
		});
	},
	selected: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.selectedIndex = self.data;
		});
	},
	default: function () {
		var self = this;
		var path = self.toCamelCase(self.attribute.cmds);

		window.requestAnimationFrame(function () {
			self.setByPath(self.element, path, self.data);
		});
	}
};

Unit.prototype.unrenderMethods = {
	on: function () {
		var eventName = this.attribute.cmds[1];
		this.element.removeEventListener(eventName, this.data, false);
	},
	each: function () {
		var self = this;

		var animate = function () {
			self.element.removeChild(self.element.lastChild);
			if (self.element.lastChild) animate();
		};

		window.requestAnimationFrame(animate);
	},
	value: function () {
		this.element.removeEventListener('change', this.change.bind(this));
		this.element.removeEventListener('keyup', this.change.bind(this));
	},
	html: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.innerText = '';
		});
	},
	text: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.innerText = '';
		});
	},
	default: function () {

	}
};

Unit.prototype.unrender = function () {
	this.unrenderMethod();
	return this;
};

Unit.prototype.render = function () {
	this.renderMethod();
	return this;
};

Unit.prototype.create = function (options) {
	this.view = options.view;
	this.model = options.model;
	this.data = options.data;
	this.element = options.element;
	this.attribute = options.attribute;
	this.modifiers = options.modifiers;

	this.renderMethod = (this.renderMethods[this.attribute.cmds[0]] || this.renderMethods['default']).bind(this);
	this.unrenderMethod = (this.unrenderMethods[this.attribute.cmds[0]] || this.unrenderMethods['default']).bind(this);

	Object.defineProperty(this, 'data', {
		enumerable: true,
		configurable: true,
		get: function () {
			var data = this.model.get(this.attribute.path);

			this.modifiers.forEach(function (modifier) {
				data = modifier.call(data);
			});

			return data;
		},
		set: function (value) {
			return this.model.set(this.attribute.path, value);
		}
	});

	this.renderMethod();

	return this;
};

module.exports = function (options) {
	return new Unit().create(options);
};
<commit_msg>fixed issue when there is no length<commit_after>
function Unit () {}

Unit.prototype.setByPath = function (collection, path, value) {
	var keys = path.split('.');
	var last = keys.length - 1;

	for (var i = 0, key; i < last; i++) {
		key = keys[i];
		if (collection[key] === undefined) collection[key] = {};
		collection = collection[key];
	}

	return collection[keys[last]] = value;
};

Unit.prototype.toCamelCase = function (data) {
	if (data.constructor.name === 'Array') data = data.join('-');
	return data.replace(/-[a-z]/g, function (match) {
		return match[1].toUpperCase();
	});
};

Unit.prototype.renderMethods = {
	on: function () {
		var eventName = this.attribute.cmds[1];
		this.element.removeEventListener(eventName, this.data, false);
		this.element.addEventListener(eventName, this.data, false);
	},
	each: function () {
		var self = this, animate;

		if (!self.data) {
			return;
		} else if (!self.clone) {

			self.variable = self.attribute.cmds.slice(1).join('.');
			self.clone = self.element.removeChild(self.element.children[0]).outerHTML;
			self.pattern = new RegExp('(((data-)?j(-(\\w)+)+="))' + self.variable + '(((\\.(\\w)+)+)?((\\s+)?\\|((\\s+)?(\\w)+)+)?(\\s+)?")', 'g');

			animate = function () {

				self.element.insertAdjacentHTML(
					'beforeend',
					self.clone.replace(
						self.pattern, '$1' + self.attribute.path + '.' + self.element.children.length + '$6'
					)
				);

				self.view.addOne(self.element.lastChild);
				self.view.addAll(self.element.lastChild.getElementsByTagName('*'));

				if (self.element.children.length < self.data.length) {
					window.requestAnimationFrame(animate);
				}

			};

			window.requestAnimationFrame(animate);

		} else if (self.element.children.length > self.data.length) {

			animate = function () {

				if (self.element.children.length > self.data.length) {

					self.view.removeAll(self.element.lastChild.getElementsByTagName('*'));
					self.view.removeOne(self.element.lastChild);
					self.element.removeChild(self.element.lastChild);

					if (self.element.children.length > self.data.length) {
						window.requestAnimationFrame(animate);
					}

				}

			};

			window.requestAnimationFrame(animate);

		} else if (self.element.children.length < self.data.length) {

			animate = function () {

				if (self.element.children.length < self.data.length) {

					self.element.insertAdjacentHTML(
						'beforeend',
						self.clone.replace(
							self.pattern, '$1' + self.attribute.path + '.' + self.element.children.length + '$6'
						)
					);

					self.view.addOne(self.element.lastChild);
					self.view.addAll(self.element.lastChild.getElementsByTagName('*'));

					if (self.element.children.length < self.data.length) {
						window.requestAnimationFrame(animate);
					}

				}

			};

			window.requestAnimationFrame(animate);

		}

	},
	value: function () {
		var self = this;

		if (self.change) return;
		if (self.element.type === 'button' || self.element.type === 'reset') return self.change = true;

		self.change = function () {
			self.data = self.element.type !== 'radio' && self.element.type !== 'checked' ? self.element.value : self.element.checked;
		};

		self.element.addEventListener('change', self.change.bind(self), true);
		self.element.addEventListener('keyup', self.change.bind(self), true);
	},
	html: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.innerHTML = self.data;
			self.view.addAll(self.element.getElementsByTagName('*'));
		});
	},
	css: function () {
		var self = this;
		var css = this.data;

		if (self.attribute.cmds.length > 1) {
			css = self.attribute.cmds.slice(1).join('-') + ': ' +  css + ';';
		}

		window.requestAnimationFrame(function () {
			self.element.style.cssText += css;
		});
	},
	class: function () {
		var self = this;
		var className = self.attribute.cmds.slice(1).join('-');

		window.requestAnimationFrame(function () {
			self.element.classList.toggle(className, self.data);
		});
	},
	text: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.innerText = self.data;
		});
	},
	enable: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.disabled = !self.data;
		});
	},
	disable: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.disabled = self.data;
		});
	},
	show: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.hidden = !self.data;
		});
	},
	hide: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.hidden = self.data;
		});
	},
	write: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.readOnly = !self.data;
		});
	},
	read: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.readOnly = self.data;
		});
	},
	selected: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.selectedIndex = self.data;
		});
	},
	default: function () {
		var self = this;
		var path = self.toCamelCase(self.attribute.cmds);

		window.requestAnimationFrame(function () {
			self.setByPath(self.element, path, self.data);
		});
	}
};

Unit.prototype.unrenderMethods = {
	on: function () {
		var eventName = this.attribute.cmds[1];
		this.element.removeEventListener(eventName, this.data, false);
	},
	each: function () {
		var self = this;

		var animate = function () {
			self.element.removeChild(self.element.lastChild);
			if (self.element.lastChild) animate();
		};

		window.requestAnimationFrame(animate);
	},
	value: function () {
		this.element.removeEventListener('change', this.change.bind(this));
		this.element.removeEventListener('keyup', this.change.bind(this));
	},
	html: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.innerText = '';
		});
	},
	text: function () {
		var self = this;

		window.requestAnimationFrame(function () {
			self.element.innerText = '';
		});
	},
	default: function () {

	}
};

Unit.prototype.unrender = function () {
	this.unrenderMethod();
	return this;
};

Unit.prototype.render = function () {
	this.renderMethod();
	return this;
};

Unit.prototype.create = function (options) {
	this.view = options.view;
	this.model = options.model;
	this.data = options.data;
	this.element = options.element;
	this.attribute = options.attribute;
	this.modifiers = options.modifiers;

	this.renderMethod = (this.renderMethods[this.attribute.cmds[0]] || this.renderMethods['default']).bind(this);
	this.unrenderMethod = (this.unrenderMethods[this.attribute.cmds[0]] || this.unrenderMethods['default']).bind(this);

	Object.defineProperty(this, 'data', {
		enumerable: true,
		configurable: true,
		get: function () {
			var data = this.model.get(this.attribute.path);

			this.modifiers.forEach(function (modifier) {
				data = modifier.call(data);
			});

			return data;
		},
		set: function (value) {
			return this.model.set(this.attribute.path, value);
		}
	});

	this.renderMethod();

	return this;
};

module.exports = function (options) {
	return new Unit().create(options);
};
<|endoftext|>


<commit_before><commit_msg>used github api to load content of citation style and locale<commit_after><|endoftext|>


<commit_before><commit_msg>commit<commit_after><|endoftext|>


<commit_before>const express = require('express');
const app = express();
const exec = require('child_process').exec;
const fs = require('fs');
const nodemailer = require("nodemailer");
const config = require('./config.js');

const cmd = 'sh scripts/build.sh ';
const port = 3000;
const send_mail_ids = "george2@ncsu.edu, george.meg91@gmail.com";

const smtpTransport = nodemailer.createTransport("SMTP",{
    service: "Gmail",
    auth: {
        user: config.email,
        pass: config.password
    }
});
var mailOptions = {
    from: `${config.user} <${config.email}>`, // sender address
    to: send_mail_ids, // list of receivers
    subject: 'Build Report : ', // Subject line
    html: 'Check the results at here.', // plaintext body
};

function build(request, response) {
    log = '';
    branch = request.params.branch;
	  var child = exec(cmd + branch, {maxBuffer: 1024 * 5000}, function(error, stdout, stderr) {
        is_success = isSuccess(log);
    		if( error ) {
    		    is_success = false;
    		    console.log(error);
    		    console.log("ERROR");
    		}
    		time_stamp = new Date().getTime();
    		directory = "scripts/files";
    		file_name = directory+"/"+time_stamp+".txt";
    		checkDirectory(directory, function(dir_err) {
    		    if(dir_err) {
                    console.error("Error while creating directory!!!", dir_err);
                    return;
                }
                fs.writeFile(file_name, log, function(err) {
                    if(err) {
                        return console.log(err);
                    }
                    console.log(`The file ${file_name} was saved!`);
                });
                fs.readFile('scripts/reports.json', 'utf8', function(err, data) {
                    if (err) {
                        data = [];
                    } else {
                        data = JSON.parse(data);
                    }
                    record = {
                        'file_name': file_name.split(/\/(.+)?/)[1],
                        'branch': branch,
                        'is_success' : is_success,
                        'time_stamp': time_stamp
                    };
                    data.unshift(record);
                    fs.writeFile('scripts/reports.json', JSON.stringify(data), function(err, data){
                        if(err) {
                            console.log(err);
                        } else {
                            console.log("The file scripts/reports.json was saved!");
                        }
                    });
                });
                if (is_success) {
                  mailOptions.subject += "SUCCESS";
                } else {
                  mailOptions.subject += "Failure";
                }
                smtpTransport.sendMail(mailOptions, function(error, response){
                    if(error){
                        console.log(error);
                    }else{
                        console.log("Message sent: " + response.message);
                    }
                });
    		});
    });

	// Stream results

	child.stdout.pipe( response );
	child.stdout.on('data', function(buf) {
	   log += buf;
	});

	child.stderr.pipe( process.stderr );
}

function checkDirectory(directory, callback) {
  fs.stat(directory, function(err, stats) {
    //Check if error defined and the error code is "not exists"
    if (err) {
      //Create the directory, call the callback.
      fs.mkdir(directory, callback);
    } else {
      //just in case there was a different error:
      callback(err)
    }
  });
}

function format_ts(time_stamp) {
    d = new Date(time_stamp);
    return d.getMonth() + "/" + d.getDate() + "/" + d.getFullYear() +
     " "+ d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds()
}

function isSuccess(log) {
  return log.includes("[INFO] BUILD SUCCESS");
}

app.use('/static', express.static(__dirname + '/files'));

app.get('/', (request, response) => {
  response.send('Hello, this is a miniature server');
});

app.get('/deploy/:branch', build)

app.get('/report', function(request, response) {
   fs.readFile('scripts/reports.json', 'utf8', function(err, data) {
       if (err) {
           data = [];
       } else {
           data = JSON.parse(data);
       }
       html = "";
       html += "

Time	Branch	Status	Report
";
       for (var i=0; i<data.length; i++) {
           row = "
";
           row += "" +format_ts(data[i].time_stamp)+"	";
           row += "" +data[i].branch+"	";
           if (data[i].is_success) {
                row += "Success	";
           } else {
                row += "Failed	";
           }
           row += "Report	"
           html += row;

       }
       html += "
";
       response.send(html);
   });
});

app.listen(port, (err) => {
  if (err) {
    return console.log('something bad happened', err);
  }
  console.log(`server is listening on ${port}`);
})
<commit_msg>Added wei's email<commit_after>const express = require('express');
const app = express();
const exec = require('child_process').exec;
const fs = require('fs');
const nodemailer = require("nodemailer");
const config = require('./config.js');

const cmd = 'sh scripts/build.sh ';
const port = 3000;
const send_mail_ids = "george2@ncsu.edu, wfu@ncsu.edu";

const smtpTransport = nodemailer.createTransport("SMTP",{
    service: "Gmail",
    auth: {
        user: config.email,
        pass: config.password
    }
});
var mailOptions = {
    from: `${config.user} <${config.email}>`, // sender address
    to: send_mail_ids, // list of receivers
    subject: 'Build Report : ', // Subject line
    html: 'Check the results at here.', // plaintext body
};

function build(request, response) {
    log = '';
    branch = request.params.branch;
	  var child = exec(cmd + branch, {maxBuffer: 1024 * 5000}, function(error, stdout, stderr) {
        is_success = isSuccess(log);
    		if( error ) {
    		    is_success = false;
    		    console.log(error);
    		    console.log("ERROR");
    		}
    		time_stamp = new Date().getTime();
    		directory = "scripts/files";
    		file_name = directory+"/"+time_stamp+".txt";
    		checkDirectory(directory, function(dir_err) {
    		    if(dir_err) {
                    console.error("Error while creating directory!!!", dir_err);
                    return;
                }
                fs.writeFile(file_name, log, function(err) {
                    if(err) {
                        return console.log(err);
                    }
                    console.log(`The file ${file_name} was saved!`);
                });
                fs.readFile('scripts/reports.json', 'utf8', function(err, data) {
                    if (err) {
                        data = [];
                    } else {
                        data = JSON.parse(data);
                    }
                    record = {
                        'file_name': file_name.split(/\/(.+)?/)[1],
                        'branch': branch,
                        'is_success' : is_success,
                        'time_stamp': time_stamp
                    };
                    data.unshift(record);
                    fs.writeFile('scripts/reports.json', JSON.stringify(data), function(err, data){
                        if(err) {
                            console.log(err);
                        } else {
                            console.log("The file scripts/reports.json was saved!");
                        }
                    });
                });
                if (is_success) {
                  mailOptions.subject += "SUCCESS";
                } else {
                  mailOptions.subject += "Failure";
                }
                smtpTransport.sendMail(mailOptions, function(error, response){
                    if(error){
                        console.log(error);
                    }else{
                        console.log("Message sent: " + response.message);
                    }
                });
    		});
    });

	// Stream results

	child.stdout.pipe( response );
	child.stdout.on('data', function(buf) {
	   log += buf;
	});

	child.stderr.pipe( process.stderr );
}

function checkDirectory(directory, callback) {
  fs.stat(directory, function(err, stats) {
    //Check if error defined and the error code is "not exists"
    if (err) {
      //Create the directory, call the callback.
      fs.mkdir(directory, callback);
    } else {
      //just in case there was a different error:
      callback(err)
    }
  });
}

function format_ts(time_stamp) {
    d = new Date(time_stamp);
    return d.getMonth() + "/" + d.getDate() + "/" + d.getFullYear() +
     " "+ d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds()
}

function isSuccess(log) {
  return log.includes("[INFO] BUILD SUCCESS");
}

app.use('/static', express.static(__dirname + '/files'));

app.get('/', (request, response) => {
  response.send('Hello, this is a miniature server');
});

app.get('/deploy/:branch', build)

app.get('/report', function(request, response) {
   fs.readFile('scripts/reports.json', 'utf8', function(err, data) {
       if (err) {
           data = [];
       } else {
           data = JSON.parse(data);
       }
       html = "";
       html += "

Time	Branch	Status	Report
";
       for (var i=0; i<data.length; i++) {
           row = "
";
           row += "" +format_ts(data[i].time_stamp)+"	";
           row += "" +data[i].branch+"	";
           if (data[i].is_success) {
                row += "Success	";
           } else {
                row += "Failed	";
           }
           row += "Report	"
           html += row;

       }
       html += "
";
       response.send(html);
   });
});

app.listen(port, (err) => {
  if (err) {
    return console.log('something bad happened', err);
  }
  console.log(`server is listening on ${port}`);
})
<|endoftext|>


<commit_before><commit_msg>code formatting<commit_after><|endoftext|>


<commit_before><commit_msg>fixed right/left postition of boxes<commit_after><|endoftext|>


<commit_before><commit_msg>Add most basic explain results as a test<commit_after><|endoftext|>


<commit_before>/*
==================================================================================================
	Lowland - JavaScript low level functions
	Copyright (C) 2012 Sebatian Fastner
==================================================================================================
*/

(function(global){

	// Based upon http://www.codeproject.com/Articles/35737/Absolute-Position-of-a-DOM-Element

	var userAgent = navigator.userAgent;

	var getIEVersion = function() {
		var rv = -1; // Return value assumes failure.

		var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
		if (re.exec(userAgent) != null)
		rv = parseFloat(RegExp.$1);

		return rv;
	}

	var getOperaVersion = function() {
		var rv = 0; // Default value
		if (window.opera) {
			var sver = window.opera.version();
			rv = parseFloat(sver);
		}
		return rv;
	}

	var isIENew = jasy.Env.getValue("engine") == "trident" && getIEVersion() >= 8;
	var isIEOld = jasy.Env.getValue("engine") == "trident" && !isIENew;

	var isFireFoxOld =  jasy.Env.getValue("engine") == "gecko" && ((userAgent.match(/firefox\/2./i) != null) || (userAgent.match(/firefox\/1./i) != null));
	var isFireFoxNew =  jasy.Env.getValue("engine") == "gecko" && !isFireFoxOld;

	var isChrome =  navigator.appVersion.match(/Chrome/) != null;
	var isOperaOld =  jasy.Env.getValue("engine") == "presto" && (getOperaVersion() < 10);

	var parseBorderWidth = function(width) {
		var res = 0;
		if (typeof(width) == "string" && width != null && width != "" ) {
			var p = width.indexOf("px");
			if (p >= 0) {
				res = parseInt(width.substring(0, p), 10);
			} else {
				// If no px value just use 1
				res = 1;
			}
		}
		return res;
	}

	//returns border width for some element
	var getBorderWidth = window.getComputedStyle ? function(element) {
		var style = window.getComputedStyle(element, null);

		return {
			left: parseInt(style.borderLeftWidth, 10),
			top: parseInt(style.borderTopWidth, 10),
			right: parseInt(style.borderRightWidth, 10),
			bottom: parseInt(style.borderBottomWidth, 10)
		};
	} : function(element) {
		var style = element.style;

		var res = {
			left: parseBorderWidth(style.borderLeftWidth),
			top: parseBorderWidth(style.borderTopWidth),
			right: parseBorderWidth(style.borderRightWidth),
			bottom: parseBorderWidth(style.borderBottomWidth)
		};

		return res;
	}

	var viewportElement = document.documentElement;

	var getLocation = viewportElement.getBoundingClientRect ? function(element) {

		var res = {
			left: 0,
			top: 0
		};

		if (!element) {
			return res;
		}

		var box = element.getBoundingClientRect();
		var scrollLeft = viewportElement.scrollLeft;
		var scrollTop = viewportElement.scrollTop;

		res.left = box.left + scrollLeft;
		res.top = box.top + scrollTop;

		return res;

	} : function(element) {
		var res = {
			left: 0,
			top: 0
		};

		if (!element) {
			return res;
		}

		var offsetParent;

		res.left = element.offsetLeft;
		res.top = element.offsetTop;

		var parentNode = element.parentNode;
		var borderWidth = null;

		while (offsetParent != null) {
			res.left += offsetParent.offsetLeft;
			res.top += offsetParent.offsetTop;

			var parentTagName =
			offsetParent.tagName.toLowerCase();

			if ((isIEOld && parentTagName != "table") || ((isFireFoxNew || isChrome) && parentTagName == "td")) {
				borderWidth = getBorderWidth(offsetParent);
				res.left += borderWidth.left;
				res.top += borderWidth.top;
			}

			if (offsetParent != document.body && offsetParent != document.documentElement) {
				res.left -= offsetParent.scrollLeft;
				res.top -= offsetParent.scrollTop;
			}

			//next lines are necessary to fix the problem
			//with offsetParent
			if (jasy.Env.getValue("engine") != "trident" && !isOperaOld || isIENew) {
				while (offsetParent != parentNode && parentNode !== null) {
					res.left -= parentNode.scrollLeft;
					res.top -= parentNode.scrollTop;
					if (isFireFoxOld || jasy.Env.getValue("engine") == "webkit") {
						borderWidth = getBorderWidth(parentNode);
						res.left += borderWidth.left;
						res.top += borderWidth.top;
					}
					parentNode = parentNode.parentNode;
				}
			}

			parentNode = offsetParent.parentNode;
			offsetParent = offsetParent.offsetParent;
		}

		return res;
	}

	var propertyHelper = {};
	var getProperty = function(property) {
		if (propertyHelper[property]) {
			return propertyHelper[property];
		}

		var prop = lowland.bom.Style.property(property).replace(/([A-Z])/g, '-$1').toLowerCase();
		propertyHelper[property] = prop;

		return prop;
	};

	core.Module("lowland.bom.Element", {

		replaceStyle : function(element, styleMap) {
			var cssText = [];

			for (var key in styleMap) {
				cssText.push(getProperty(key));
				cssText.push(":");
				cssText.push(styleMap[key]);
				cssText.push(";");
			}

			element.style.cssText = cssText.join("");
		},

		getContentWidth : function(element) {
			if ((jasy.Env.getValue("engine") == "gecko") && (element.getBoundingClientRect)) {
				// see https://bugzilla.mozilla.org/show_bug.cgi?id=450422
				var rect = element.getBoundingClientRect();
				return Math.round(rect.right) - Math.round(rect.left);
			} else {
				return element.offsetWidth;
			}
		},

		getContentHeight : function(element) {
			if ((jasy.Env.getValue("engine") == "gecko") && (element.getBoundingClientRect)) {
				// see https://bugzilla.mozilla.org/show_bug.cgi?id=450422
				var rect = element.getBoundingClientRect();
				return Math.round(rect.bottom) - Math.round(rect.top);
			} else {
				return element.offsetHeight;
			}
		},

		getContentSize : function(element) {
			if ((jasy.Env.getValue("engine") == "gecko") && (element.getBoundingClientRect)) {
				// see https://bugzilla.mozilla.org/show_bug.cgi?id=450422
				var rect = element.getBoundingClientRect();
				return  {
					width: Math.round(rect.right) - Math.round(rect.left),
					height: Math.round(rect.bottom) - Math.round(rect.top)
				};
			} else {
				return {
					width: element.offsetWidth,
					height: element.offsetHeight
				};
			}
		},

		getLocation : getLocation

	});

})(this);<commit_msg>Add optional viewport element to getLocation<commit_after>/*
==================================================================================================
	Lowland - JavaScript low level functions
	Copyright (C) 2012 Sebatian Fastner
==================================================================================================
*/

(function(global){

	// Based upon http://www.codeproject.com/Articles/35737/Absolute-Position-of-a-DOM-Element

	var userAgent = navigator.userAgent;

	var getIEVersion = function() {
		var rv = -1; // Return value assumes failure.

		var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
		if (re.exec(userAgent) != null)
		rv = parseFloat(RegExp.$1);

		return rv;
	}

	var getOperaVersion = function() {
		var rv = 0; // Default value
		if (window.opera) {
			var sver = window.opera.version();
			rv = parseFloat(sver);
		}
		return rv;
	}

	var isIENew = jasy.Env.getValue("engine") == "trident" && getIEVersion() >= 8;
	var isIEOld = jasy.Env.getValue("engine") == "trident" && !isIENew;

	var isFireFoxOld =  jasy.Env.getValue("engine") == "gecko" && ((userAgent.match(/firefox\/2./i) != null) || (userAgent.match(/firefox\/1./i) != null));
	var isFireFoxNew =  jasy.Env.getValue("engine") == "gecko" && !isFireFoxOld;

	var isChrome =  navigator.appVersion.match(/Chrome/) != null;
	var isOperaOld =  jasy.Env.getValue("engine") == "presto" && (getOperaVersion() < 10);

	var parseBorderWidth = function(width) {
		var res = 0;
		if (typeof(width) == "string" && width != null && width != "" ) {
			var p = width.indexOf("px");
			if (p >= 0) {
				res = parseInt(width.substring(0, p), 10);
			} else {
				// If no px value just use 1
				res = 1;
			}
		}
		return res;
	}

	//returns border width for some element
	var getBorderWidth = window.getComputedStyle ? function(element) {
		var style = window.getComputedStyle(element, null);

		return {
			left: parseInt(style.borderLeftWidth, 10),
			top: parseInt(style.borderTopWidth, 10),
			right: parseInt(style.borderRightWidth, 10),
			bottom: parseInt(style.borderBottomWidth, 10)
		};
	} : function(element) {
		var style = element.style;

		var res = {
			left: parseBorderWidth(style.borderLeftWidth),
			top: parseBorderWidth(style.borderTopWidth),
			right: parseBorderWidth(style.borderRightWidth),
			bottom: parseBorderWidth(style.borderBottomWidth)
		};

		return res;
	}

	var getLocation = document.documentElement.getBoundingClientRect ? function(element, viewportElement) {

        if (!viewportElement) {
            viewportElement = document.documentElement;
        }

		var res = {
			left: 0,
			top: 0
		};

		if (!element) {
			return res;
		}

		var box = element.getBoundingClientRect();
		var scrollLeft = viewportElement.scrollLeft;
		var scrollTop = viewportElement.scrollTop;

		res.left = box.left + scrollLeft;
		res.top = box.top + scrollTop;

		return res;

	} : function(element, viewportElement) {
		var res = {
			left: 0,
			top: 0
		};

		if (!element) {
			return res;
		}

		var offsetParent;

		res.left = element.offsetLeft;
		res.top = element.offsetTop;

		var parentNode = element.parentNode;
		var borderWidth = null;

		while (offsetParent != null) {
			res.left += offsetParent.offsetLeft;
			res.top += offsetParent.offsetTop;

			var parentTagName =
			offsetParent.tagName.toLowerCase();

			if ((isIEOld && parentTagName != "table") || ((isFireFoxNew || isChrome) && parentTagName == "td")) {
				borderWidth = getBorderWidth(offsetParent);
				res.left += borderWidth.left;
				res.top += borderWidth.top;
			}

			if (offsetParent != document.body && offsetParent != document.documentElement) {
				res.left -= offsetParent.scrollLeft;
				res.top -= offsetParent.scrollTop;
			}

			//next lines are necessary to fix the problem
			//with offsetParent
			if (jasy.Env.getValue("engine") != "trident" && !isOperaOld || isIENew) {
				while (offsetParent != parentNode && parentNode !== null) {
					res.left -= parentNode.scrollLeft;
					res.top -= parentNode.scrollTop;
					if (isFireFoxOld || jasy.Env.getValue("engine") == "webkit") {
						borderWidth = getBorderWidth(parentNode);
						res.left += borderWidth.left;
						res.top += borderWidth.top;
					}
					parentNode = parentNode.parentNode;
				}
			}

			parentNode = offsetParent.parentNode;
			offsetParent = offsetParent.offsetParent;
		}

		return res;
	}

	var propertyHelper = {};
	var getProperty = function(property) {
		if (propertyHelper[property]) {
			return propertyHelper[property];
		}

		var prop = lowland.bom.Style.property(property).replace(/([A-Z])/g, '-$1').toLowerCase();
		propertyHelper[property] = prop;

		return prop;
	};

	core.Module("lowland.bom.Element", {

		replaceStyle : function(element, styleMap) {
			var cssText = [];

			for (var key in styleMap) {
				cssText.push(getProperty(key));
				cssText.push(":");
				cssText.push(styleMap[key]);
				cssText.push(";");
			}

			element.style.cssText = cssText.join("");
		},

		getContentWidth : function(element) {
			if ((jasy.Env.getValue("engine") == "gecko") && (element.getBoundingClientRect)) {
				// see https://bugzilla.mozilla.org/show_bug.cgi?id=450422
				var rect = element.getBoundingClientRect();
				return Math.round(rect.right) - Math.round(rect.left);
			} else {
				return element.offsetWidth;
			}
		},

		getContentHeight : function(element) {
			if ((jasy.Env.getValue("engine") == "gecko") && (element.getBoundingClientRect)) {
				// see https://bugzilla.mozilla.org/show_bug.cgi?id=450422
				var rect = element.getBoundingClientRect();
				return Math.round(rect.bottom) - Math.round(rect.top);
			} else {
				return element.offsetHeight;
			}
		},

		getContentSize : function(element) {
			if ((jasy.Env.getValue("engine") == "gecko") && (element.getBoundingClientRect)) {
				// see https://bugzilla.mozilla.org/show_bug.cgi?id=450422
				var rect = element.getBoundingClientRect();
				return  {
					width: Math.round(rect.right) - Math.round(rect.left),
					height: Math.round(rect.bottom) - Math.round(rect.top)
				};
			} else {
				return {
					width: element.offsetWidth,
					height: element.offsetHeight
				};
			}
		},

		getLocation : getLocation

	});

})(this);<|endoftext|>


<commit_before>document.getElementById('submitButton').onclick = function()
{
    var username = document.getElementById('username').value;
    document.getElementById('username').value = '';
    var postAddition = document.getElementById('postAddition').value;
    document.getElementById('postAddition').value = '';

    chrome.storage.sync.get('additions', function(items)
    {
        var additions = items.additions || [];
        additions.push({username: username, postAddition: postAddition});

        chrome.storage.sync.set({additions: additions}, function()
        {
            document.getElementById('message').innerHTML = 'Added option';
            window.setTimeout(function()
            {
                document.getElementById('message').innerHTML = '';
            }, 2000);
            displayAdditions();
        });
    });
}

function deleteItem()
{
    var id = parseInt(this.parentElement.parentElement.getAttribute('data-id'));
    chrome.storage.sync.get('additions', function(items)
    {
        var additions = items.additions || [];
        additions.splice(id, 1);

        chrome.storage.sync.set({additions: additions}, function()
        {
            document.getElementById('message').innerHTML = 'Removed option';
            window.setTimeout(function()
            {
                document.getElementById('message').innerHTML = '';
            }, 2000);
            displayAdditions();
        });
    });
}

function displayAdditions()
{
    document.getElementById('additionList').innerHTML = '
Username	Post addition
';

    chrome.storage.sync.get('additions', function(items)
    {
        if (items.additions != null)
        {
            for (i = 0; i < items.additions.length; i++)
                document.getElementById('additionList').innerHTML += '
' + items.additions[i].username + '	' + items.additions[i].postAddition + '	<button class="deleteButton">Delete</button>
';
            var deleteButtons = document.getElementsByClassName('deleteButton');
            for (i = 0; i < deleteButtons.length; i++)
                deleteButtons[i].onclick = deleteItem;
        }
    });
}

displayAdditions();
<commit_msg>Allow user to add an item when the press enter<commit_after>function addItem()
{
    var username = document.getElementById('username').value;
    document.getElementById('username').value = '';
    var postAddition = document.getElementById('postAddition').value;
    document.getElementById('postAddition').value = '';

    chrome.storage.sync.get('additions', function(items)
    {
        var additions = items.additions || [];
        additions.push({username: username, postAddition: postAddition});

        chrome.storage.sync.set({additions: additions}, function()
        {
            document.getElementById('message').innerHTML = 'Added option';
            window.setTimeout(function()
            {
                document.getElementById('message').innerHTML = '';
            }, 2000);
            displayAdditions();
        });
    });
}
function addItemKeyPress(e)
{
    if (e.keyCode == 13)
        addItem();
}

document.getElementById('username').onkeypress = addItemKeyPress;
document.getElementById('postAddition').onkeypress = addItemKeyPress;
document.getElementById('submitButton').onclick = addItem;

function deleteItem()
{
    var id = parseInt(this.parentElement.parentElement.getAttribute('data-id'));
    chrome.storage.sync.get('additions', function(items)
    {
        var additions = items.additions || [];
        additions.splice(id, 1);

        chrome.storage.sync.set({additions: additions}, function()
        {
            document.getElementById('message').innerHTML = 'Removed option';
            window.setTimeout(function()
            {
                document.getElementById('message').innerHTML = '';
            }, 2000);
            displayAdditions();
        });
    });
}

function displayAdditions()
{
    document.getElementById('additionList').innerHTML = '
Username	Post addition
';

    chrome.storage.sync.get('additions', function(items)
    {
        if (items.additions != null)
        {
            for (i = 0; i < items.additions.length; i++)
                document.getElementById('additionList').innerHTML += '
' + items.additions[i].username + '	' + items.additions[i].postAddition + '	<button class="deleteButton">Delete</button>
';
            var deleteButtons = document.getElementsByClassName('deleteButton');
            for (i = 0; i < deleteButtons.length; i++)
                deleteButtons[i].onclick = deleteItem;
        }
    });
}

displayAdditions();
<|endoftext|>


<commit_before>/*
 * Project: Tyval
 * Version: 2.0.0
 * Author: delvedor
 * Twitter: @delvedor
 * License: MIT
 * GitHub: https://github.com/delvedor/Tyval
 */

'use strict'
/* globals variable, check, parameters */
/* eslint-disable no-native-reassign, no-extend-native, no-new-func */

// Code parser
const esprima = require('esprima')
// AST traversal
const estraverse = require('estraverse')
// Code generator
const escodegen = require('escodegen')

const tyval = {
  // Validator functions
  validators: [],
  // Parameters for the validator functions
  parameters: {},
  // Checks if the variable is a string
  isString: function () {
    this.validators.push(function isString () {
      check &= typeof variable === 'string'
    })
    return this
  },
  // Checks if the variable is a number
  isNumber: function () {
    this.validators.push(function isNumber () {
      check &= typeof variable === 'number'
    })
    return this
  },
  // Checks if the variable is null
  isNull: function () {
    this.validators.push(function isNull () {
      check &= variable === null
    })
    return this
  },
  // Checks if the variable is undefined
  isUndefined: function () {
    this.validators.push(function isUndefined () {
      check &= variable === undefined
    })
    return this
  },
  // Checks if the variable is a boolean
  isBoolean: function () {
    this.validators.push(function isBoolean () {
      check &= typeof variable === 'boolean'
    })
    return this
  },
  // Checks if the variable is an object
  isObject: function () {
    this.validators.push(function isObject () {
      check &= typeof variable === 'object'
    })
    return this
  },
  // Checks if the variable is a function
  isFunction: function () {
    this.validators.push(function isFunction () {
      check &= typeof variable === 'function'
    })
    return this
  },
  // Checks if the string is alphanumberic
  alphanum: function () {
    this.validators.push(function alphanum () {
      const reg = /((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+[0-9a-z]+$/i
      check &= reg.test(variable)
    })
    return this
  },
  // Tests the regex passed as input
  regex: function (reg, flag) {
    this.parameters.regex = {
      reg: reg,
      flag: flag
    }
    this.validators.push(function regex () {
      let reg = new RegExp(parameters.regex.reg, parameters.regex.flag)
      check &= reg.test(variable)
    })
    return this
  },
  // Checks if the string.length is lower than the passed max value
  maxStr: function (max) {
    this.parameters.maxStr = max
    this.validators.push(function max () {
      check &= variable.length <= parameters.maxStr
    })
    return this
  },
  // Checks if the string.length is higher than the passed value
  minStr: function (min) {
    this.parameters.minStr = min
    this.validators.push(function min () {
      check &= variable.length >= parameters.minStr
    })
    return this
  },
  // Checks if the number is lower than the passed value
  maxNum: function (max) {
    this.parameters.maxNumber = max
    this.validators.push(function max () {
      check &= variable <= parameters.maxNumber
    })
    return this
  },
  // Checks if the number is higher than the passed value
  minNum: function (min) {
    this.parameters.minNumber = min
    this.validators.push(function min () {
      check &= variable >= parameters.minNumber
    })
    return this
  },
  // Checks if the variable is a positive number
  positive: function () {
    this.validators.push(function positive () {
      check &= variable > 0
    })
    return this
  },
  // Checks if the variable is a negative number
  negative: function () {
    this.validators.push(function negative () {
      check &= variable < 0
    })
    return this
  },
  // Checks if the variable is an Integer
  integer: function () {
    this.validators.push(function integer () {
      check &= Number.isInteger(variable)
    })
    return this
  },
  // Checks if the variable is a Float
  float: function () {
    this.validators.push(function float () {
      check &= !Number.isInteger(variable)
    })
    return this
  },
  // Checks if the variable is a safe integer
  safeInteger: function () {
    this.validators.push(function safeInteger () {
      check &= Number.isSafeInteger(variable)
    })
    return this
  },
  // Checks if the variable is a finite number
  finite: function () {
    this.validators.push(function finite () {
      check &= Number.isFinite(variable)
    })
    return this
  },
  // Adds a new function to tyval
  extend: function (func) {
    tyval[func.name] = func
  },
  // Parse the validators code in a single function
  toFunction: function () {
    // Fix for the RegExp to JSON
    RegExp.prototype.toJSON = RegExp.prototype.toString
    let functionCode = '"use strict";\nlet check = 1;\nlet parameters = ' + JSON.stringify(this.parameters) + '\n'
    let ast = null
    let block = null
    this.validators.forEach(function (code) {
      // get function ast
      ast = esprima.parse(code.toString(), {})
      block = null
      estraverse.traverse(ast, {
        enter: function (node, parent) {
          // gets only the function code
          if (node.type === 'BlockStatement') {
            block = node
            this.break()
          }
        }
      })
      // generate the function code without the function declaration
      functionCode += escodegen.generate(block) + ';\n'
    })
    functionCode += 'return !!check;'
    // restore validators and parameters
    this.validators = []
    this.parameters = {}
    return new Function('variable', functionCode)
  }
}

module.exports = tyval
<commit_msg>Updated toFunction and added toArray, maxArray and minArray<commit_after>/*
 * Project: Tyval
 * Version: 2.1.0
 * Author: delvedor
 * Twitter: @delvedor
 * License: MIT
 * GitHub: https://github.com/delvedor/Tyval
 */

'use strict'
/* globals variable, check, parameters */
/* eslint-disable no-native-reassign, no-extend-native, no-new-func */

// Code parser
const esprima = require('esprima')
// AST traversal
const estraverse = require('estraverse')
// Code generator
const escodegen = require('escodegen')

const tyval = {
  // Validator functions
  validators: [],
  // Parameters for the validator functions
  parameters: {},
  // Checks if the variable is a string
  isString: function () {
    this.validators.push(function isString () {
      check = check && typeof variable === 'string'
    })
    return this
  },
  // Checks if the variable is a number
  isNumber: function () {
    this.validators.push(function isNumber () {
      check = check && typeof variable === 'number'
    })
    return this
  },
  // Checks if the variable is null
  isNull: function () {
    this.validators.push(function isNull () {
      check = check && variable === null
    })
    return this
  },
  // Checks if the variable is undefined
  isUndefined: function () {
    this.validators.push(function isUndefined () {
      check = check && variable === undefined
    })
    return this
  },
  // Checks if the variable is a boolean
  isBoolean: function () {
    this.validators.push(function isBoolean () {
      check = check && typeof variable === 'boolean'
    })
    return this
  },
  // Checks if the variable is an object
  isObject: function () {
    this.validators.push(function isObject () {
      check = check && typeof variable === 'object'
    })
    return this
  },
  // Checks if the variable is an array
  isArray: function () {
    this.validators.push(function isArray () {
      check = check && Array.isArray(variable)
    })
    return this
  },
  // Checks if the variable is a function
  isFunction: function () {
    this.validators.push(function isFunction () {
      check = check && typeof variable === 'function'
    })
    return this
  },
  // Checks if the string is alphanumberic
  alphanum: function () {
    this.validators.push(function alphanum () {
      const reg = /((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+[0-9a-z]+$/i
      check = check && reg.test(variable)
    })
    return this
  },
  // Tests the regex passed as input
  regex: function (reg, flag) {
    this.parameters.regex = {
      reg: reg,
      flag: flag
    }
    this.validators.push(function regex () {
      let reg = new RegExp(parameters.regex.reg, parameters.regex.flag)
      check = check && reg.test(variable)
    })
    return this
  },
  // Checks if the string.length is lower than the passed max value
  maxStr: function (max) {
    this.parameters.maxStr = max
    this.validators.push(function max () {
      check = check && variable.length <= parameters.maxStr
    })
    return this
  },
  // Checks if the string.length is higher than the passed value
  minStr: function (min) {
    this.parameters.minStr = min
    this.validators.push(function min () {
      check = check && variable.length >= parameters.minStr
    })
    return this
  },
  // Checks if the array.length is higher than the passed value
  maxArray: function (max) {
    this.parameters.maxArray = max
    this.validators.push(function maxArray () {
      check = check && variable.length <= parameters.maxArray
    })
    return this
  },
  // Checks if the array.length is lower than the passed value
  minArray: function (min) {
    this.parameters.minArray = min
    this.validators.push(function minArray () {
      check = check && variable.length >= parameters.minArray
    })
    return this
  },
  // Checks if the number is lower than the passed value
  maxNum: function (max) {
    this.parameters.maxNumber = max
    this.validators.push(function max () {
      check = check && variable <= parameters.maxNumber
    })
    return this
  },
  // Checks if the number is higher than the passed value
  minNum: function (min) {
    this.parameters.minNumber = min
    this.validators.push(function min () {
      check = check && variable >= parameters.minNumber
    })
    return this
  },
  // Checks if the variable is a positive number
  positive: function () {
    this.validators.push(function positive () {
      check = check && variable > 0
    })
    return this
  },
  // Checks if the variable is a negative number
  negative: function () {
    this.validators.push(function negative () {
      check = check && variable < 0
    })
    return this
  },
  // Checks if the variable is an Integer
  integer: function () {
    this.validators.push(function integer () {
      check = check && Number.isInteger(variable)
    })
    return this
  },
  // Checks if the variable is a Float
  float: function () {
    this.validators.push(function float () {
      check = check && !Number.isInteger(variable)
    })
    return this
  },
  // Checks if the variable is a safe integer
  safeInteger: function () {
    this.validators.push(function safeInteger () {
      check = check && Number.isSafeInteger(variable)
    })
    return this
  },
  // Checks if the variable is a finite number
  finite: function () {
    this.validators.push(function finite () {
      check = check && Number.isFinite(variable)
    })
    return this
  },
  // Adds a new function to tyval
  extend: function (func) {
    tyval[func.name] = func
  },
  // Parse the validators code in a single function
  toFunction: function () {
    // Fix for the RegExp to JSON
    RegExp.prototype.toJSON = RegExp.prototype.toString
    let functionCode = '"use strict";\nlet check = true;\nlet parameters = ' + JSON.stringify(this.parameters) + '\n'
    let ast = null
    let block = null
    this.validators.forEach(function (code, index, array) {
      // get function ast
      ast = esprima.parse(code.toString(), {})
      block = null
      estraverse.traverse(ast, {
        enter: function (node, parent) {
          // gets only the function code
          if (node.type === 'BlockStatement') {
            block = node
            this.break()
          }
        }
      })
      // generate the function code without the function declaration
      if (index === array.length - 1) {
        functionCode += escodegen.generate(block) + ';\n'
      } else {
        functionCode += escodegen.generate(block) + ';\nif (!check) { return false; }\n'
      }
    })
    functionCode += 'return check;'
    this.validators = []
    this.parameters = {}
    return new Function('variable', functionCode)
  }
}

module.exports = tyval
<|endoftext|>


<commit_before>const Signal = require('signals')
const mat4 = require('pex-math/mat4')
const vec3 = require('pex-math/vec3')
const quat = require('pex-math/quat')
const ray = require('pex-geom/ray')
const interpolateAngle = require('interpolate-angle')
const clamp = require('pex-math/utils').clamp
const lerp = require('pex-math/utils').lerp
const toRadians = require('pex-math/utils').toRadians
const toDegrees = require('pex-math/utils').toDegrees
const latLonToXyz = require('latlon-to-xyz')
const xyzToLatLon = require('xyz-to-latlon')
const offset = require('mouse-event-offset')

function getViewRay (camera, x, y, windowWidth, windowHeight) {
  if (camera.frustum) {
    x += camera.frustum.offset[0]
    y += camera.frustum.offset[1]
    windowWidth = camera.frustum.totalSize[0]
    windowHeight = camera.frustum.totalSize[1]
  }
  let nx = 2 * x / windowWidth - 1
  let ny = 1 - 2 * y / windowHeight

  let hNear = 2 * Math.tan(camera.fov / 2) * camera.near
  let wNear = hNear * camera.aspect

  nx *= (wNear * 0.5)
  ny *= (hNear * 0.5)

  let origin = [0, 0, 0]
  let direction = vec3.normalize([nx, ny, -camera.near])
  let ray = [origin, direction]

  return ray
}

function Orbiter (opts) {
  this.type = 'Orbiter'
  this.entity = null
  this.dirty = false
  this.changed = new Signal()

  const initialState = {
    target: [0, 0, 0],
    position: [0, 0, 5],
    invViewMatrix: mat4.create(),
    dragging: false,
    lat: 0, // Y
    lon: 0, // XZ
    currentLat: 0,
    currentLon: 0,
    easing: 1,
    element: opts.element || window,
    width: 0,
    height: 0,
    clickPosWindow: [0, 0],
    dragPos: [0, 0, 0],
    dragPosWindow: [0, 0],
    distance: 1,
    currentDistance: 1,
    minDistance: 0.1,
    maxDistance: 10,
    zoomSlowdown: 400,
    zoom: true,
    pan: true,
    drag: true,
    dragSlowdown: 4,
    clickTarget: [0, 0, 0],
    clickPosPlane: [0, 0, 0],
    dragPosPlane: [0, 0, 0],
    clickPosWorld: [0, 0, 0],
    dragPosWorld: [0, 0, 0],
    panPlane: null,
    autoUpdate: true
  }

  this.set(initialState)
  this.set(opts)
}

Orbiter.prototype.init = function (entity) {
  this.entity = entity
  this.setup()
}

Orbiter.prototype.set = function (opts) {
  Object.assign(this, opts)
  if (opts.target || opts.position) {
    const distance = vec3.distance(this.position, this.target)
    const latLon = xyzToLatLon(vec3.normalize(vec3.sub(vec3.copy(this.position), this.target)))
    this.lat = latLon[0]
    this.lon = latLon[1]
    this.currentLat = this.lat
    this.currentLon = this.lon
    this.distance = distance
    this.currentDistance = this.distance
    this.minDistance = opts.minDistance || distance / 10
    this.maxDistance = opts.maxDistance || distance * 10
  }
  Object.keys(opts).forEach((prop) => this.changed.dispatch(prop))
}

const tempmat4 = mat4.create()
Orbiter.prototype.update = function () {
  this.updateCamera()
  const camera = this.entity.getComponent('Camera')

  const transformCmp = this.entity.transform
  const transformRotation = transformCmp.rotation
  mat4.set(tempmat4, camera.viewMatrix)
  mat4.invert(tempmat4)
  quat.fromMat4(transformRotation, tempmat4)
  transformCmp.set({
    position: this.position,
    rotation: transformRotation
  })
}

Orbiter.prototype.updateWindowSize = function () {
  const width = this.element.clientWidth || this.element.innerWidth
  const height = this.element.clientHeight || this.element.innerHeight
  if (width !== this.width) {
    this.width = width
    this.height = height
    this.radius = Math.min(this.width / 2, this.height / 2)
  }
}

Orbiter.prototype.updateCamera = function () {
  const position = this.position
  const target = this.target
  const camera = this.entity.getComponent('Camera')

  this.lat = clamp(this.lat, -89.5, 89.5)
  this.lon = this.lon % (360)

  this.currentLat = toDegrees(
    interpolateAngle(
      (toRadians(this.currentLat) + 2 * Math.PI) % (2 * Math.PI),
      (toRadians(this.lat) + 2 * Math.PI) % (2 * Math.PI),
      this.easing
    )
  )
  this.currentLon = toDegrees(
    interpolateAngle(
      (toRadians(this.currentLon) + 2 * Math.PI) % (2 * Math.PI),
      (toRadians(this.lon) + 2 * Math.PI) % (2 * Math.PI),
      this.easing
    )
  )
  this.currentDistance = lerp(this.currentDistance, this.distance, this.easing)

  // set new camera position according to the current
  // rotation at distance relative to target
  latLonToXyz(this.currentLat, this.currentLon, position)
  vec3.scale(position, this.currentDistance)
  vec3.add(position, target)
  mat4.identity(camera.viewMatrix)

  var up = [0, 1, 0]
  mat4.lookAt(camera.viewMatrix, position, target, up)
}

Orbiter.prototype.setup = function () {
  const orbiter = this
  const camera = this.entity.getComponent('Camera')

  function down (x, y, shift) {
    orbiter.dragging = true
    orbiter.dragPos[0] = x
    orbiter.dragPos[1] = y
    if (shift && orbiter.pan) {
      orbiter.clickPosWindow[0] = x
      orbiter.clickPosWindow[1] = y
      vec3.set(orbiter.clickTarget, orbiter.target)
      const targetInViewSpace = vec3.multMat4(vec3.copy(orbiter.clickTarget), camera.viewMatrix)
      orbiter.panPlane = [targetInViewSpace, [0, 0, 1]]
      ray.hitTestPlane(
        getViewRay(camera, orbiter.clickPosWindow[0], orbiter.clickPosWindow[1], orbiter.width, orbiter.height),
        orbiter.panPlane[0],
        orbiter.panPlane[1],
        orbiter.clickPosPlane
      )
      ray.hitTestPlane(
        getViewRay(camera, orbiter.dragPosWindow[0], orbiter.dragPosWindow[1], orbiter.width, orbiter.height),
        orbiter.panPlane[0],
        orbiter.panPlane[1],
        orbiter.dragPosPlane
      )
    } else {
      orbiter.panPlane = null
    }
  }

  function move (x, y, shift) {
    if (!orbiter.dragging) {
      return
    }
    if (shift && orbiter.panPlane) {
      orbiter.dragPosWindow[0] = x
      orbiter.dragPosWindow[1] = y
      ray.hitTestPlane(
        getViewRay(camera, orbiter.clickPosWindow[0], orbiter.clickPosWindow[1], orbiter.width, orbiter.height),
        orbiter.panPlane[0],
        orbiter.panPlane[1],
        orbiter.clickPosPlane
      )
      ray.hitTestPlane(
        getViewRay(camera, orbiter.dragPosWindow[0], orbiter.dragPosWindow[1], orbiter.width, orbiter.height),
        orbiter.panPlane[0],
        orbiter.panPlane[1],
        orbiter.dragPosPlane
      )
      mat4.set(orbiter.invViewMatrix, camera.viewMatrix)
      mat4.invert(orbiter.invViewMatrix)
      vec3.multMat4(vec3.set(orbiter.clickPosWorld, orbiter.clickPosPlane), orbiter.invViewMatrix)
      vec3.multMat4(vec3.set(orbiter.dragPosWorld, orbiter.dragPosPlane), orbiter.invViewMatrix)
      const diffWorld = vec3.sub(vec3.copy(orbiter.dragPosWorld), orbiter.clickPosWorld)
      const target = vec3.sub(vec3.copy(orbiter.clickTarget), diffWorld)
      orbiter.set({ target: target })
      orbiter.updateCamera()
    } else if (orbiter.drag) {
      const dx = x - orbiter.dragPos[0]
      const dy = y - orbiter.dragPos[1]
      orbiter.dragPos[0] = x
      orbiter.dragPos[1] = y

      orbiter.lat += dy / orbiter.dragSlowdown
      orbiter.lon -= dx / orbiter.dragSlowdown

      // TODO: how to have resolution independed scaling? will this code behave differently with retina/pixelRatio=2?
      orbiter.updateCamera()
    }
  }

  function up () {
    orbiter.dragging = false
    orbiter.panPlane = null
  }

  function scroll (dy) {
    if (!orbiter.zoom) {
      return
    }
    orbiter.distance *= 1 + dy / orbiter.zoomSlowdown
    orbiter.distance = clamp(orbiter.distance, orbiter.minDistance, orbiter.maxDistance)
    orbiter.updateCamera()
  }

  function onMouseDown (e) {
    orbiter.updateWindowSize()
    const pos = offset(e, window)
    down(
      pos[0],
      pos[1],
      e.shiftKey || (e.touches && e.touches.length === 2)
    )
  }

  function onMouseMove (e) {
    const pos = offset(e, window)
    move(
      pos[0],
      pos[1],
      e.shiftKey || (e.touches && e.touches.length === 2)
    )
  }

  function onMouseUp (e) {
    up()
  }

  function onWheel (e) {
    scroll(e.deltaY)
    e.preventDefault()
  }

  function onTouchStart (e) {
    e.preventDefault()
    onMouseDown(e)
  }

  this._onMouseDown = onMouseDown
  this._onTouchStart = onTouchStart
  this._onMouseMove = onMouseMove
  this._onMouseUp = onMouseUp
  this._onWheel = onWheel

  this.element.addEventListener('mousedown', onMouseDown)
  this.element.addEventListener('touchstart', onTouchStart)
  this.element.addEventListener('wheel', onWheel)
  window.addEventListener('mousemove', onMouseMove)
  window.addEventListener('touchmove', onMouseMove)
  window.addEventListener('mouseup', onMouseUp)
  window.addEventListener('touchend', onMouseUp)

  this.updateCamera()
}

Orbiter.prototype.dispose = function () {
  this.element.removeEventListener('mousedown', this._onMouseDown)
  this.element.removeEventListener('touchstart', this._onTouchStart)
  this.element.removeEventListener('wheel', this._onWheel)
  window.removeEventListener('mousemove', this._onMouseMove)
  window.removeEventListener('touchmove', this._onMouseMove)
  window.removeEventListener('mouseup', this._onMouseUp)
  window.removeEventListener('touchend', this._onMouseUp)
}

module.exports = function createOrbiter (opts) {
  return new Orbiter(opts)
}
<commit_msg>Fix camera orbiter on iOS<commit_after>const Signal = require('signals')
const mat4 = require('pex-math/mat4')
const vec3 = require('pex-math/vec3')
const quat = require('pex-math/quat')
const ray = require('pex-geom/ray')
const interpolateAngle = require('interpolate-angle')
const clamp = require('pex-math/utils').clamp
const lerp = require('pex-math/utils').lerp
const toRadians = require('pex-math/utils').toRadians
const toDegrees = require('pex-math/utils').toDegrees
const latLonToXyz = require('latlon-to-xyz')
const xyzToLatLon = require('xyz-to-latlon')
const eventOffset = require('mouse-event-offset')

function offset (e, target) {
  if (e.touches) return eventOffset(e.touches[0], target)
  else return eventOffset(e, target)
}

function getViewRay (camera, x, y, windowWidth, windowHeight) {
  if (camera.frustum) {
    x += camera.frustum.offset[0]
    y += camera.frustum.offset[1]
    windowWidth = camera.frustum.totalSize[0]
    windowHeight = camera.frustum.totalSize[1]
  }
  let nx = 2 * x / windowWidth - 1
  let ny = 1 - 2 * y / windowHeight

  let hNear = 2 * Math.tan(camera.fov / 2) * camera.near
  let wNear = hNear * camera.aspect

  nx *= (wNear * 0.5)
  ny *= (hNear * 0.5)

  let origin = [0, 0, 0]
  let direction = vec3.normalize([nx, ny, -camera.near])
  let ray = [origin, direction]

  return ray
}

function Orbiter (opts) {
  this.type = 'Orbiter'
  this.entity = null
  this.dirty = false
  this.changed = new Signal()

  const initialState = {
    target: [0, 0, 0],
    position: [0, 0, 5],
    invViewMatrix: mat4.create(),
    dragging: false,
    lat: 0, // Y
    lon: 0, // XZ
    currentLat: 0,
    currentLon: 0,
    easing: 1,
    element: opts.element || window,
    width: 0,
    height: 0,
    clickPosWindow: [0, 0],
    dragPos: [0, 0, 0],
    dragPosWindow: [0, 0],
    distance: 1,
    currentDistance: 1,
    minDistance: 0.1,
    maxDistance: 10,
    zoomSlowdown: 400,
    zoom: true,
    pan: true,
    drag: true,
    dragSlowdown: 4,
    clickTarget: [0, 0, 0],
    clickPosPlane: [0, 0, 0],
    dragPosPlane: [0, 0, 0],
    clickPosWorld: [0, 0, 0],
    dragPosWorld: [0, 0, 0],
    panPlane: null,
    autoUpdate: true
  }

  this.set(initialState)
  this.set(opts)
}

Orbiter.prototype.init = function (entity) {
  this.entity = entity
  this.setup()
}

Orbiter.prototype.set = function (opts) {
  Object.assign(this, opts)
  if (opts.target || opts.position) {
    const distance = vec3.distance(this.position, this.target)
    const latLon = xyzToLatLon(vec3.normalize(vec3.sub(vec3.copy(this.position), this.target)))
    this.lat = latLon[0]
    this.lon = latLon[1]
    this.currentLat = this.lat
    this.currentLon = this.lon
    this.distance = distance
    this.currentDistance = this.distance
    this.minDistance = opts.minDistance || distance / 10
    this.maxDistance = opts.maxDistance || distance * 10
  }
  Object.keys(opts).forEach((prop) => this.changed.dispatch(prop))
}

const tempmat4 = mat4.create()
Orbiter.prototype.update = function () {
  this.updateCamera()
  const camera = this.entity.getComponent('Camera')

  const transformCmp = this.entity.transform
  const transformRotation = transformCmp.rotation
  mat4.set(tempmat4, camera.viewMatrix)
  mat4.invert(tempmat4)
  quat.fromMat4(transformRotation, tempmat4)
  transformCmp.set({
    position: this.position,
    rotation: transformRotation
  })
}

Orbiter.prototype.updateWindowSize = function () {
  const width = this.element.clientWidth || this.element.innerWidth
  const height = this.element.clientHeight || this.element.innerHeight
  if (width !== this.width) {
    this.width = width
    this.height = height
    this.radius = Math.min(this.width / 2, this.height / 2)
  }
}

Orbiter.prototype.updateCamera = function () {
  const position = this.position
  const target = this.target
  const camera = this.entity.getComponent('Camera')

  this.lat = clamp(this.lat, -89.5, 89.5)
  this.lon = this.lon % (360)

  this.currentLat = toDegrees(
    interpolateAngle(
      (toRadians(this.currentLat) + 2 * Math.PI) % (2 * Math.PI),
      (toRadians(this.lat) + 2 * Math.PI) % (2 * Math.PI),
      this.easing
    )
  )
  this.currentLon = toDegrees(
    interpolateAngle(
      (toRadians(this.currentLon) + 2 * Math.PI) % (2 * Math.PI),
      (toRadians(this.lon) + 2 * Math.PI) % (2 * Math.PI),
      this.easing
    )
  )
  this.currentDistance = lerp(this.currentDistance, this.distance, this.easing)

  // set new camera position according to the current
  // rotation at distance relative to target
  latLonToXyz(this.currentLat, this.currentLon, position)
  vec3.scale(position, this.currentDistance)
  vec3.add(position, target)
  mat4.identity(camera.viewMatrix)

  var up = [0, 1, 0]
  mat4.lookAt(camera.viewMatrix, position, target, up)
}

Orbiter.prototype.setup = function () {
  const orbiter = this
  const camera = this.entity.getComponent('Camera')

  function down (x, y, shift) {
    orbiter.dragging = true
    orbiter.dragPos[0] = x
    orbiter.dragPos[1] = y
    if (shift && orbiter.pan) {
      orbiter.clickPosWindow[0] = x
      orbiter.clickPosWindow[1] = y
      vec3.set(orbiter.clickTarget, orbiter.target)
      const targetInViewSpace = vec3.multMat4(vec3.copy(orbiter.clickTarget), camera.viewMatrix)
      orbiter.panPlane = [targetInViewSpace, [0, 0, 1]]
      ray.hitTestPlane(
        getViewRay(camera, orbiter.clickPosWindow[0], orbiter.clickPosWindow[1], orbiter.width, orbiter.height),
        orbiter.panPlane[0],
        orbiter.panPlane[1],
        orbiter.clickPosPlane
      )
      ray.hitTestPlane(
        getViewRay(camera, orbiter.dragPosWindow[0], orbiter.dragPosWindow[1], orbiter.width, orbiter.height),
        orbiter.panPlane[0],
        orbiter.panPlane[1],
        orbiter.dragPosPlane
      )
    } else {
      orbiter.panPlane = null
    }
  }

  function move (x, y, shift) {
    if (!orbiter.dragging) {
      return
    }
    if (shift && orbiter.panPlane) {
      orbiter.dragPosWindow[0] = x
      orbiter.dragPosWindow[1] = y
      ray.hitTestPlane(
        getViewRay(camera, orbiter.clickPosWindow[0], orbiter.clickPosWindow[1], orbiter.width, orbiter.height),
        orbiter.panPlane[0],
        orbiter.panPlane[1],
        orbiter.clickPosPlane
      )
      ray.hitTestPlane(
        getViewRay(camera, orbiter.dragPosWindow[0], orbiter.dragPosWindow[1], orbiter.width, orbiter.height),
        orbiter.panPlane[0],
        orbiter.panPlane[1],
        orbiter.dragPosPlane
      )
      mat4.set(orbiter.invViewMatrix, camera.viewMatrix)
      mat4.invert(orbiter.invViewMatrix)
      vec3.multMat4(vec3.set(orbiter.clickPosWorld, orbiter.clickPosPlane), orbiter.invViewMatrix)
      vec3.multMat4(vec3.set(orbiter.dragPosWorld, orbiter.dragPosPlane), orbiter.invViewMatrix)
      const diffWorld = vec3.sub(vec3.copy(orbiter.dragPosWorld), orbiter.clickPosWorld)
      const target = vec3.sub(vec3.copy(orbiter.clickTarget), diffWorld)
      orbiter.set({ target: target })
      orbiter.updateCamera()
    } else if (orbiter.drag) {
      const dx = x - orbiter.dragPos[0]
      const dy = y - orbiter.dragPos[1]
      orbiter.dragPos[0] = x
      orbiter.dragPos[1] = y

      orbiter.lat += dy / orbiter.dragSlowdown
      orbiter.lon -= dx / orbiter.dragSlowdown

      // TODO: how to have resolution independed scaling? will this code behave differently with retina/pixelRatio=2?
      orbiter.updateCamera()
    }
  }

  function up () {
    orbiter.dragging = false
    orbiter.panPlane = null
  }

  function scroll (dy) {
    if (!orbiter.zoom) {
      return
    }
    orbiter.distance *= 1 + dy / orbiter.zoomSlowdown
    orbiter.distance = clamp(orbiter.distance, orbiter.minDistance, orbiter.maxDistance)
    orbiter.updateCamera()
  }

  function onMouseDown (e) {
    orbiter.updateWindowSize()
    const pos = offset(e, window)
    down(
      pos[0],
      pos[1],
      e.shiftKey || (e.touches && e.touches.length === 2)
    )
  }

  function onMouseMove (e) {
    const pos = offset(e, window)
    move(
      pos[0],
      pos[1],
      e.shiftKey || (e.touches && e.touches.length === 2)
    )
  }

  function onMouseUp (e) {
    up()
  }

  function onWheel (e) {
    scroll(e.deltaY)
    e.preventDefault()
  }

  function onTouchStart (e) {
    e.preventDefault()
    onMouseDown(e)
  }

  function onTouchMove (e) {
    e.preventDefault()
    onMouseMove(e)
  }

  this._onMouseDown = onMouseDown
  this._onTouchStart = onTouchStart
  this._onMouseMove = onMouseMove
  this._onTouchMove = onTouchMove
  this._onMouseUp = onMouseUp
  this._onWheel = onWheel

  this.element.addEventListener('mousedown', onMouseDown)
  this.element.addEventListener('touchstart', onTouchStart)
  this.element.addEventListener('wheel', onWheel)
  window.addEventListener('mousemove', onMouseMove)
  window.addEventListener('touchmove', onTouchMove, { passive: false })
  window.addEventListener('mouseup', onMouseUp)
  window.addEventListener('touchend', onMouseUp)

  this.updateCamera()
}

Orbiter.prototype.dispose = function () {
  this.element.removeEventListener('mousedown', this._onMouseDown)
  this.element.removeEventListener('touchstart', this._onTouchStart)
  this.element.removeEventListener('wheel', this._onWheel)
  window.removeEventListener('mousemove', this._onMouseMove)
  window.removeEventListener('touchmove', this._onMouseMove)
  window.removeEventListener('mouseup', this._onMouseUp)
  window.removeEventListener('touchend', this._onMouseUp)
}

module.exports = function createOrbiter (opts) {
  return new Orbiter(opts)
}
<|endoftext|>


<commit_before>/*
 * tzmap.js - Library for working with the geography of timezones in JavaScript

 * Written in 2011 by L. David Baron <dbaron@dbaron.org>

 * To the extent possible under law, the author(s) have dedicated all
 * copyright and related and neighboring rights to this software to the
 * public domain worldwide.  This software is distributed without any
 * warranty.
 *
 * You should have received a copy of the CC0 Public Domain Dedication
 * along with this software.  If not, see
 * <http://creativecommons.org/publicdomain/zero/1.0/>.
 */
(function() {
    "use strict";

    var gXHR = null;
    var gLoadSuccessCallbacks = [];
    var gLoadErrorCallbacks = [];
    var gJSON = null;

    var public_loadData = function(success_callback, error_callback) {
        if (gJSON) {
            if (success_callback) {
                setTimeout(success_callback, 0);
            }
            return;
        }
        if (success_callback) {
            gLoadSuccessCallbacks.push(success_callback);
        }
        if (error_callback) {
            gLoadErrorCallbacks.push(error_callback);
        }
        if (gXHR) {
            return;
        }

        var isHTTP = window.location.protocol == "http:" ||
                     window.location.protocol == "https:";

        function rsc() {
            if (gXHR.readyState != 4) {
                return;
            }

            var success = false;
            if (!isHTTP || (200 <= gXHR.status && gXHR.status < 300)) {
                var json = null;
                try {
                    json = JSON.parse(gXHR.responseText);
                } catch (ex) {
                }
                if (json && json.chains && json.zones) {
                    success = true;
                    gJSON = json;
                }
            }

            var callbacks = success ? gLoadSuccessCallbacks
                                    : gLoadErrorCallbacks;

            gLoadSuccessCallbacks = [];
            gLoadErrorCallbacks = [];
            gXHR = null;

            for (var idx in callbacks) {
                callbacks[idx]();
            }
        }

        gXHR = new XMLHttpRequest();
        gXHR.onreadystatechange = rsc;
        gXHR.open("GET", isHTTP ? "world-map.json.gz" : "world-map.json");
        gXHR.send();
    }

    /**
     * Check if needle is in [rangea, rangeb] or [rangeb, rangea].
     */
    function in_range(needle, rangea, rangeb) {
        if (rangea < rangeb) {
            return rangea <= needle && needle <= rangeb;
        } else {
            return rangeb <= needle && needle <= rangea;
        }
    }

    var public_zoneAt = function(lat, lon) {
        var zones = gJSON.zones;
        var chains = gJSON.chains;

        if (lat >= 90 || lat <= -90)
            return null;
        lon = ((lon % 360) + 180) % 360 - 180;

        for (var tzid in zones) {
            var zone = zones[tzid];
            for (var regionIdx in zone) {
                var region = zone[regionIdx];

                // Since we don't need to worry about zones containing
                // the north pole (FIXME: really?), we can just count
                // the number of times that a north-south line from the
                // north pole to our point intersects the zone.
                // Furthermore, the segments are short enough that we
                // don't need to worry about whether they're great
                // circle lines or lines on an easier projection.
                var intersects = 0;
                for (var chainIdx in region) {
                    var chainobj = region[chainIdx];
                    var chainID = chainobj[0];
                    var chainInv = chainobj[1];
                    var chain = chains[chainID];
                    var prevlon, prevlat;
                    for (var pointIdx in chain) {
                        var pointobj = chain[pointIdx];
                        var ptlon = pointobj[0];
                        var ptlat = pointobj[1];
                        if (pointIdx > 0) {
                            if (ptlon == prevlon) {
                                // vertical line.  All we need to do is
                                // check if our point is *on* it.
                                if (ptlon == lon
                                    && in_range(lat, ptlat, prevlat))
                                    return tzid;
                            } else {
                                var alon, ptalon, prevalon;
                                if (Math.abs(ptlon - prevlon) > 180) {
                                    // this segment crosses the date line,
                                    // so use adjusted numbers instead
                                    alon = (lon + 180) % 360;
                                    ptalon = (ptlon + 180) % 360;
                                    prevalon = (prevlon + 180) % 360;
                                } else {
                                    alon = lon;
                                    ptalon = ptlon;
                                    prevalon = prevlon;
                                }

                                var segInv;
                                if (ptalon < prevalon) {
                                    var tmp = ptalon;
                                    ptalon = prevalon;
                                    prevalon = tmp;
                                    segInv = !chainInv;
                                } else {
                                    segInv = chainInv;
                                }

                                if ((prevalon < alon && alon < ptalon)
                                    || (segInv ? (ptalon == alon)
                                               : (prevalon == alon))) {
                                    var xlat = prevlat + (ptlat - prevlat) * ((alon - prevalon) / (ptalon - prevalon));
                                    if (xlat == lat)
                                        // on the line
                                        return tzid;
                                    if (xlat > lat) {
                                        ++intersects;
                                    }
                                }
                            }
                        }
                        prevlon = ptlon;
                        prevlat = ptlat;
                    }
                }
                if (intersects % 2 == 1) {
                    return tzid;
                }
            }
        }
        return null;
    }

    // Exports:
    window.tzmap = {
        /**
         * loadData(success_callback, error_callback)
         *
         * This library has to load a significant amount of timezone
         * boundary data in order to work.  This function triggers the
         * loading of the data.  When the loading completes
         * successfully, success_callback is called; if it fails,
         * error_callback is called.
         *
         * Other methods of this library can be used only after
         * success_callback has been called.
         */
        loadData: public_loadData,

        /**
         * zoneAt(lat, lon)
         *
         * Return the time zone name (e.g., "America/Los_Angeles") at
         * the given latitude and longitude, or null if no time zone is
         * found at that location.  If the location is exactly on a time
         * zone boundary, which time zone will be returned is undefined,
         * but one of them will be.
         */
        zoneAt: public_zoneAt,
    };
})();
<commit_msg>Better handling of segment endpoints, but still getting false positives of America/Iqaluit (or America/Nassau if from south pole) for 40, -75.<commit_after>/*
 * tzmap.js - Library for working with the geography of timezones in JavaScript

 * Written in 2011 by L. David Baron <dbaron@dbaron.org>

 * To the extent possible under law, the author(s) have dedicated all
 * copyright and related and neighboring rights to this software to the
 * public domain worldwide.  This software is distributed without any
 * warranty.
 *
 * You should have received a copy of the CC0 Public Domain Dedication
 * along with this software.  If not, see
 * <http://creativecommons.org/publicdomain/zero/1.0/>.
 */
(function() {
    "use strict";

    var gXHR = null;
    var gLoadSuccessCallbacks = [];
    var gLoadErrorCallbacks = [];
    var gJSON = null;

    var public_loadData = function(success_callback, error_callback) {
        if (gJSON) {
            if (success_callback) {
                setTimeout(success_callback, 0);
            }
            return;
        }
        if (success_callback) {
            gLoadSuccessCallbacks.push(success_callback);
        }
        if (error_callback) {
            gLoadErrorCallbacks.push(error_callback);
        }
        if (gXHR) {
            return;
        }

        var isHTTP = window.location.protocol == "http:" ||
                     window.location.protocol == "https:";

        function rsc() {
            if (gXHR.readyState != 4) {
                return;
            }

            var success = false;
            if (!isHTTP || (200 <= gXHR.status && gXHR.status < 300)) {
                var json = null;
                try {
                    json = JSON.parse(gXHR.responseText);
                } catch (ex) {
                }
                if (json && json.chains && json.zones) {
                    success = true;
                    gJSON = json;
                }
            }

            var callbacks = success ? gLoadSuccessCallbacks
                                    : gLoadErrorCallbacks;

            gLoadSuccessCallbacks = [];
            gLoadErrorCallbacks = [];
            gXHR = null;

            for (var idx in callbacks) {
                callbacks[idx]();
            }
        }

        gXHR = new XMLHttpRequest();
        gXHR.onreadystatechange = rsc;
        gXHR.open("GET", isHTTP ? "world-map.json.gz" : "world-map.json");
        gXHR.send();
    }

    /**
     * Check if needle is in [rangea, rangeb] or [rangeb, rangea].
     */
    function in_range(needle, rangea, rangeb) {
        if (rangea < rangeb) {
            return rangea <= needle && needle <= rangeb;
        } else {
            return rangeb <= needle && needle <= rangea;
        }
    }

    var public_zoneAt = function(lat, lon) {
        var zones = gJSON.zones;
        var chains = gJSON.chains;

        if (lat >= 90 || lat <= -90)
            return null;
        lon = ((lon % 360) + 180) % 360 - 180;

        for (var tzid in zones) {
            var zone = zones[tzid];
            for (var regionIdx in zone) {
                var region = zone[regionIdx];

                // Since we don't need to worry about zones containing
                // the north pole (FIXME: really?), we can just count
                // the number of times that a north-south line from the
                // north pole to our point intersects the zone.
                // Furthermore, the segments are short enough that we
                // don't need to worry about whether they're great
                // circle lines or lines on an easier projection.
                var intersects = 0;
                for (var chainIdx in region) {
                    var chainobj = region[chainIdx];
                    var chainID = chainobj[0];
                    var chainInv = chainobj[1];
                    var chain = chains[chainID];
                    var prevlon, prevlat;
                    for (var pointIdx in chain) {
                        var pointobj = chain[pointIdx];
                        var ptlon = pointobj[0];
                        var ptlat = pointobj[1];
                        if (pointIdx > 0) {
                            if (ptlon == prevlon) {
                                // vertical line.  All we need to do is
                                // check if our point is *on* it.
                                if (ptlon == lon
                                    && in_range(lat, ptlat, prevlat))
                                    return tzid;
                            } else {
                                var alon, ptalon, prevalon;
                                if (Math.abs(ptlon - prevlon) > 180) {
                                    // this segment crosses the date line,
                                    // so use adjusted numbers instead
                                    alon = (lon + 180) % 360;
                                    ptalon = (ptlon + 180) % 360;
                                    prevalon = (prevlon + 180) % 360;
                                } else {
                                    alon = lon;
                                    ptalon = ptlon;
                                    prevalon = prevlon;
                                }

                                if (ptalon < prevalon) {
                                    var tmp = ptalon;
                                    ptalon = prevalon;
                                    prevalon = tmp;
                                } else {
                                }

                                // Check the endpoint at the west end
                                // but not the east end.  This does the
                                // right thing for the intersects count,
                                // since we need to worry about both
                                // what happens when the segments turn
                                // back the opposite east/west or
                                // continue the same east/west from the
                                // boundary.  FIXME: But it's not quite
                                // right for the on-the-line check.
                                if (prevalon <= alon && alon < ptalon) {
                                    var xlat = prevlat + (ptlat - prevlat) * ((alon - prevalon) / (ptalon - prevalon));
                                    if (xlat == lat)
                                        // on the line
                                        return tzid;
                                    if (xlat > lat) {
                                        ++intersects;
                                    }
                                }
                            }
                        }
                        prevlon = ptlon;
                        prevlat = ptlat;
                    }
                }
                if (intersects % 2 == 1) {
                    return tzid;
                }
            }
        }
        return null;
    }

    // Exports:
    window.tzmap = {
        /**
         * loadData(success_callback, error_callback)
         *
         * This library has to load a significant amount of timezone
         * boundary data in order to work.  This function triggers the
         * loading of the data.  When the loading completes
         * successfully, success_callback is called; if it fails,
         * error_callback is called.
         *
         * Other methods of this library can be used only after
         * success_callback has been called.
         */
        loadData: public_loadData,

        /**
         * zoneAt(lat, lon)
         *
         * Return the time zone name (e.g., "America/Los_Angeles") at
         * the given latitude and longitude, or null if no time zone is
         * found at that location.  If the location is exactly on a time
         * zone boundary, which time zone will be returned is undefined,
         * but one of them will be.
         */
        zoneAt: public_zoneAt,
    };
})();
<|endoftext|>