<commit_before>(function(define) {'use strict';
define(function(require) {

// Module that contains methods for displaying and formatting variables/datasets
return function(loader) {
   var utils;
   utils    = require('../utils');

   // build array of arrays of objects (array of rows to be displayed)
   // each object has a `name` and a `value` property.
   // `ncol` is a positive integer
   loader.addInstanceMethod('Variable', 'layOut', function layOut(ncol) {
      var rows, currentRow, i, stringVar, names;
      names = this.names();
      stringVar = this.asString();
      ncol = Math.floor(ncol);
      if (!(ncol >= 1)) { ncol = 1; }
      rows = [];
      currentRow = [];
      for (i = 1; i <= this.length(); i += 1) {
         currentRow.push({
            index: i,
            value: utils.getDefault(stringVar.get(i), ''),
            name:  utils.isMissing(names) ? '' : names.get(i)
         });
         if (currentRow.length >= ncol) {
            rows.push(currentRow);
            currentRow = [];
         }
      }
      // if we have an unfinished row, include it before returning
      if (currentRow.length > 0) { rows.push(currentRow); }
      return rows;
   });

   // Returns a string (an HTML expression) for displaying the variable.
   // `options` can include:
   //  - ncol: a positive integer, defaults to 1
   //  - withNames: a boolean value, defaults to false
   //  - value: an object with properties `tag` and `class` for specifying the
   //           html tag and the class attribute.
   //  - name:  an object... same as value
   //  - row:   an object... same as value
   //  All three of these objects are optional, and their individual parts are
   //  optional as well.  Defaults to `<td>` or `<tr>` for the tag and to
   //  `var-value`, `var-name`, `var-row` for the class.
   loader.addInstanceMethod('Variable', 'toHTML', function toHTML(options) {
      var arr, defaults, makeRow;
      function wrap(params) {
         return function(str, others) {
            return '<' + params.tag + ' class="' + params.class + '" ' +
                  (others || '') + '>' + str +
                  '</' + params.tag + '>';
         };
      }
      defaults = {
         ncol: 1,
         withNames: false,
         value: { tag: 'td', class: 'var-value' },
         name:  { tag: 'td', class: 'var-name' },
         row:   { tag: 'tr', class: 'var-row' }
      };
      options = utils.mixin({}, options, defaults);
      options.value = utils.mixin(options.value, defaults.value);
      options.name  = utils.mixin(options.name, defaults.name);
      options.row   = utils.mixin(options.row, defaults.row);
      makeRow = options.withNames ?
         function(row) {
            // entries include names and values
            return row.map(function(obj) {
               return wrap(options.name)(obj.name  , 'data-relIndex="' + obj.index + '"') +
                      wrap(options.value)(obj.value, 'data-relIndex="' + obj.index + '"');
            }).join('');
         } :
         function(row) {
            return row.map(function(obj) {
               return wrap(options.value)(obj.value, 'data-relIndex="' + obj.index + '"');
            }).join('');
         };
      arr = this.layOut(options.ncol);
      return arr.map(function(row) {
         // row is an array of objects with properties `value` and `name`
         return makeRow(row);
      }).map(wrap(options.row)).join('\n');
   });

   // Returns a string variable for displaying the given variable with
   // specified numerical formatting. `options` is an object and may
   // include the following properties:
   //  - type:  has a string value 'scientific' or 'fixed
   //  - decimals:  indicates fixed number of decimal digits to the right of
   //       the decimal point; has a non-negative integer value; defaults to 4 in
   //       the case of scientific notation, and to 2 otherwise
   loader.addInstanceMethod('Variable', 'format', function format(options) {
      var v, maxDigits;
      v = this.asScalar().names(this.names());
      function nDigits(x) {
         return x === 0 ? 1 : Math.abs(Math.log(Math.abs(x)) / Math.LN10);
      }
      options = options || {};
      if (!options.type) {
         maxDigits = Math.floor(v.map(nDigits).max());
         options.type = maxDigits > 4 ? 'scientific' : 'fixed';
      }
      if (!(options.decimals >= 0)) {
         options.decimals = options.type === 'scientific' ? 4 : 2;
      }
      return v.map(utils.format[options.type](options.decimals), false, 'string');
   });

// boilerplate below here
};

});

}(typeof define === 'function' && define.amd ? define : function(factory) {
   'use strict';
   module.exports = factory(require);
}));
<commit_msg>Variable#format should not alter the passed obj<commit_after>(function(define) {'use strict';
define(function(require) {

// Module that contains methods for displaying and formatting variables/datasets
return function(loader) {
   var utils;
   utils    = require('../utils');

   // build array of arrays of objects (array of rows to be displayed)
   // each object has a `name` and a `value` property.
   // `ncol` is a positive integer
   loader.addInstanceMethod('Variable', 'layOut', function layOut(ncol) {
      var rows, currentRow, i, stringVar, names;
      names = this.names();
      stringVar = this.asString();
      ncol = Math.floor(ncol);
      if (!(ncol >= 1)) { ncol = 1; }
      rows = [];
      currentRow = [];
      for (i = 1; i <= this.length(); i += 1) {
         currentRow.push({
            index: i,
            value: utils.getDefault(stringVar.get(i), ''),
            name:  utils.isMissing(names) ? '' : names.get(i)
         });
         if (currentRow.length >= ncol) {
            rows.push(currentRow);
            currentRow = [];
         }
      }
      // if we have an unfinished row, include it before returning
      if (currentRow.length > 0) { rows.push(currentRow); }
      return rows;
   });

   // Returns a string (an HTML expression) for displaying the variable.
   // `options` can include:
   //  - ncol: a positive integer, defaults to 1
   //  - withNames: a boolean value, defaults to false
   //  - value: an object with properties `tag` and `class` for specifying the
   //           html tag and the class attribute.
   //  - name:  an object... same as value
   //  - row:   an object... same as value
   //  All three of these objects are optional, and their individual parts are
   //  optional as well.  Defaults to `<td>` or `<tr>` for the tag and to
   //  `var-value`, `var-name`, `var-row` for the class.
   loader.addInstanceMethod('Variable', 'toHTML', function toHTML(options) {
      var arr, defaults, makeRow;
      function wrap(params) {
         return function(str, others) {
            return '<' + params.tag + ' class="' + params.class + '" ' +
                  (others || '') + '>' + str +
                  '</' + params.tag + '>';
         };
      }
      defaults = {
         ncol: 1,
         withNames: false,
         value: { tag: 'td', class: 'var-value' },
         name:  { tag: 'td', class: 'var-name' },
         row:   { tag: 'tr', class: 'var-row' }
      };
      options = utils.mixin({}, options, defaults);
      options.value = utils.mixin(options.value, defaults.value);
      options.name  = utils.mixin(options.name, defaults.name);
      options.row   = utils.mixin(options.row, defaults.row);
      makeRow = options.withNames ?
         function(row) {
            // entries include names and values
            return row.map(function(obj) {
               return wrap(options.name)(obj.name  , 'data-relIndex="' + obj.index + '"') +
                      wrap(options.value)(obj.value, 'data-relIndex="' + obj.index + '"');
            }).join('');
         } :
         function(row) {
            return row.map(function(obj) {
               return wrap(options.value)(obj.value, 'data-relIndex="' + obj.index + '"');
            }).join('');
         };
      arr = this.layOut(options.ncol);
      return arr.map(function(row) {
         // row is an array of objects with properties `value` and `name`
         return makeRow(row);
      }).map(wrap(options.row)).join('\n');
   });

   // Returns a string variable for displaying the given variable with
   // specified numerical formatting. `options` is an object and may
   // include the following properties:
   //  - type:  has a string value 'scientific' or 'fixed
   //  - decimals:  indicates fixed number of decimal digits to the right of
   //       the decimal point; has a non-negative integer value; defaults to 4 in
   //       the case of scientific notation, and to 2 otherwise
   loader.addInstanceMethod('Variable', 'format', function format(options) {
      var v, maxDigits;
      v = this.asScalar().names(this.names());
      function nDigits(x) {
         return x === 0 ? 1 : Math.abs(Math.log(Math.abs(x)) / Math.LN10);
      }
      options = utils.mixin({}, options);
      if (!options.type) {
         maxDigits = Math.floor(v.map(nDigits).max());
         options.type = maxDigits > 4 ? 'scientific' : 'fixed';
      }
      if (!(options.decimals >= 0)) {
         options.decimals = options.type === 'scientific' ? 4 : 2;
      }
      return v.map(utils.format[options.type](options.decimals), false, 'string');
   });

// boilerplate below here
};

});

}(typeof define === 'function' && define.amd ? define : function(factory) {
   'use strict';
   module.exports = factory(require);
}));
<|endoftext|>


<commit_before>$(window).load(function() {

	// don't init gallery if it consists of a single photo
	if($("div.image").length > 1) {
		// wrap images
		$("div.image").wrapAll("

");

		// init gallery: Gallery.init(imageHolder, imageWrapperWidth, imageCountHolder, nextButton, prevButton)
		Gallery.init($("div#image-holder"), $("div.image")[0].offsetWidth, $("p#gallery-count").children("span")[0], $("a#next-image"), $("a#previous-image"));
	}
});<commit_msg>Made JS fire before all images are loaded<commit_after>$(function() {

	// don't init gallery if it consists of a single photo
	if($("div.image").length > 1) {
		// wrap images
		$("div.image").wrapAll("

");

		// init gallery: Gallery.init(imageHolder, imageWrapperWidth, imageCountHolder, nextButton, prevButton)
		Gallery.init($("div#image-holder"), 560, $("p#gallery-count").children("span")[0], $("a#next-image"), $("a#previous-image"));
	}
});<|endoftext|>


<commit_before>require("should");

var credentials = require("./credentials");
var arb = require("../../lib/arb")(credentials.loginName, credentials.transactionKey);

describe("arb.getSubscriptionStatus", function() {
    it("should get status", function(done) {
        arb.getSubscriptionStatus({ refId: "my-ref", subscriptionId: "2109340800" }, function(error, response) {
            (typeof error === "undefined").should.be.true;

            response.status.length.should.be.greaterThan(0);

            done();
        });
    });
});
<commit_msg>Fix getSubscriptionStatus system test<commit_after>require("should");

var credentials = require("./credentials");
var arb = require("../../lib/arb")(credentials.loginName, credentials.transactionKey);

describe("arb.getSubscriptionStatus", function() {
    it("should get status", function(done) {
        arb.getSubscriptionStatus({ refId: "my-ref", subscriptionId: "21093408" }, function(error, response) {
            (typeof error === "undefined").should.be.true;

            response.status.length.should.be.greaterThan(0);

            done();
        });
    });
});
<|endoftext|>


<commit_before>
// UI is a singleton with events
function UI(){};
UI.prototype = new EventEmitter();
UI = new UI();

// fires a custom event after the user has finished resizing the window
UI.once('init', function(){
	var UI = this
	  , settle_time = 100 // ms to wait before firing our event
	  , resize_timer;

	function emitResized(){
		UI.emit('resized');
		resize_timer = null;
	}

	$(window).resize(function(){
		if(resize_timer) clearTimeout(resize_timer);
		resize_timer = setTimeout(emitResized, settle_time);
	});
});

UI.once('init', function(){
	if(!readonly){
		IO.socket.on('connect', function(){
			$('#message').attr('disabled', false).focus();
		});
	}
});

UI.update_connection_status = function(text){
	$('#connection_status').text(text);
};

<commit_msg>Remove remnants of JS layout resizing code<commit_after>
// UI is a singleton with events
function UI(){};
UI.prototype = new EventEmitter();
UI = new UI();

UI.once('init', function(){
	if(!readonly){
		IO.socket.on('connect', function(){
			$('#message').attr('disabled', false).focus();
		});
	}
});

UI.update_connection_status = function(text){
	$('#connection_status').text(text);
};

<|endoftext|>


<commit_before>var path = require('path');

module.exports = function(instance, def) {

	var __class = {
		_name: 'unknow',
		_column: {

		},
		_index: null,

		_collection: null,
		_db: null,

		init: function() {
			// support __name in each field
			var columns = _.encodeObject(this._column, '__type');
			try {
				_.each(columns, function(column, key) {
					(function(column, key) {
						var callback = arguments.callee;
						if(_.isArray(column)) {
							if(_.isObject(column[0]) && !_.isArray(column[0])) {
								_.each(column[0], function(v, k) {
									callback(column[0][k], key + '.$.' + k);
								});
							} else {
								callback(column[0], key);
							};
						} else {
							if(_.isObject(column)) {
								if(column.__type !== undefined) {
									column.__name = key;
								};
							};
						};
					})(columns[key], key);
				});
			} catch(ex) {
			};
			this._column = _.decodeObject(columns);
			// re-init column, support quick define: {}: object datatype, []: array datatype
			try {
				var tmp = {};
				var callback = function(res, value, key) {
					var args = arguments;
					var tmp = value;
					if(_.isArray(value)) {
						var a = {
							res: value[0]
						};
						args.callee(a, value[0], key + '.$');
						tmp = instance.base.types.array(a['res']);
						tmp.__name = key + '.$';
					} else if(_.isObject(value) && value.__type === undefined) {
						_.each(tmp, function(v, k) {
							var a = {
								res: v
							};
							args.callee(a, v, key + '.' + k);
							tmp[k] = a.res;
						});
						tmp = instance.base.types.object(tmp);
						tmp.__name = key;
					}
					res.res = tmp;
				};
				_.each(this._column, function(value, key) {
					var res = {
						res: value
					};
					callback(res, value, key);
					tmp[key] = res.res;
				});
				this._column = tmp;
			} catch(ex) {
				throw ex;
			};
			// call super
			this._super.apply(this, arguments);
		},

		getCollection: function(callback) {
			var log = this;
			if(this._collection) {
				callback(this._collection);
			} else {
				if(this.getDB()) {
					this.getDB().collection(this._name, function(err, collection) {
						if(!err) {
							this._collection = collection;
							callback(this._collection);
						} else {
							throw {
								func: 'getCollection',
								message: 'collection is null'
							};
						};
					});
				};
			};
		},

		getDB: function() {
			this._db === null ? this._db = def.db : null;
			if(this._db === null) {
				throw {
					func: 'getDB',
					message: 'db is null'
				};
			};
			return this._db;
		},

		create_validate: function(args) {
			// check default & require value
			_.each(this._column, function(column, name) {
				args[name] = column.validate(args[name]);
			});
			// remove all undefined property
			_.each(args, function(value, key) {
				if(value === undefined) {
					delete args[key];
				};
			});
			// return
			return args;
		},

		update_validate: function(selector, document, options) {
			var log = this;
			var self = this;
			var columns = this._column;
			/*
			* fields query
			*/
			// $inc: don't need to check datatype

			// $rename: doesn't support
			if(document.$rename) {
				throw {
					msg: "ekitjs doesn't support $rename"
				};
			};

			// $setOnInsert: use on upsert, check when create

			// $set: don't know how to check datatype yet. It's too complicated. I trust you.

			// $unset: doesn't support
			if(document.$unset) {
				throw {
					msg: "ekitjs doesn't support $unset"
				};
			};

			/*
			* array query
			*/

			// $addToSet: I trust you.

			// $pop: I trust you.

			// $pullAll: I trust you.

			// $pull: I trust you.

			// $pushAll: I trust you.

			// $push: I trust you.

			return true;
		},

		create: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result) {
				callback.call(self, err, result);
			});
			// validate args
			try {
				args[0] = this.create_validate(args[0]);
			} catch(ex) {
				log.log({
					func: 'create',
					args: JSON.stringify(arguments),
					ex: ex
				});
				callback.call(self, ex, null);
				return;
			};
			// execute
			this.getCollection(function(collection) {
				collection.insert.apply(collection, args);
			});
			return;
		},

		read: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			// remove disable _id field
			var fields = {};
			_.each(args, function(value, key, l, opt) {
				if(opt.index === 0) {
					return;
				};
				if(value.fields !== undefined) {
					if(value.fields._id !== undefined) {
						delete args[key].fields._id;
					};
					fields = args[key].fields;
				};
			});
			// find
			this.getCollection(function(collection) {
				collection.find.apply(collection, args).toArray(function(err, data) {
					if(err) {
						return callback.call(self, err, []);
					} else {
						/*
						* init function field (store === false)
						*/
						// init loop injection
						if(self.___loop_injection === true) {
							return callback.call(self, null, data);
						};
						self.___loop_injection = true;

						// callback & reset loop_injection
						var success = function() {
							try {
								self.___loop_injection = false;
								delete self.___loop_injection;
							} catch(ex) {
							};
							callback.call(self, null, data);
						};

						// get ids
						var ids = [];
						var keyData = {};
						_.each(data, function(value) {
							ids.push(value._id);
							keyData[value._id] = value;
						});

						// get hide & show fields
						var isQueryHide = null;
						var show_fields = [];
						var hidden_fields = [];
						_.each(fields, function(value, key) {
							if(value === 0) {
								hidden_fields.push(key);
								isQueryHide = true;
							} else if(value === 1) {
								show_fields.push(key);
								isQueryHide = false;
							}
							;
						});

						// filter function fields in column with hidden fields
						var funcs = {};
						_.each(self._column, function(column, name) {
							switch(column.__type) {
							case 'func':
								funcs[name] = column;
								break;
							case 'object':
								funcs[name] = column.getFuncField();
								break;
							};
						});
						funcs = _.encodeObject(funcs, '__type');
						_.each(funcs, function(value, key) {
							// just keep function with store === false
							if(value.get('store') !== false) {
								delete funcs[key];
								return;
							};
							if(isQueryHide === true) {
								// remove function field in hidden_fields
								_.each(hidden_fields, function(field) {
									if(key.indexOf(field) === 0) {
										delete funcs[key];
									}
								});
							} else if(isQueryHide === false) {
								// remove function field not in show_fields
								var isIn = false;
								_.each(show_fields, function(field) {
									if(key.indexOf(field) === 0) {
										isIn = true;
									};
								});
								if(!isIn) {
									delete funcs[key];
								};
							}
							;
						});

						// sort funcs by sequence
						_.sortObject(funcs, function(a, b) {
							return a.get('sequence') - b.get('sequence');
						});

						// rebuild function fields sequence
						var sequence = [];
						_.each(funcs, function(func, key, l, opt) {
							sequence.push((function(func, key, opt) {
								return function(fcb) {
									func.get('get').call(self, ids, keyData, function(funcData) {
										_.each(funcData, function(fData, _id) {
											var tmp = {};
											if(func.get('multi') === true) {
												tmp[key] = fData[key];
											} else {
												tmp[key] = fData;
											};
											_.setObject(keyData[_id], tmp);
										});
										data = _.values(keyData);
										// callback & reset loop_injection
										if(opt.isLast === true) {
											success();
										};
										fcb();
									});
								};
							})(func, key, opt));
						});

						if(sequence.length === 0) {
							success();
						} else {
							(function(sequence) {
								var args = arguments;
								if(sequence.length === 0) {
									return;
								};
								var func = sequence.shift();
								func(function() {
									args.callee(sequence);
								});
							})(sequence);
						};
					};
				});
			});
		},

		update: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result, extra) {
				if(err) {
					callback.call(self, err, 0, extra);
				} else {
					callback.call(self, null, result, extra);
				};
			});
			// init full args
			if(args.length === 3) {
				args[3] = args[2];
				args[2] = {};
			};
			// normal update
			var func_update = function() {
				// validate args
				try {
					// remove $setOnInsert
					delete args[1].$setOnInsert;
					// start validate
					this.update_validate(args[0], args[1], args[2]);
				} catch(ex) {
					log.log({
						func: 'update',
						args: JSON.stringify(arguments),
						ex: ex
					});
					callback.call(self, ex, 0);
					return;
				};
				// start update
				this.getCollection(function(collection) {
					collection.update.apply(collection, args);
				});
			};

			// check upsert
			var upsert = args[2].upsert;
			delete args[2].upsert;
			if(upsert === true) {
				this.read(args[0], {
					_id: 1
				}, function(err, data) {
					if(data.length > 0) {
						func_update.call(self);
					} else {
						// create using $setOnInsert
						if(args[1].$setOnInsert !== undefined) {
							self.create(args[1].$setOnInsert, function(e, r) {
								if(e) {
									args[3](e, 0);
								} else {
									args[3](null, 1, r);
								};
							});
						} else {
							args[3]({
								msg: 'can not find $setOnInsert'
							}, 0);
						};
					};
				});
			} else {
				func_update.call(self);
			};
			return;
		},

		'delete': function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result) {
				if(err) {
					callback.call(self, null);
				} else {
					callback.call(self, result);
				};
			});
			// delete
			this.getCollection(function(collection) {
				collection.remove.apply(collection, args);
			});
			return true;
		}
	};

	instance.base.model = instance.base.base.extend(__class);

	// config model static type
	instance.base.model.__type = 'model';
	instance.base.model.__extend = instance.base.model.extend;
	instance.base.model.extend = function() {
		var res = instance.base.model.__extend.apply(instance.base.model, arguments);
		res.__type = instance.base.model.__type;
		return res;
	};
};
<commit_msg>done CRUD<commit_after>var path = require('path');

module.exports = function(instance, def) {

	var __class = {
		_name: 'unknow',
		_column: {

		},
		_index: null,

		_collection: null,
		_db: null,

		init: function() {
			// support __name in each field
			var columns = _.encodeObject(this._column, '__type');
			try {
				_.each(columns, function(column, key) {
					(function(column, key) {
						var callback = arguments.callee;
						if(_.isArray(column)) {
							if(_.isObject(column[0]) && !_.isArray(column[0])) {
								_.each(column[0], function(v, k) {
									callback(column[0][k], key + '.$.' + k);
								});
							} else {
								callback(column[0], key);
							};
						} else {
							if(_.isObject(column)) {
								if(column.__type !== undefined) {
									column.__name = key;
								};
							};
						};
					})(columns[key], key);
				});
			} catch(ex) {
			};
			this._column = _.decodeObject(columns);
			// re-init column, support quick define: {}: object datatype, []: array datatype
			try {
				var tmp = {};
				var callback = function(res, value, key) {
					var args = arguments;
					var tmp = value;
					if(_.isArray(value)) {
						var a = {
							res: value[0]
						};
						args.callee(a, value[0], key + '.$');
						tmp = instance.base.types.array(a['res']);
						tmp.__name = key + '.$';
					} else if(_.isObject(value) && value.__type === undefined) {
						_.each(tmp, function(v, k) {
							var a = {
								res: v
							};
							args.callee(a, v, key + '.' + k);
							tmp[k] = a.res;
						});
						tmp = instance.base.types.object(tmp);
						tmp.__name = key;
					}
					res.res = tmp;
				};
				_.each(this._column, function(value, key) {
					var res = {
						res: value
					};
					callback(res, value, key);
					tmp[key] = res.res;
				});
				this._column = tmp;
			} catch(ex) {
				throw ex;
			};
			// call super
			this._super.apply(this, arguments);
		},

		getCollection: function(callback) {
			var log = this;
			if(this._collection) {
				callback(this._collection);
			} else {
				if(this.getDB()) {
					this.getDB().collection(this._name, function(err, collection) {
						if(!err) {
							this._collection = collection;
							callback(this._collection);
						} else {
							throw {
								func: 'getCollection',
								message: 'collection is null'
							};
						};
					});
				};
			};
		},

		getDB: function() {
			this._db === null ? this._db = def.db : null;
			if(this._db === null) {
				throw {
					func: 'getDB',
					message: 'db is null'
				};
			};
			return this._db;
		},

		create_validate: function(args) {
			// check default & require value
			_.each(this._column, function(column, name) {
				args[name] = column.validate(args[name]);
			});
			// remove all undefined property
			_.each(args, function(value, key) {
				if(value === undefined) {
					delete args[key];
				};
			});
			// return
			return args;
		},

		update_validate: function(selector, document, options) {
			var log = this;
			var self = this;
			var columns = this._column;
			/*
			* fields query
			*/
			// $inc: don't need to check datatype

			// $rename: doesn't support
			if(document.$rename) {
				throw {
					msg: "ekitjs doesn't support $rename"
				};
			};

			// $setOnInsert: use on upsert, check when create

			// $set: don't know how to check datatype yet. It's too complicated. I trust you.

			// $unset: doesn't support
			if(document.$unset) {
				throw {
					msg: "ekitjs doesn't support $unset"
				};
			};

			/*
			* array query
			*/

			// $addToSet: I trust you.

			// $pop: I trust you.

			// $pullAll: I trust you.

			// $pull: I trust you.

			// $pushAll: I trust you.

			// $push: I trust you.

			return true;
		},

		create: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result) {
				callback.call(self, err, result);
			});
			// validate args
			try {
				args[0] = this.create_validate(args[0]);
			} catch(ex) {
				log.log({
					func: 'create',
					args: JSON.stringify(arguments),
					ex: ex
				});
				callback.call(self, ex, null);
				return;
			};
			// execute
			this.getCollection(function(collection) {
				collection.insert.apply(collection, args);
			});
			return;
		},

		read: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			// remove disable _id field
			var fields = {};
			_.each(args, function(value, key, l, opt) {
				if(opt.index === 0) {
					return;
				};
				if(value.fields !== undefined) {
					if(value.fields._id !== undefined) {
						delete args[key].fields._id;
					};
					fields = args[key].fields;
				};
			});
			// find
			this.getCollection(function(collection) {
				collection.find.apply(collection, args).toArray(function(err, data) {
					if(err) {
						return callback.call(self, err, []);
					} else {
						/*
						* init function field (store === false)
						*/
						// init loop injection
						if(self.___loop_injection === true) {
							return callback.call(self, null, data);
						};
						self.___loop_injection = true;

						// callback & reset loop_injection
						var success = function() {
							try {
								self.___loop_injection = false;
								delete self.___loop_injection;
							} catch(ex) {
							};
							callback.call(self, null, data);
						};

						// get ids
						var ids = [];
						var keyData = {};
						_.each(data, function(value) {
							ids.push(value._id);
							keyData[value._id] = value;
						});

						// get hide & show fields
						var isQueryHide = null;
						var show_fields = [];
						var hidden_fields = [];
						_.each(fields, function(value, key) {
							if(value === 0) {
								hidden_fields.push(key);
								isQueryHide = true;
							} else if(value === 1) {
								show_fields.push(key);
								isQueryHide = false;
							}
							;
						});

						// filter function fields in column with hidden fields
						var funcs = {};
						_.each(self._column, function(column, name) {
							switch(column.__type) {
							case 'func':
								funcs[name] = column;
								break;
							case 'object':
								funcs[name] = column.getFuncField();
								break;
							};
						});
						funcs = _.encodeObject(funcs, '__type');
						_.each(funcs, function(value, key) {
							// just keep function with store === false
							if(value.get('store') !== false) {
								delete funcs[key];
								return;
							};
							if(isQueryHide === true) {
								// remove function field in hidden_fields
								_.each(hidden_fields, function(field) {
									if(key.indexOf(field) === 0) {
										delete funcs[key];
									}
								});
							} else if(isQueryHide === false) {
								// remove function field not in show_fields
								var isIn = false;
								_.each(show_fields, function(field) {
									if(key.indexOf(field) === 0) {
										isIn = true;
									};
								});
								if(!isIn) {
									delete funcs[key];
								};
							}
							;
						});

						// sort funcs by sequence
						_.sortObject(funcs, function(a, b) {
							return a.get('sequence') - b.get('sequence');
						});

						// rebuild function fields sequence
						var sequence = [];
						_.each(funcs, function(func, key, l, opt) {
							sequence.push((function(func, key, opt) {
								return function(fcb) {
									func.get('get').call(self, ids, keyData, function(funcData) {
										_.each(funcData, function(fData, _id) {
											var tmp = {};
											if(func.get('multi') === true) {
												tmp[key] = fData[key];
											} else {
												tmp[key] = fData;
											};
											_.setObject(keyData[_id], tmp);
										});
										data = _.values(keyData);
										// callback & reset loop_injection
										if(opt.isLast === true) {
											success();
										};
										fcb();
									});
								};
							})(func, key, opt));
						});

						if(sequence.length === 0) {
							success();
						} else {
							(function(sequence) {
								var args = arguments;
								if(sequence.length === 0) {
									return;
								};
								var func = sequence.shift();
								func(function() {
									args.callee(sequence);
								});
							})(sequence);
						};
					};
				});
			});
		},

		update: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result, extra) {
				if(err) {
					callback.call(self, err, 0, extra);
				} else {
					callback.call(self, null, result, extra);
				};
			});
			// init full args
			if(args.length === 3) {
				args[3] = args[2];
				args[2] = {};
			};
			// normal update
			var func_update = function() {
				// validate args
				try {
					// remove $setOnInsert
					delete args[1].$setOnInsert;
					// start validate
					this.update_validate(args[0], args[1], args[2]);
				} catch(ex) {
					log.log({
						func: 'update',
						args: JSON.stringify(arguments),
						ex: ex
					});
					callback.call(self, ex, 0);
					return;
				};
				// start update
				this.getCollection(function(collection) {
					collection.update.apply(collection, args);
				});
			};

			// check upsert
			var upsert = args[2].upsert;
			delete args[2].upsert;
			if(upsert === true) {
				this.read(args[0], {
					_id: 1
				}, function(err, data) {
					if(data.length > 0) {
						func_update.call(self);
					} else {
						// create using $setOnInsert
						if(args[1].$setOnInsert !== undefined) {
							self.create(args[1].$setOnInsert, function(e, r) {
								if(e) {
									args[3](e, 0);
								} else {
									args[3](null, 1, r);
								};
							});
						} else {
							args[3]({
								msg: 'can not find $setOnInsert'
							}, 0);
						};
					};
				});
			} else {
				func_update.call(self);
			};
			return;
		},

		'delete': function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result) {
				callback.call(self, err, result);
			});
			// delete
			this.getCollection(function(collection) {
				collection.remove.apply(collection, args);
			});
		}
	};

	instance.base.model = instance.base.base.extend(__class);

	// config model static type
	instance.base.model.__type = 'model';
	instance.base.model.__extend = instance.base.model.extend;
	instance.base.model.extend = function() {
		var res = instance.base.model.__extend.apply(instance.base.model, arguments);
		res.__type = instance.base.model.__type;
		return res;
	};
};
<|endoftext|>


<commit_before>"use strict";

module.exports = function (grunt) {
    grunt.registerTask("check", [
        "exec:globals",
        "concurrent:linters",
        "concurrent:quality",
        "exec:metrics",
        "jsdoc:public",
        "connectIf",
        "exec:checkDoc"
    ]);
};
<commit_msg>Linting doc part of the build process (#280)<commit_after>"use strict";

module.exports = function (grunt) {
    grunt.registerTask("check", [
        "exec:globals",
        "concurrent:linters",
        "concurrent:quality",
        "exec:metrics",
        "exec:buildLintingDoc",
        "jsdoc:public",
        "connectIf",
        "exec:checkDoc"
    ]);
};
<|endoftext|>


<commit_before>var render = require('./render'),
  util = require('./util'),
  file = util.file,
  yfm = util.yfm,
  partial = util.partial,
  async = require('async'),
  path = require('path'),
  _ = require('underscore'),
  cache = {};

var config = exports.config = {};

exports.init = function(callback){
  render.compile(hexo.theme_dir + '_config.yml', function(err, file){
    if (err) throw err;

    if (file){
      for (var i in file){
        (function(i){
          config.__defineGetter__(i, function(){
            return file[i];
          });
        })(i);

        callback();
      }
      console.log(file);
    } else {
      callback();
    }
  });
};

exports.assets = function(callback){
  var themeDir = hexo.theme_dir,
    publicDir = hexo.public_dir;

  file.dir(themeDir, function(files){
    async.forEach(files, function(item, next){
      var extname = path.extname(item),
        filename = path.basename(item, extname),
        dirs = item.split('/');

      for (var i=0, len=dirs.length; i<len; i++){
        var front = dirs[i].substring(0, 1);
        if (front === '_' || front === '.'){
          return next(null);
        }
      }

      if (dirs[0] === 'layout'){
        file.read(themeDir + item, function(err, file){
          if (err) throw err;
          cache[filename] = yfm(file);
          cache[filename].source = themeDir + item;
          next(null);
        });
      } else {
        render.compile(themeDir + item, function(err, result, output){
          if (err) throw err;
          file.write(publicDir + item.replace(extname, '.' + output), result, next);
        });
      }
    }, callback);
  })
};

exports.render = function(template, locals, callback){
  if (_.isFunction(locals)){
    callback = locals;
    locals = {};
  }

  var layout = cache[template],
    body = partial.render(layout.source, layout._content, locals);

  if (layout.layout && cache.hasOwnProperty(layout.layout)){
    var parent = cache[layout.layout];

    var newLocals = _.clone(locals);
    newLocals.body = body;

    var content = partial.render(parent.source, parent._content, newLocals);
  } else {
    var content = body;
  }

  callback(null, content);
};<commit_msg>Fixed theme assets copy<commit_after>var render = require('./render'),
  util = require('./util'),
  file = util.file,
  yfm = util.yfm,
  partial = util.partial,
  async = require('async'),
  path = require('path'),
  _ = require('underscore'),
  cache = {};

var config = exports.config = {};

exports.init = function(callback){
  render.compile(hexo.theme_dir + '_config.yml', function(err, file){
    if (err) throw err;

    if (file){
      for (var i in file){
        (function(i){
          config.__defineGetter__(i, function(){
            return file[i];
          });
        })(i);

        callback();
      }
    } else {
      callback();
    }
  });
};

exports.assets = function(callback){
  var themeDir = hexo.theme_dir,
    publicDir = hexo.public_dir;

  file.dir(themeDir, function(files){
    async.forEach(files, function(item, next){
      var extname = path.extname(item),
        filename = path.basename(item, extname),
        dirs = item.split('/');

      for (var i=0, len=dirs.length; i<len; i++){
        var front = dirs[i].substring(0, 1);
        if (front === '_' || front === '.'){
          return next(null);
        }
      }

      if (dirs[0] === 'layout'){
        file.read(themeDir + item, function(err, file){
          if (err) throw err;
          cache[filename] = yfm(file);
          cache[filename].source = themeDir + item;
          next(null);
        });
      } else {
        render.compile(themeDir + item, function(err, result, output){
          if (err) throw err;
          var outputFile = output ? item.replace(extname, '.' + output) : item;
          file.write(publicDir + outputFile, result, next);
        });
      }
    }, callback);
  })
};

exports.render = function(template, locals, callback){
  if (_.isFunction(locals)){
    callback = locals;
    locals = {};
  }

  var layout = cache[template],
    body = partial.render(layout.source, layout._content, locals);

  if (layout.layout && cache.hasOwnProperty(layout.layout)){
    var parent = cache[layout.layout];

    var newLocals = _.clone(locals);
    newLocals.body = body;

    var content = partial.render(parent.source, parent._content, newLocals);
  } else {
    var content = body;
  }

  callback(null, content);
};<|endoftext|>


<commit_before>var asciitree = require('asciitree');
var bitwise = require('../utils/bitwise');

var PADDING = '0'.repeat(8);

function numberToBitstring(number) {
  return (PADDING + number.toString(2)).slice(-8);
}

function unmask(x) {
  return 8 - Math.log2((~x >>> 0) & 0xff) - 1;
}

// NOTE: max number of internal nodes = n - 1 so max full size = 2n - 1
// NOTE: deleting = compressing above node into grandparent
// --> parent.opposite becomes granparent.path

// NOTE: use negative numbers to distinguish node types in static version
// --> internal nodes should use positive numbers because they are used more
// and this will save up some time

// DAFSA transducer etc.
// todo: only compare last part of string

// http://benlynn.blogspot.com/2013/11/crit-bit-tree-micro-optimizations_3.html
// http://benlynn.blogspot.com/2013/11/crit-bit-tree-micro-optimizations_3.html
// https://github.com/blynn/blt/blob/master/blt.c
// https://dotat.at/prog/qp/blog-2015-10-04.html

// TODO: variant starting at i byte
function findCriticalBit(a, b) {
  var i = 0;

  var min = Math.min(a.length, b.length);

  while (i < min) {
    if (a[i] !== b[i]) {
      return [i, bitwise.criticalBit8Mask(a.charCodeAt(i), b.charCodeAt(i))];
    }

    i++;
  }

  return a.length === b.length ? null : [i, bitwise.criticalBit8Mask(b.charCodeAt(0), 0)];
}

function get(key, address) {
  return bitwise.testCriticalBit8(key.charCodeAt(address[0]), address[1]);
}

// NOTE: maybe it is possible to avoid conditions with bitwise magic
function criticalGt(a, b) {
  if (a[0] > b[0])
    return true;

  if (a[0] < b[0])
    return false;

  // TODO: issue here because of the mask?
  if (a[1] > b[1])
    return true;

  return false;
}

function InternalNode(critical) {
  this.critical = critical;
  this.left = null;
  this.right = null;
}

function ExternalNode(key) {
  this.key = key;
}

function CritBitTree() {
  this.root = null;
}

CritBitTree.prototype.has = function(key) {
  if (this.root === null) {
    return false;
  }

  var node = this.root;

  while (true) {
    if (node instanceof ExternalNode) {

      return node.key === key;
    }

    else {
      var bit = get(key, node.critical);

      if (bit === 0) {
        if (!node.left) {
          return false;
        }

        node = node.left;
      }
      else {
        if (!node.right) {
          return false;
        }

        node = node.right;
      }
    }
  }
};

// TODO: case when the item is already in the tree
CritBitTree.prototype.add = function(key) {
  if (this.root === null) {
    this.root = new ExternalNode(key);
    return;
  }

  var node = this.root,
      ancestors = [];

  while (true) {
    if (node instanceof ExternalNode) {
      var critical = findCriticalBit(key, node.key);

      var internal = new InternalNode(critical);

      var left = get(key, critical) === 0;

      if (left) {
        internal.left = new ExternalNode(key);
        internal.right = node;
      }
      else {
        internal.left = node;
        internal.right = new ExternalNode(key);
      }

      // Bubbling up
      var best = -1;

      for (var i = ancestors.length - 1; i >= 0; i--) {
        var [a] = ancestors[i];

        if (criticalGt(a.critical, critical))
          continue;

        best = i;
        break;
      }

      // Need to attach to root
      if (best < 0) {

        this.root = internal;

        // Rewire parent as child?
        if (ancestors.length > 0) {
          var [parent] = ancestors[0];

          if (left)
            internal.right = parent;
          else
            internal.left = parent;
        }
      }

      // No need for rotation
      else if (best === ancestors.length - 1) {

        var [parent, wentRight] = ancestors[best];

        if (wentRight)
          parent.right = internal;
        else
          parent.left = internal;
      }

      // Rotation
      else {
        var [parent, wentRight] = ancestors[best];
        var [child] = ancestors[best + 1];

        if (wentRight) {
          parent.right = internal;
        }
        else {
          parent.left = internal;
        }

        if (left)
          internal.right = child;
        else
          internal.left = child;
      }

      return;
    }

    else {
      var bit = get(key, node.critical);

      if (bit === 0) {
        if (!node.left) {
          node.left = new ExternalNode(key);
          return;
        }

        ancestors.push([node, false]);
        node = node.left;
      }
      else {
        if (!node.right) {
          node.right = new ExternalNode(key);
          return;
        }

        ancestors.push([node, true]);
        node = node.right;
      }
    }
  }
};

// --> parent.opposite becomes granparent.path
CritBitTree.prototype.delete = function(key) {
  if (this.root === null) {
    return;
  }

  var node = this.root,
      parent = null,
      wentRightForParent = false
      grandparent = null,
      wentRightForGranparent = false;

  while (true) {
    if (node instanceof ExternalNode) {

      if (node.key === key) {

        if (parent === null) {
          this.root = null;
        }

        else if (grandparent === null) {
          if (wentRightForParent)
            this.root = parent.left;
          else
            this.root = parent.right;
        }

        else if (wentRightForGranparent) {
          if (wentRightForParent)
            grandparent.right = parent.left;
          else
            grandparent.right = parent.right;
        }
        else {
          if (wentRightForParent)
            grandparent.left = parent.left;
          else
            grandparent.left = parent.right;
        }
      }

      return;
    }

    else {
      var bit = get(key, node.critical);

      if (bit === 0) {
        if (!node.left) {
          node.left = new ExternalNode(key);
          return;
        }

        grandparent = parent;
        parent = node;
        wentRightForGranparent = wentRightForParent;
        wentRightForParent = false;
        node = node.left;
      }
      else {
        if (!node.right) {
          node.right = new ExternalNode(key);
          return;
        }

        grandparent = parent;
        parent = node;
        wentRightForGranparent = wentRightForParent;
        wentRightForParent = true;
        node = node.right;
      }
    }
  }
};

CritBitTree.prototype[Symbol.for('nodejs.util.inspect.custom')] = function() {
  return this.root;
};

function printNode(node) {
  if (!node)
    return '';

  if (node instanceof InternalNode)
    return '(' + node.critical[0] + ',' + unmask(node.critical[1]) + ')';

  return node.key + '•' + Array.from(node.key, k => k.charCodeAt(0)).map(numberToBitstring);
}

function log(tree) {

  const title = printNode;

  const children = node => (node instanceof ExternalNode ? null : [node.left , node.right]);

  console.log(asciitree(tree.root, title, children));
}

module.exports = CritBitTree;

if (require.main === module) {
  var tree = new CritBitTree();

  // tree.add(0);
  // tree.add(1);
  // tree.add(2);
  // tree.add(3);
  // tree.add(4);
  // tree.add(5);
  // tree.add(6);
  // tree.add(7);
  // tree.add(8);
  // tree.add(9);
  // tree.add(10);
  // tree.add(11);
  // tree.add(12);
  // tree.add(13);
  // tree.add(14);
  // tree.add(15);

  tree.add('abcde');
  tree.add('bcd');
  // tree.add('abb');
  // tree.add('abc');
  tree.add('abd');
  tree.add('abdg');
  tree.add('abe');
  tree.add('aba');
  tree.add('abz');

  // tree.delete('bcd');
  // tree.delete('abd');
  // tree.delete('abcde');
  // tree.delete('aba');
  // tree.delete('abdg');
  // tree.delete('abz');
  // tree.delete('abe');

  // tree.add(String.fromCharCode(13));
  // tree.add(String.fromCharCode(10));
  // tree.add(String.fromCharCode(8));
  // tree.add(String.fromCharCode(12));
  // tree.add(String.fromCharCode(1));
  // tree.add(String.fromCharCode(145));
  // tree.add(String.fromCharCode(14));
  // tree.add(String.fromCharCode(9));
  // tree.add(String.fromCharCode(11));
  // tree.add(String.fromCharCode(255));

  // console.log(tree);
  log(tree);

}
<commit_msg>Packing critical address<commit_after>var asciitree = require('asciitree');
var bitwise = require('../utils/bitwise');

var PADDING = '0'.repeat(8);

function numberToBitstring(number) {
  return (PADDING + number.toString(2)).slice(-8);
}

function unmask(x) {
  return 8 - Math.log2((~x >>> 0) & 0xff) - 1;
}

// NOTE: max number of internal nodes = n - 1 so max full size = 2n - 1
// NOTE: deleting = compressing above node into grandparent
// --> parent.opposite becomes granparent.path

// NOTE: use negative numbers to distinguish node types in static version
// --> internal nodes should use positive numbers because they are used more
// and this will save up some time

// DAFSA transducer etc.
// todo: only compare last part of string

// NOTE: packing critical bit (left number must be the largest one)
// (0b1010 << 4) | 0b1001 => 0b10101001
// https://stackoverflow.com/questions/29177035/working-with-binary-in-python-splitting-numbers

// http://benlynn.blogspot.com/2013/11/crit-bit-tree-micro-optimizations_3.html
// https://github.com/blynn/blt/blob/master/blt.c
// https://dotat.at/prog/qp/blog-2015-10-04.html

function packCritical(byte, mask) {
  return (byte << 8) | mask;
}

function unpackByte(x) {
  return x >> 8;
}

function unpackMask(x) {
  return x & 0xff;
}

// TODO: variant starting at i byte
function findCriticalBit(a, b) {
  var i = 0;

  var min = Math.min(a.length, b.length);

  while (i < min) {
    if (a[i] !== b[i]) {
      return packCritical(i, bitwise.criticalBit8Mask(a.charCodeAt(i), b.charCodeAt(i)));
    }

    i++;
  }

  return a.length === b.length ? null : packCritical(i, bitwise.criticalBit8Mask(b.charCodeAt(0), 0));
}

function get(key, critical) {
  return bitwise.testCriticalBit8(key.charCodeAt(unpackByte(critical)), unpackMask(critical));
}

// NOTE: maybe it is possible to avoid conditions with bitwise magic
function criticalGt(a, b) {
  if (a > b)
    return true;

  if (a < b)
    return false;

  // TODO: issue here because of the mask?
  // if (a[1] > b[1])
  //   return true;

  return false;
}

function InternalNode(critical) {
  this.critical = critical
  this.left = null;
  this.right = null;
}

function ExternalNode(key) {
  this.key = key;
}

function CritBitTree() {
  this.root = null;
}

CritBitTree.prototype.has = function(key) {
  if (this.root === null) {
    return false;
  }

  var node = this.root;

  while (true) {
    if (node instanceof ExternalNode) {

      return node.key === key;
    }

    else {
      var bit = get(key, node.critical);

      if (bit === 0) {
        if (!node.left) {
          return false;
        }

        node = node.left;
      }
      else {
        if (!node.right) {
          return false;
        }

        node = node.right;
      }
    }
  }
};

// TODO: case when the item is already in the tree
CritBitTree.prototype.add = function(key) {
  if (this.root === null) {
    this.root = new ExternalNode(key);
    return;
  }

  var node = this.root,
      ancestors = [];

  while (true) {
    if (node instanceof ExternalNode) {
      var critical = findCriticalBit(key, node.key);

      var internal = new InternalNode(critical);

      var left = get(key, critical) === 0;

      if (left) {
        internal.left = new ExternalNode(key);
        internal.right = node;
      }
      else {
        internal.left = node;
        internal.right = new ExternalNode(key);
      }

      // Bubbling up
      var best = -1;

      for (var i = ancestors.length - 1; i >= 0; i--) {
        var [a] = ancestors[i];

        if (criticalGt(a.critical, critical))
          continue;

        best = i;
        break;
      }

      // Need to attach to root
      if (best < 0) {

        this.root = internal;

        // Rewire parent as child?
        if (ancestors.length > 0) {
          var [parent] = ancestors[0];

          if (left)
            internal.right = parent;
          else
            internal.left = parent;
        }
      }

      // No need for rotation
      else if (best === ancestors.length - 1) {

        var [parent, wentRight] = ancestors[best];

        if (wentRight)
          parent.right = internal;
        else
          parent.left = internal;
      }

      // Rotation
      else {
        var [parent, wentRight] = ancestors[best];
        var [child] = ancestors[best + 1];

        if (wentRight) {
          parent.right = internal;
        }
        else {
          parent.left = internal;
        }

        if (left)
          internal.right = child;
        else
          internal.left = child;
      }

      return;
    }

    else {
      var bit = get(key, node.critical);

      if (bit === 0) {
        if (!node.left) {
          node.left = new ExternalNode(key);
          return;
        }

        ancestors.push([node, false]);
        node = node.left;
      }
      else {
        if (!node.right) {
          node.right = new ExternalNode(key);
          return;
        }

        ancestors.push([node, true]);
        node = node.right;
      }
    }
  }
};

// --> parent.opposite becomes granparent.path
CritBitTree.prototype.delete = function(key) {
  if (this.root === null) {
    return;
  }

  var node = this.root,
      parent = null,
      wentRightForParent = false
      grandparent = null,
      wentRightForGranparent = false;

  while (true) {
    if (node instanceof ExternalNode) {

      if (node.key === key) {

        if (parent === null) {
          this.root = null;
        }

        else if (grandparent === null) {
          if (wentRightForParent)
            this.root = parent.left;
          else
            this.root = parent.right;
        }

        else if (wentRightForGranparent) {
          if (wentRightForParent)
            grandparent.right = parent.left;
          else
            grandparent.right = parent.right;
        }
        else {
          if (wentRightForParent)
            grandparent.left = parent.left;
          else
            grandparent.left = parent.right;
        }
      }

      return;
    }

    else {
      var bit = get(key, node.critical);

      if (bit === 0) {
        if (!node.left) {
          node.left = new ExternalNode(key);
          return;
        }

        grandparent = parent;
        parent = node;
        wentRightForGranparent = wentRightForParent;
        wentRightForParent = false;
        node = node.left;
      }
      else {
        if (!node.right) {
          node.right = new ExternalNode(key);
          return;
        }

        grandparent = parent;
        parent = node;
        wentRightForGranparent = wentRightForParent;
        wentRightForParent = true;
        node = node.right;
      }
    }
  }
};

CritBitTree.prototype[Symbol.for('nodejs.util.inspect.custom')] = function() {
  return this.root;
};

function printNode(node) {
  if (!node)
    return '';

  if (node instanceof InternalNode)
    return '(' + unpackByte(node.critical) + ',' + unmask(unpackMask(node.critical)) + ')';

  return node.key + '•' + Array.from(node.key, k => k.charCodeAt(0)).map(numberToBitstring);
}

function log(tree) {

  const title = printNode;

  const children = node => (node instanceof ExternalNode ? null : [node.left , node.right]);

  console.log(asciitree(tree.root, title, children));
}

module.exports = CritBitTree;

if (require.main === module) {
  var tree = new CritBitTree();

  // tree.add(0);
  // tree.add(1);
  // tree.add(2);
  // tree.add(3);
  // tree.add(4);
  // tree.add(5);
  // tree.add(6);
  // tree.add(7);
  // tree.add(8);
  // tree.add(9);
  // tree.add(10);
  // tree.add(11);
  // tree.add(12);
  // tree.add(13);
  // tree.add(14);
  // tree.add(15);

  tree.add('abcde');
  tree.add('bcd');
  // tree.add('abb');
  // tree.add('abc');
  tree.add('abd');
  tree.add('abdg');
  tree.add('abe');
  tree.add('aba');
  tree.add('abz');

  // tree.delete('bcd');
  // tree.delete('abd');
  // tree.delete('abcde');
  // tree.delete('aba');
  // tree.delete('abdg');
  // tree.delete('abz');
  // tree.delete('abe');

  // tree.add(String.fromCharCode(13));
  // tree.add(String.fromCharCode(10));
  // tree.add(String.fromCharCode(8));
  // tree.add(String.fromCharCode(12));
  // tree.add(String.fromCharCode(1));
  // tree.add(String.fromCharCode(145));
  // tree.add(String.fromCharCode(14));
  // tree.add(String.fromCharCode(9));
  // tree.add(String.fromCharCode(11));
  // tree.add(String.fromCharCode(255));

  // console.log(tree);
  log(tree);

  var a = packCritical(45, 154);
  console.log('Address', 45, 154, a);
  console.log('Byte', unpackByte(a));
  console.log('Mask', unpackMask(a));
}
<|endoftext|>


<commit_before>var stroke = (function() {
  function strokeBuilder() {
    var strokes = {};

    utils.each(stroke.definitions, function(key, definition) {
      strokes[key] = strokeFactory(definition);
    });

    return strokes;
  }

  function strokeFactory(definition, name) {
    var Stroke = function(args) {
      if (!(this instanceof Stroke)) {
        return new Stroke([].slice.call(arguments, 0));
      }

      this.args = args, this.name = name;
    };

    Stroke.repeat = function(times) {
      var strokes = [], stroke = new Stroke();
      while (times--) { strokes.push(stroke); }

      return strokes;
    };

    utils.mixin(Stroke.prototype, {
      repeat: function(times) {
        var strokes = [];
        while (times--) { strokes.push(this); }

        return strokes;
      }

    , exec: function($input, o) {
        definition.apply($input, [o].concat(this.args));
      }
    });

    return Stroke;
  }

  return { builder: strokeBuilder, factory: strokeFactory };
})();
<commit_msg>Pass name of stroke to stroke factory.<commit_after>var stroke = (function() {
  function strokeBuilder() {
    var strokes = {};

    utils.each(stroke.definitions, function(key, definition) {
      strokes[key] = strokeFactory(definition, key);
    });

    return strokes;
  }

  function strokeFactory(definition, name) {
    var Stroke = function(args) {
      if (!(this instanceof Stroke)) {
        return new Stroke([].slice.call(arguments, 0));
      }

      this.args = args, this.name = name;
    };

    Stroke.repeat = function(times) {
      var strokes = [], stroke = new Stroke();
      while (times--) { strokes.push(stroke); }

      return strokes;
    };

    utils.mixin(Stroke.prototype, {
      repeat: function(times) {
        var strokes = [];
        while (times--) { strokes.push(this); }

        return strokes;
      }

    , exec: function($input, o) {
        definition.apply($input, [o].concat(this.args));
      }
    });

    return Stroke;
  }

  return { builder: strokeBuilder, factory: strokeFactory };
})();
<|endoftext|>


<commit_before>/**
@desc Popup view for Burnt Chrome
*/
class Popup {
  /**
  @desc Initialize the instance.
  @private
  */
  constructor() {
    // Setup defaults
    /**
    Background page.
    */
    this.background = chrome.extension.getBackgroundPage();
    /**
    Template element ID
    */
    this.templateId = 'frame';

    /**
    Static form data
    */
    this.formData = {}

    /**
    Last error message
    */
    this.lastErrorMessage = "";

    //
    this.setupTemplates();

    // Render initial page
    this.refresh();
  }

  /**
  @desc Setup handlebars views to render compiled templates to.
  @private
  */
  setupTemplates() {
    let handlebarsSelector = 'script[type="text/x-handlebars-template"]';
    $(handlebarsSelector).each((index, $el) => {
      // console.log($el, $el.id);
      var templateId = $el.id;
      if (!templateId) {
        return console.warn("Unknown id for element: ", $el);
      }
      let containerId = templateId.split('-template')[0];
      $($el).after($(`<div id="${containerId}"></div>`));
    });
  }

  /**
  @desc Generate the context to pass to the template to render.
  @private
  */
  getContext() {
    return {
      isLoggedIn: this.background.moderator.loggedIn,
      isLocked: this.background.moderator.isLocked(),
      isUnlocked: this.background.moderator.isUnlocked(),
      whitelist: this.background.moderator.getWhitelistJSON(),
      email: this.background.moderator.getEmail(),
      errorMessage: this.lastErrorMessage
    };
  }

  /**
  @desc Render the template with a context
  @private
  */
  render(id, context) {
    console.log('render', context);
    let el = document.getElementById(`${id}-template`);
    let source = el.innerHTML;
    let template = Handlebars.compile(source);
    let html = template(context);
    document.getElementById(id).innerHTML = html;
    // console.log('html', html);
    // Bind events
    this.bind();
  }

  /**
  @desc Bind the events
  @private
  */
  bind() {
    // console.log('Bind element events');
    $('#lock').click(() => this.lock());
    $('#logout').click(() => this.logout());
    $('#unlock').click(() => this.unlock());
    $('#add-to-whitelist').click(() => this.addToWhitelist());
    $('.delete').click(() => this.removeFromWhitelist(event.target.id));
    $('#loginIntro').click(() => this.loginIntro());
    $('#listIntro').click(() => this.listIntro());
    $('#lockedIntro').click(() => introJs().start());
    $('#export-whitelist').click(() => {
      let whitelistJSON = JSON.stringify(this.background.moderator.getWhitelistJSON());
      let email = this.background.moderator.getEmail();
      let fileName = `Whitelist for ${email}.json`;
      this.downloadData(fileName, whitelistJSON, "application/json");
    });
    $('#import-whitelist-file').change((event) => {
      let $el = $(event.currentTarget);
      // Source: http://stackoverflow.com/a/13747921/2578205
      let file = $el.prop('files')[0];
      let reader = new FileReader();
      reader.onload = () => {
        let data = reader.result;
        let json = JSON.parse(data);
        // Add each entry to current whitelist
        this.importWhitelist(json);
      };
      reader.readAsText(file);
    });
    $('#import-whitelist').click((event) => {
      let url = $('#import-whitelist-url').val();
      if (!url) {
        // Invalid URL
        return this.showError(new Error('Enter a URL to import whitelist from.'));
      } else {
        // Temporarily disable
        this.background.moderator.disable();

        let callback = (error) => {
          if (error) {
            this.showError(error);
          }
          // Re-enable
          this.background.moderator.enable();
        };

        // Source: https://developers.google.com/web/updates/2015/03/introduction-to-fetch?hl=en
        fetch(url)
        .then((response) => {
          if (response.status !== 200) {
            callback(new Error(`An error occurred reading ${url}`));
          } else {
            response.json().then((data) => {
              this.importWhitelist(data);
              callback();
            }).catch(callback);
          }
        })
        .catch(callback);
      }
    });

    // Clickng a whitelist entry fill in entry fields above
    $('.whitelist-entry').click((event) => {
      event.preventDefault();
      // console.log('whitelist-entry click', event, this);
      let $el = $(event.currentTarget);
      let title = $el.data('title');
      let url = $el.data('url');
      $('input[name="entry-title"]').val(title).change();
      $('input[name="entry-url"]').val(url).change();
    });

    // Make sure that input elements do not
    // have their value cleared when template is refreshed.
    // So every change to input, we store the state
    // On refresh, we sync the last state of the input field
    // back into the input field.
    const attr = "name"
      // We store state in formData property
    const formData = this.formData;
    // Iterate over all Input fields that should be syncing their value
    $(`input[${attr}]`).each((idx, el) => {
      let $el = $(el);
      // Get the key to sync with in the formData
      let key = $el.attr(attr);
      // Get current value of input
      let val = _.get(formData, key);
      $el.val(val);
      // console.log($el, attr, key, val);

      // Handle changing input value
      // and storing state
      let changeHandler = (event) => {
          let text = $(event.currentTarget).val();
          // console.log(key, text);
          _.set(formData, key, text);
        }
        // Bind events to listen for changes to input field
      $el.change(changeHandler);
      $el.keyup(changeHandler);
    });

  }

  /**
  @desc Refresh the template
  @private
  */
  refresh() {
    this.render(this.templateId, this.getContext());
  }

  /**
  @desc Lock
  @public
  */
  lock() {
    // console.log('Lock!');
    let context = this.getContext();
    let email = $('input[name="email"]').val() || context.email;
    let password = $('input[name="password"]').val();

    let emailPattern = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
    let emailValid = (emailPattern.test(email) && email !== null);
    let passwordValid = (password !== "" && password !== null);
    $('#emailError').toggleClass("errorshow", !emailValid);
    $('#passwordError').toggleClass("errorshow", !passwordValid);

    if (emailValid && passwordValid) {
      console.log(email, password);
      let successful = this.background.moderator.lock(email, password);
      console.log('successful', successful);
      this.refresh();
      introJs().exit();
      $('#passwordError').toggleClass("errorshow", !successful);
    }
  }

  /**
  @desc Logout
  @public
  */
  logout() {
    // console.log('logout!');
    let successful = this.background.moderator.logout();
    // console.log('successful', successful);
    this.refresh();
    introJs().exit();
  }

  /**
  @desc Unlock
  @public
  */
  unlock() {
    // console.log('Unlock!');
    let context = this.getContext();
    let email = context.email;
    let password = $('input[name="password"]').val();

    let passwordValid = (password !== "" && password !== null);
    $('#passwordError').toggleClass("errorshow", !passwordValid);
    // console.log(email, password);
    if (passwordValid) {
      let successful = this.background.moderator.unlock(email, password);
      this.refresh();
      $('#passwordError').toggleClass("errorshow", !successful);
    }
  }

  /**
  @desc Add to Whitelist
  @public
  */
  addToWhitelist() {
    // console.log('addToWhitelist');
    let title = $('input[name="entry-title"]').val();
    let url = $('input[name="entry-url"]').val();
    try {
      let successful = this.background.moderator.addToWhitelist(title, url);
      if (successful) this.refresh();
    } catch (error) {
      this.showError(error);
    }
  }

  removeFromWhitelist(url) {
    let successful = this.background.moderator.removeFromWhitelist(url);
    if (successful) this.refresh();
  }

  showError(error) {
    let message = error.message;
    this.lastErrorMessage = message
    this.refresh();
    // $('#entryError').text(message).toggleClass("errorshow", !error);
  }

  importWhitelist(whitelist) {
    _.each(whitelist, ({
      title,
      url
    }) => {
      try {
        this.background.moderator.addToWhitelist(title, url);
      } catch (error) {
        this.showError(error);
      }
    });
    this.refresh();
  }

  loginIntro() {
    let intro = introJs();
    intro.setOptions({
      steps: [{
        intro: 'Welcome to Burnt Chrome!
If this is your first time \
        using Burnt Chrome, use these steps to get started!'
      }, {
        element: document.querySelector('input[name="email"]'),
        intro: 'First lets create your account. Please enter a valid \
        email address.',
        position: 'right'
      }, {
        element: document.querySelector('input[name="password"]'),
        intro: 'Next, choose the password you would like to use.',
        position: 'right'
      }, {
        element: document.querySelector('#lock'),
        intro: 'Click \"login and lock\" to login and setup your whitelist!',
        position: 'left'
      }]
    });
    intro.start();
  }

  listIntro() {
    let intro = introJs();
    intro.setOptions({
      steps: [{
        intro: 'Welcome to your whitelist!
Only websites on your list will be available \
          while your browser is locked with Burnt Chrome. Lets get started by adding some entries.'
      }, {
        element: document.querySelector('input[name="entry-title"]'),
        intro: 'First give your entry a title or short description.',
        position: 'right'
      }, {
        element: document.querySelector('input[name="entry-url"]'),
        intro: 'Next, enter the URL you wish to permit.',
        position: 'right'
      }, {
        element: document.querySelector('#add-to-whitelist'),
        intro: 'Click here to add your entry to the whitelist.',
        position: 'right'
      }, {
        element: document.querySelector('#logout'),
        intro: 'When finished logout. Browsing will remain locked \
          to your whitelist until you choose to unlock it.',
        position: 'right'
      }]
    });
    intro.start();
  }

  downloadData(name, data, type) {
    // Browser support
    window.URL = window.URL || window.webkitURL;
    // Arg defaults
    type = type || "text/plain";
    name = name || "download";
    data = data || "";
    // Create Blob
    let blob;
    if (data instanceof Blob) {
      blob = data;
    } else {
      blob = new Blob([data], {
        type: type
      });
    }
    let url = window.URL.createObjectURL(blob)
      // Create link
    let link = document.createElement("a")
    link.download = name
    link.href = url
      // Download!
      // See http://stackoverflow.com/a/25047811/2578205 for more details
    let event = document.createEvent("MouseEvents")
    event.initMouseEvent(
      "click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null
    )
    link.dispatchEvent(event)
  }

};
/**
Ready up!

@ignore
*/
$(document).ready(() => {
  // console.log('Ready');
  let burnt = window.burnt = new Popup();

});<commit_msg>Automatically login when enter key is pressed in password field<commit_after>/**
@desc Popup view for Burnt Chrome
*/
class Popup {
  /**
  @desc Initialize the instance.
  @private
  */
  constructor() {
    // Setup defaults
    /**
    Background page.
    */
    this.background = chrome.extension.getBackgroundPage();
    /**
    Template element ID
    */
    this.templateId = 'frame';

    /**
    Static form data
    */
    this.formData = {}

    /**
    Last error message
    */
    this.lastErrorMessage = "";

    //
    this.setupTemplates();

    // Render initial page
    this.refresh();
  }

  /**
  @desc Setup handlebars views to render compiled templates to.
  @private
  */
  setupTemplates() {
    let handlebarsSelector = 'script[type="text/x-handlebars-template"]';
    $(handlebarsSelector).each((index, $el) => {
      // console.log($el, $el.id);
      var templateId = $el.id;
      if (!templateId) {
        return console.warn("Unknown id for element: ", $el);
      }
      let containerId = templateId.split('-template')[0];
      $($el).after($(`<div id="${containerId}"></div>`));
    });
  }

  /**
  @desc Generate the context to pass to the template to render.
  @private
  */
  getContext() {
    return {
      isLoggedIn: this.background.moderator.loggedIn,
      isLocked: this.background.moderator.isLocked(),
      isUnlocked: this.background.moderator.isUnlocked(),
      whitelist: this.background.moderator.getWhitelistJSON(),
      email: this.background.moderator.getEmail(),
      errorMessage: this.lastErrorMessage
    };
  }

  /**
  @desc Render the template with a context
  @private
  */
  render(id, context) {
    console.log('render', context);
    let el = document.getElementById(`${id}-template`);
    let source = el.innerHTML;
    let template = Handlebars.compile(source);
    let html = template(context);
    document.getElementById(id).innerHTML = html;
    // console.log('html', html);
    // Bind events
    this.bind();
  }

  /**
  @desc Bind the events
  @private
  */
  bind() {
    // console.log('Bind element events');
    $('#lock').click(() => this.lock());
    $('#logout').click(() => this.logout());
    $('#unlock').click(() => this.unlock());
    $('#add-to-whitelist').click(() => this.addToWhitelist());
    $('.delete').click(() => this.removeFromWhitelist(event.target.id));
    $('#loginIntro').click(() => this.loginIntro());
    $('#listIntro').click(() => this.listIntro());
    $('#lockedIntro').click(() => introJs().start());
    $('#export-whitelist').click(() => {
      let whitelistJSON = JSON.stringify(this.background.moderator.getWhitelistJSON());
      let email = this.background.moderator.getEmail();
      let fileName = `Whitelist for ${email}.json`;
      this.downloadData(fileName, whitelistJSON, "application/json");
    });
    $('#import-whitelist-file').change((event) => {
      let $el = $(event.currentTarget);
      // Source: http://stackoverflow.com/a/13747921/2578205
      let file = $el.prop('files')[0];
      let reader = new FileReader();
      reader.onload = () => {
        let data = reader.result;
        let json = JSON.parse(data);
        // Add each entry to current whitelist
        this.importWhitelist(json);
      };
      reader.readAsText(file);
    });
    $('#import-whitelist').click((event) => {
      let url = $('#import-whitelist-url').val();
      if (!url) {
        // Invalid URL
        return this.showError(new Error('Enter a URL to import whitelist from.'));
      } else {
        // Temporarily disable
        this.background.moderator.disable();

        let callback = (error) => {
          if (error) {
            this.showError(error);
          }
          // Re-enable
          this.background.moderator.enable();
        };

        // Source: https://developers.google.com/web/updates/2015/03/introduction-to-fetch?hl=en
        fetch(url)
        .then((response) => {
          if (response.status !== 200) {
            callback(new Error(`An error occurred reading ${url}`));
          } else {
            response.json().then((data) => {
              this.importWhitelist(data);
              callback();
            }).catch(callback);
          }
        })
        .catch(callback);
      }
    });

    // Clicking a whitelist entry fill in entry fields above
    $('.whitelist-entry').click((event) => {
      event.preventDefault();
      // console.log('whitelist-entry click', event, this);
      let $el = $(event.currentTarget);
      let title = $el.data('title');
      let url = $el.data('url');
      $('input[name="entry-title"]').val(title).change();
      $('input[name="entry-url"]').val(url).change();
    });

    // When Enter is pressed while focus is inside of password field
    // Login automatically
    $('input[name="password"]').keyup((event) => {
      if (event.keyCode === 13) {
        this.lock();
      }
    })

    // Make sure that input elements do not
    // have their value cleared when template is refreshed.
    // So every change to input, we store the state
    // On refresh, we sync the last state of the input field
    // back into the input field.
    const attr = "name"
      // We store state in formData property
    const formData = this.formData;
    // Iterate over all Input fields that should be syncing their value
    $(`input[${attr}]`).each((idx, el) => {
      let $el = $(el);
      // Get the key to sync with in the formData
      let key = $el.attr(attr);
      // Get current value of input
      let val = _.get(formData, key);
      $el.val(val);
      // console.log($el, attr, key, val);

      // Handle changing input value
      // and storing state
      let changeHandler = (event) => {
          let text = $(event.currentTarget).val();
          // console.log(key, text);
          _.set(formData, key, text);
        }
        // Bind events to listen for changes to input field
      $el.change(changeHandler);
      $el.keyup(changeHandler);
    });

  }

  /**
  @desc Refresh the template
  @private
  */
  refresh() {
    this.render(this.templateId, this.getContext());
  }

  /**
  @desc Lock
  @public
  */
  lock() {
    // console.log('Lock!');
    let context = this.getContext();
    let email = $('input[name="email"]').val() || context.email;
    let password = $('input[name="password"]').val();

    let emailPattern = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
    let emailValid = (emailPattern.test(email) && email !== null);
    let passwordValid = (password !== "" && password !== null);
    $('#emailError').toggleClass("errorshow", !emailValid);
    $('#passwordError').toggleClass("errorshow", !passwordValid);

    if (emailValid && passwordValid) {
      console.log(email, password);
      let successful = this.background.moderator.lock(email, password);
      console.log('successful', successful);
      this.refresh();
      introJs().exit();
      $('#passwordError').toggleClass("errorshow", !successful);
    }
  }

  /**
  @desc Logout
  @public
  */
  logout() {
    // console.log('logout!');
    let successful = this.background.moderator.logout();
    // console.log('successful', successful);
    this.refresh();
    introJs().exit();
  }

  /**
  @desc Unlock
  @public
  */
  unlock() {
    // console.log('Unlock!');
    let context = this.getContext();
    let email = context.email;
    let password = $('input[name="password"]').val();

    let passwordValid = (password !== "" && password !== null);
    $('#passwordError').toggleClass("errorshow", !passwordValid);
    // console.log(email, password);
    if (passwordValid) {
      let successful = this.background.moderator.unlock(email, password);
      this.refresh();
      $('#passwordError').toggleClass("errorshow", !successful);
    }
  }

  /**
  @desc Add to Whitelist
  @public
  */
  addToWhitelist() {
    // console.log('addToWhitelist');
    let title = $('input[name="entry-title"]').val();
    let url = $('input[name="entry-url"]').val();
    try {
      let successful = this.background.moderator.addToWhitelist(title, url);
      if (successful) this.refresh();
    } catch (error) {
      this.showError(error);
    }
  }

  removeFromWhitelist(url) {
    let successful = this.background.moderator.removeFromWhitelist(url);
    if (successful) this.refresh();
  }

  showError(error) {
    let message = error.message;
    this.lastErrorMessage = message
    this.refresh();
    // $('#entryError').text(message).toggleClass("errorshow", !error);
  }

  importWhitelist(whitelist) {
    _.each(whitelist, ({
      title,
      url
    }) => {
      try {
        this.background.moderator.addToWhitelist(title, url);
      } catch (error) {
        this.showError(error);
      }
    });
    this.refresh();
  }

  loginIntro() {
    let intro = introJs();
    intro.setOptions({
      steps: [{
        intro: 'Welcome to Burnt Chrome!
If this is your first time \
        using Burnt Chrome, use these steps to get started!'
      }, {
        element: document.querySelector('input[name="email"]'),
        intro: 'First lets create your account. Please enter a valid \
        email address.',
        position: 'right'
      }, {
        element: document.querySelector('input[name="password"]'),
        intro: 'Next, choose the password you would like to use.',
        position: 'right'
      }, {
        element: document.querySelector('#lock'),
        intro: 'Click \"login and lock\" to login and setup your whitelist!',
        position: 'left'
      }]
    });
    intro.start();
  }

  listIntro() {
    let intro = introJs();
    intro.setOptions({
      steps: [{
        intro: 'Welcome to your whitelist!
Only websites on your list will be available \
          while your browser is locked with Burnt Chrome. Lets get started by adding some entries.'
      }, {
        element: document.querySelector('input[name="entry-title"]'),
        intro: 'First give your entry a title or short description.',
        position: 'right'
      }, {
        element: document.querySelector('input[name="entry-url"]'),
        intro: 'Next, enter the URL you wish to permit.',
        position: 'right'
      }, {
        element: document.querySelector('#add-to-whitelist'),
        intro: 'Click here to add your entry to the whitelist.',
        position: 'right'
      }, {
        element: document.querySelector('#logout'),
        intro: 'When finished logout. Browsing will remain locked \
          to your whitelist until you choose to unlock it.',
        position: 'right'
      }]
    });
    intro.start();
  }

  downloadData(name, data, type) {
    // Browser support
    window.URL = window.URL || window.webkitURL;
    // Arg defaults
    type = type || "text/plain";
    name = name || "download";
    data = data || "";
    // Create Blob
    let blob;
    if (data instanceof Blob) {
      blob = data;
    } else {
      blob = new Blob([data], {
        type: type
      });
    }
    let url = window.URL.createObjectURL(blob)
      // Create link
    let link = document.createElement("a")
    link.download = name
    link.href = url
      // Download!
      // See http://stackoverflow.com/a/25047811/2578205 for more details
    let event = document.createEvent("MouseEvents")
    event.initMouseEvent(
      "click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null
    )
    link.dispatchEvent(event)
  }

};
/**
Ready up!

@ignore
*/
$(document).ready(() => {
  // console.log('Ready');
  let burnt = window.burnt = new Popup();

});<|endoftext|>


<commit_before>import { sortBy } from "lodash";

const adForRequest = (ad) => ({
  id: ad.id,
  html: ad.html,
  targeting: ad.targeting
});

const endpoint = process.env.NODE_ENV === 'production' ?
  "https://projects.propublica.org/facebook-ads/ads" :
  "http://0.0.0.0:8080/facebook-ads/ads";

const headers = (language) => new Headers({
  "Accept-Language": language.language + "-" + language.country + ";q=1.0"
});

const sendAds = (body, language) => fetch(endpoint, {
  method: "POST",
  mode: 'no-cors',
  headers: headers(language),
  body: JSON.stringify(body)
});

const getAds = (language, cb) => fetch(endpoint, {
  method: "GET",
  headers: headers(language),
}).then((res) => res.json()).then((ads) => cb(ads.ads || ads));

const mergeAds = (ads, newAds) => {
  let ids = new Map(ads.map(ad => [ad.id, ad]));
  newAds.forEach(ad => {
    ad = Object.assign({}, {created_at: (new Date()).valueOf()}, ad);
    if(ids.has(ad.id)) {
      let old = ids.get(ad.id);
      let newAd = Object.assign({}, old, ad);
      ids.set(newAd.id, newAd);
    } else {
      ids.set(ad.id, ad);
    }
  });
  const idSort = sortBy(Array.from(ids.values()), (a) => -1 * Date.parse(a.created_at));
  const poli = sortBy(idSort, (a) => a.rating === "political");
  return poli.slice(0, 100);
};

const updateBadge = (ratings) => {
  const num = ratings.filter(rating => !("rating" in rating)).length;
  if(num > 0) {
    chrome.browserAction.setBadgeText({text: num > 100 ? "100+" : "" + num});
  } else {
    chrome.browserAction.setBadgeText({text: ""});
  }
};

const getBrowserLocale = () => ({
  language: navigator.language.split("-")[0],
  country: navigator.language.split("-")[1]
});

export { sendAds, getAds, mergeAds, updateBadge, adForRequest, getBrowserLocale };
<commit_msg>collect 'em all<commit_after>import { sortBy } from "lodash";

const adForRequest = (ad) => ({
  id: ad.id,
  html: ad.html,
  targeting: ad.targeting
});

const endpoint = process.env.NODE_ENV === 'production' ?
  "https://projects.propublica.org/facebook-ads/ads" :
  "http://0.0.0.0:8080/facebook-ads/ads";

const headers = (language) => new Headers({
  "Accept-Language": language.language + "-" + language.country + ";q=1.0"
});

const sendAds = (body, language) => fetch(endpoint, {
  method: "POST",
  mode: 'no-cors',
  headers: headers(language),
  body: JSON.stringify(body)
});

const getAds = (language, cb) => fetch(endpoint, {
  method: "GET",
  headers: headers(language),
}).then((res) => res.json()).then((ads) => cb(ads.ads || ads));

const mergeAds = (ads, newAds) => {
  let ids = new Map(ads.map(ad => [ad.id, ad]));
  newAds.forEach(ad => {
    ad = Object.assign({}, {created_at: (new Date()).valueOf()}, ad);
    if(ids.has(ad.id)) {
      let old = ids.get(ad.id);
      let newAd = Object.assign({}, old, ad);
      ids.set(newAd.id, newAd);
    } else {
      ids.set(ad.id, ad);
    }
  });
  const idSort = sortBy(Array.from(ids.values()), (a) => -1 * Date.parse(a.created_at));
  return sortBy(idSort, (a) => a.rating === "political");
};

const updateBadge = (ratings) => {
  const num = ratings.filter(rating => !("rating" in rating)).length;
  if(num > 0) {
    chrome.browserAction.setBadgeText({text: num > 100 ? "100+" : "" + num});
  } else {
    chrome.browserAction.setBadgeText({text: ""});
  }
};

const getBrowserLocale = () => ({
  language: navigator.language.split("-")[0],
  country: navigator.language.split("-")[1]
});

export { sendAds, getAds, mergeAds, updateBadge, adForRequest, getBrowserLocale };
<|endoftext|>


<commit_before>/**
 * Browser history managment, add slug name of scenes in URL.
 *
 * @constructor FORGE.History
 * @param {FORGE.Viewer} viewer - The viewer reference.
 * @extends FORGE.BaseObject
 */
FORGE.History = function(viewer)
{
    /**
     * The viewer reference.
     * @name FORGE.History#_viewer
     * @type {FORGE.Viewer}
     * @private
     */
    this._viewer = viewer;

    /**
     * History configuration
     * @name FORGE.History#_config
     * @type {HistoryConfig}
     * @private
     */
    this._config;

    /**
     * The history module enabled flag.
     * @name  FORGE.History#_enabled
     * @type {boolean}
     * @private
     */
    this._enabled = false;

    FORGE.BaseObject.call(this, "History");
};

FORGE.History.prototype = Object.create(FORGE.BaseObject.prototype);
FORGE.History.prototype.constructor = FORGE.History;

/**
 * Default configuration of the History
 * @name FORGE.History.DEFAULT_CONFIG
 * @type {HistoryConfig}
 * @const
 */
FORGE.History.DEFAULT_CONFIG =
{
    enabled: true
};

/**
 * Boot sequence.
 * @method FORGE.History#_parseConfig
 * @param {HistoryConfig} config - History configuration to parse
 * @private
 */
FORGE.History.prototype._parseConfig = function(config)
{
    this._config = /** @type {HistoryConfig} */ (FORGE.Utils.extendSimpleObject(FORGE.History.DEFAULT_CONFIG, config));

    this._enabled = (typeof this._config.enabled === "boolean") ? this._config.enabled : true;

    if (this._enabled === true)
    {
        this._enable();
    }
};

/**
 * Enable the handler needed for History
 * @method FORGE.History#_enable
 * @private
 */
FORGE.History.prototype._enable = function()
{
    this._viewer.story.onSceneLoadStart.add(this._sceneLoadStartHandler, this);
    this._viewer.i18n.onLocaleChangeComplete.add(this._localeChangeCompleteHandler, this);
    window.addEventListener("popstate", this._onPopStateHandler, false);
};

/**
 * Disable the handler needed for History
 * @method FORGE.History#_disable
 * @private
 */
FORGE.History.prototype._disable = function()
{
    this._viewer.story.onSceneLoadStart.remove(this._sceneLoadStartHandler, this);
    this._viewer.i18n.onLocaleChangeComplete.remove(this._localeChangeCompleteHandler, this);
    window.removeEventListener("popstate", this._onPopStateHandler, false);
};

/**
 * Internal handler for scene load start.
 * @method FORGE.History#_sceneLoadStartHandler
 * @private
 */
FORGE.History.prototype._sceneLoadStartHandler = function()
{
    this._addState();
};

/**
 * Internal handler for locale change complete.
 * @method FORGE.History#_localeChangeCompleteHandler
 * @private
 */
FORGE.History.prototype._localeChangeCompleteHandler = function()
{
    this._updateState();
};

/**
 * Add a state into the history.
 * @method  FORGE.History#_addState
 * @private
 */
FORGE.History.prototype._addState = function()
{
    this.log("_addState");

    var scene = this._viewer.story.scene;

    var newState = {
        "viewer":
        {
            uid: this._viewer.uid
        },

        "scene":
        {
            uid: scene.uid
        },

        "locale": this._viewer.i18n.locale
    };

    var currentState = window.history.state;

    if (currentState === null)
    {
        window.history.replaceState(newState, scene.name, this.generateHash(scene));
    }
    else if (currentState.scene.uid !== newState.scene.uid)
    {
        window.history.pushState(newState, scene.name, this.generateHash(scene));
    }
};

/**
 * Update the current state with the correct slug name.
 * @method FORGE.History#_updateState
 * @private
 */
FORGE.History.prototype._updateState = function()
{
    this.log("_updateState");

    var currentState = /** @type {Object} */ (window.history.state);

    if (this._isStateValid(currentState))
    {
        var scene = FORGE.UID.get(currentState.scene.uid);
        currentState.locale = this._viewer.i18n.locale;
        window.history.replaceState(currentState, scene.name, this.generateHash( /** @type {FORGE.Scene} */ (scene)));
    }
};

/**
 * Internal handler for browser pop state event.

 * This event occur when user click on back or next browser buttons.
 * @param  {Event} event - The pop state event.
 * @private
 */
FORGE.History.prototype._onPopStateHandler = function(event)
{
    if (event.state === null)
    {
        return;
    }

    var viewer = FORGE.UID.get(event.state.viewer.uid);

    viewer.history.log("_onPopStateHandler");

    viewer.story.scene = event.state.scene.uid;
};

/**
 * Method to check if a state is valid for the history module.
 * @param  {Object}  state - The state to check.
 * @return {boolean} Returns true if the state is valid, false if not.
 * @private
 */
FORGE.History.prototype._isStateValid = function(state)
{
    if (state === null)
    {
        return false;
    }

    var viewerValid = (typeof state.viewer !== "undefined" && typeof state.viewer.uid === "string");
    var sceneValid = (typeof state.scene !== "undefined" && typeof state.scene.uid === "string");

    return (viewerValid === true && sceneValid === true);
};

/**
 * Generate a hash for the current scene with the i18n slug name and the scene uid.
 * @method FORGE.History#generateHash
 * @param  {FORGE.Scene|Object} scene - The scene for which you want to generate a hash.
 * @param {boolean} [keep=true] - Do we have to keep the existing URL parameters.
 * @return {string} The generated hash.
 */
FORGE.History.prototype.generateHash = function(scene, keep)
{
    // Search for other parameters in URL, beside the UID
    var result = "";

    // Keep others parameters
    if(keep !== false)
    {
        var hash = window.location.hash;

        // result for normal URL querystring
        var re = /[&#]([^&]+)/g;
        var rr;
        while ((rr = re.exec(hash)) !== null)
        {
            if (rr[1].substr(0, 3) !== "uid" && rr[1] !== scene.slug)
            {
                result += "&" + rr[1];
            }
        }
    }

    return "#" + scene.slug + "&uid=" + scene.uid + result;
};

/**
 * Add the history configuration.
 * @method FORGE.History#addConfig
 * @param {HistoryConfig} config - The configuration to add
 */
FORGE.History.prototype.addConfig = function(config)
{
    this._parseConfig(config);
};

/**
 * Destroy sequence.
 * @method FORGE.History#destroy
 */
FORGE.History.prototype.destroy = function()
{
    this._disable();

    this._viewer = null;

    FORGE.BaseObject.prototype.destroy.call(this);
};

/**
 * Get the enbled flag value of the history.
 * @name FORGE.History#enabled
 * @readonly
 * @type {boolean}
 */
Object.defineProperty(FORGE.History.prototype, "enabled",
{
    /** @this {FORGE.History} */
    get: function()
    {
        return this._enabled;
    }
});
<commit_msg>Filter the slug when writing the history.<commit_after>/**
 * Browser history managment, add slug name of scenes in URL.
 *
 * @constructor FORGE.History
 * @param {FORGE.Viewer} viewer - The viewer reference.
 * @extends FORGE.BaseObject
 */
FORGE.History = function(viewer)
{
    /**
     * The viewer reference.
     * @name FORGE.History#_viewer
     * @type {FORGE.Viewer}
     * @private
     */
    this._viewer = viewer;

    /**
     * History configuration
     * @name FORGE.History#_config
     * @type {HistoryConfig}
     * @private
     */
    this._config;

    /**
     * The history module enabled flag.
     * @name  FORGE.History#_enabled
     * @type {boolean}
     * @private
     */
    this._enabled = false;

    FORGE.BaseObject.call(this, "History");
};

FORGE.History.prototype = Object.create(FORGE.BaseObject.prototype);
FORGE.History.prototype.constructor = FORGE.History;

/**
 * Default configuration of the History
 * @name FORGE.History.DEFAULT_CONFIG
 * @type {HistoryConfig}
 * @const
 */
FORGE.History.DEFAULT_CONFIG =
{
    enabled: true
};

/**
 * Boot sequence.
 * @method FORGE.History#_parseConfig
 * @param {HistoryConfig} config - History configuration to parse
 * @private
 */
FORGE.History.prototype._parseConfig = function(config)
{
    this._config = /** @type {HistoryConfig} */ (FORGE.Utils.extendSimpleObject(FORGE.History.DEFAULT_CONFIG, config));

    this._enabled = (typeof this._config.enabled === "boolean") ? this._config.enabled : true;

    if (this._enabled === true)
    {
        this._enable();
    }
};

/**
 * Enable the handler needed for History
 * @method FORGE.History#_enable
 * @private
 */
FORGE.History.prototype._enable = function()
{
    this._viewer.story.onSceneLoadStart.add(this._sceneLoadStartHandler, this);
    this._viewer.i18n.onLocaleChangeComplete.add(this._localeChangeCompleteHandler, this);
    window.addEventListener("popstate", this._onPopStateHandler, false);
};

/**
 * Disable the handler needed for History
 * @method FORGE.History#_disable
 * @private
 */
FORGE.History.prototype._disable = function()
{
    this._viewer.story.onSceneLoadStart.remove(this._sceneLoadStartHandler, this);
    this._viewer.i18n.onLocaleChangeComplete.remove(this._localeChangeCompleteHandler, this);
    window.removeEventListener("popstate", this._onPopStateHandler, false);
};

/**
 * Internal handler for scene load start.
 * @method FORGE.History#_sceneLoadStartHandler
 * @private
 */
FORGE.History.prototype._sceneLoadStartHandler = function()
{
    this._addState();
};

/**
 * Internal handler for locale change complete.
 * @method FORGE.History#_localeChangeCompleteHandler
 * @private
 */
FORGE.History.prototype._localeChangeCompleteHandler = function()
{
    this._updateState();
};

/**
 * Add a state into the history.
 * @method  FORGE.History#_addState
 * @private
 */
FORGE.History.prototype._addState = function()
{
    this.log("_addState");

    var scene = this._viewer.story.scene;

    var newState = {
        "viewer":
        {
            uid: this._viewer.uid
        },

        "scene":
        {
            uid: scene.uid
        },

        "locale": this._viewer.i18n.locale
    };

    var currentState = window.history.state;

    if (currentState === null)
    {
        window.history.replaceState(newState, scene.name, this.generateHash(scene));
    }
    else if (currentState.scene.uid !== newState.scene.uid)
    {
        window.history.pushState(newState, scene.name, this.generateHash(scene));
    }
};

/**
 * Update the current state with the correct slug name.
 * @method FORGE.History#_updateState
 * @private
 */
FORGE.History.prototype._updateState = function()
{
    this.log("_updateState");

    var currentState = /** @type {Object} */ (window.history.state);

    if (this._isStateValid(currentState))
    {
        var scene = FORGE.UID.get(currentState.scene.uid);
        currentState.locale = this._viewer.i18n.locale;
        window.history.replaceState(currentState, scene.name, this.generateHash( /** @type {FORGE.Scene} */ (scene)));
    }
};

/**
 * Internal handler for browser pop state event.

 * This event occur when user click on back or next browser buttons.
 * @param  {Event} event - The pop state event.
 * @private
 */
FORGE.History.prototype._onPopStateHandler = function(event)
{
    if (event.state === null)
    {
        return;
    }

    var viewer = FORGE.UID.get(event.state.viewer.uid);

    viewer.history.log("_onPopStateHandler");

    viewer.story.scene = event.state.scene.uid;
};

/**
 * Method to check if a state is valid for the history module.
 * @param  {Object}  state - The state to check.
 * @return {boolean} Returns true if the state is valid, false if not.
 * @private
 */
FORGE.History.prototype._isStateValid = function(state)
{
    if (state === null)
    {
        return false;
    }

    var viewerValid = (typeof state.viewer !== "undefined" && typeof state.viewer.uid === "string");
    var sceneValid = (typeof state.scene !== "undefined" && typeof state.scene.uid === "string");

    return (viewerValid === true && sceneValid === true);
};

/**
 * Generate a hash for the current scene with the i18n slug name and the scene uid.
 * @method FORGE.History#generateHash
 * @param  {FORGE.Scene|Object} scene - The scene for which you want to generate a hash.
 * @param {boolean} [keep=true] - Do we have to keep the existing URL parameters.
 * @return {string} The generated hash.
 */
FORGE.History.prototype.generateHash = function(scene, keep)
{
    // Search for other parameters in URL, beside the UID
    var result = "";

    // Keep others parameters
    if(keep !== false)
    {
        var hash = window.location.hash;

        // get all slugs
        var slugs = this._viewer.story.scenes.map(function(s)
        {
            return s.slug;
        });

        // result for normal URL querystring
        var re = /[&#]([^&]+)/g;
        var rr;
        while ((rr = re.exec(hash)) !== null)
        {
            if (rr[1].substr(0, 3) !== "uid" && !slugs.includes(rr[1]))
            {
                result += "&" + rr[1];
            }
        }
    }

    return "#" + scene.slug + "&uid=" + scene.uid + result;
};

/**
 * Add the history configuration.
 * @method FORGE.History#addConfig
 * @param {HistoryConfig} config - The configuration to add
 */
FORGE.History.prototype.addConfig = function(config)
{
    this._parseConfig(config);
};

/**
 * Destroy sequence.
 * @method FORGE.History#destroy
 */
FORGE.History.prototype.destroy = function()
{
    this._disable();

    this._viewer = null;

    FORGE.BaseObject.prototype.destroy.call(this);
};

/**
 * Get the enbled flag value of the history.
 * @name FORGE.History#enabled
 * @readonly
 * @type {boolean}
 */
Object.defineProperty(FORGE.History.prototype, "enabled",
{
    /** @this {FORGE.History} */
    get: function()
    {
        return this._enabled;
    }
});
<|endoftext|>