<commit_before>var InstanceConstructor = require("../Instance").Instance;
var Settings            = require("../Settings");
var Property            = require("../Property");
var ErrorCodes          = require("../ErrorCodes");

exports.prepare = function (Model, associations) {
	if (Model.keys.length > 1) {
		Model.hasMany = function () {
			throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Model.hasMany() does not support multiple keys models");
		};
		return;
	}

	Model.hasMany = function () {
		var name;
		var OtherModel = Model;
		var props = null;
		var opts = {};

		for (var i = 0; i < arguments.length; i++) {
			switch (typeof arguments[i]) {
				case "string":
					name = arguments[i];
					break;
				case "function":
					OtherModel = arguments[i];
					break;
				case "object":
					if (props === null) {
						props = arguments[i];
					} else {
						opts = arguments[i];
					}
					break;
			}
		}
		if (props === null) {
			props = {};
		} else {
			for (var k in props) {
				props[k] = Property.normalize(props[k], Model.settings);
			}
		}

		var assocName = opts.name || ucfirst(name);
		var association = {
			name           : name,
			model          : OtherModel || Model,
			props          : props,
			autoFetch      : opts.autoFetch || false,
			autoFetchLimit : opts.autoFetchLimit || 2,
			// I'm not sure the next key is used..
			field          : opts.field || Model.settings.get("properties.association_key").replace("{name}", name),
			mergeTable     : opts.mergeTable || (Model.table + "_" + name),
			mergeId        : opts.mergeId || Model.settings.get("properties.association_key").replace("{name}", Model.table),
			mergeAssocId   : opts.mergeAssocId || Model.settings.get("properties.association_key").replace("{name}", name),
			getAccessor    : opts.getAccessor || ("get" + assocName),
			setAccessor    : opts.setAccessor || ("set" + assocName),
			hasAccessor    : opts.hasAccessor || ("has" + assocName),
			delAccessor    : opts.delAccessor || ("remove" + assocName),
			addAccessor    : opts.addAccessor || ("add" + assocName)
		};
		associations.push(association);

		if (opts.reverse) {
			OtherModel.hasMany(opts.reverse, Model, association.props, {
				reversed       : true,
				mergeTable     : association.mergeTable,
				mergeId        : association.mergeAssocId,
				mergeAssocId   : association.mergeId,
				field          : association.field,
				autoFetch      : association.autoFetch,
				autoFetchLimit : association.autoFetchLimit
			});
		}
		return this;
	};
};

exports.extend = function (Model, Instance, Driver, associations, opts, cb) {
	if (associations.length === 0) {
		return cb();
	}

	var pending = associations.length;
	var extendDone = function extendDone() {
		pending -= 1;

		if (pending === 0) {
			return cb();
		}
	};

	for (var i = 0; i < associations.length; i++) {
		extendInstance(Model, Instance, Driver, associations[i], opts, extendDone);
	}
};

function extendInstance(Model, Instance, Driver, association, opts, cb) {
	if (Model.settings.get("instance.cascadeRemove")) {
		Instance.on("beforeRemove", function () {
			Instance[association.delAccessor]();
		});
	}

	Object.defineProperty(Instance, association.hasAccessor, {
		value: function () {
			var Instances = Array.prototype.slice.apply(arguments);
			var cb = Instances.pop();
			var conditions = {}, options = {};

			options.__merge = {
				from: { table: association.mergeTable, field: association.mergeAssocId },
				to: { table: association.model.table, field: association.model.id },
				where: [ association.mergeTable, {} ]
			};
			options.extra = association.props;
			options.extra_info = {
				table: association.mergeTable,
				id: Instance[Model.id],
				id_prop: association.mergeId,
				assoc_prop: association.mergeAssocId
			};

			options.__merge.where[1][association.mergeId] = Instance[Model.id];

			if (Instances.length) {
				options.__merge.where[1][association.mergeAssocId] = [];

				for (var i = 0; i < Instances.length; i++) {
					options.__merge.where[1][association.mergeAssocId].push(Instances[i][association.model.id]);
				}
			}

			association.model.find(conditions, options, function (err, instances) {
				if (err) {
					return cb(err);
				}
				return cb(null, instances.length == Instances.length);
			});
			return this;
		},
		enumerable: false
	});
	Object.defineProperty(Instance, association.getAccessor, {
		value: function () {
			var options    = {};
			var conditions = null;
			var order      = null;
			var cb         = null;

			for (var i = 0; i < arguments.length; i++) {
				switch (typeof arguments[i]) {
					case "function":
						cb = arguments[i];
						break;
					case "object":
						if (Array.isArray(arguments[i])) {
							order = arguments[i];
							order[0] = [ association.model.table, order[0] ];
						} else {
							if (conditions === null) {
								conditions = arguments[i];
							} else {
								options = arguments[i];
							}
						}
						break;
					case "string":
						if (arguments[i][0] == "-") {
							order = [ [ association.model.table, arguments[i].substr(1) ], "Z" ];
						} else {
							order = [ [ association.model.table, arguments[i] ] ];
						}
						break;
					case "number":
						options.limit = arguments[i];
						break;
				}
			}

			options.__merge = {
				from  : { table: association.mergeTable, field: association.mergeAssocId },
				to    : { table: association.model.table, field: association.model.id },
				where : [ association.mergeTable, {} ]
			};
			options.extra = association.props;
			options.extra_info = {
				table: association.mergeTable,
				id: Instance[Model.id],
				id_prop: association.mergeId,
				assoc_prop: association.mergeAssocId
			};
			if (order !== null) {
				options.order = order;
			}

			if (conditions === null) {
				conditions = {};
			}

			options.__merge.where[1][association.mergeId] = Instance[Model.id];

			if (cb === null) {
				return association.model.find(conditions, options);
			}

			association.model.find(conditions, options, cb);
			return this;
		},
		enumerable: false
	});
	Object.defineProperty(Instance, association.setAccessor, {
		value: function () {
			var Instances = Array.prototype.slice.apply(arguments);
			var cb        = (Instances.length &&
			                 typeof Instances[Instances.length - 1] == "function" ? Instances.pop() : noOperation);

			if (Instances.length === 0) {
				throw ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "No associations defined");
			}

			if (Array.isArray(Instances[0])) {
				Instances = Instances[0];
			}

			Instance[association.delAccessor](function (err) {
				if (err) {
					return cb(err);
				}
				Instances.push(cb);
				Instance[association.addAccessor].apply(Instance, Instances);
			});
			return this;
		},
		enumerable: false
	});
	Object.defineProperty(Instance, association.delAccessor, {
		value: function () {
			var Associations = Array.prototype.slice.apply(arguments);
			var cb = (typeof Associations[Associations.length - 1] == "function" ? Associations.pop() : noOperation);
			var conditions = {};
			var associationIds = [];
			var run = function () {
				if (Associations.length === 0) {
					return Driver.remove(association.mergeTable, conditions, cb);
				}

				for (var i = 0; i < Associations.length; i++) {
					if (Associations[i][association.model.id]) {
						associationIds.push(Associations[i][association.model.id]);
					}
				}

				if (associationIds.length === 0) {
					return cb(null);
				}

				conditions[association.mergeAssocId] = associationIds;

				Driver.remove(association.mergeTable, conditions, cb);
			};
			conditions[association.mergeId] = Instance[Model.id];

			if (this.saved()) {
				run();
			} else {
				this.save(function (err) {
					if (err) {
						return cb(err);
					}

					return run();
				});
			}
			return this;
		},
		enumerable: false
	});
	Object.defineProperty(Instance, association.addAccessor, {
		value: function () {
			var Associations = [];
			var opts = {};
			var cb = noOperation;
			var run = function () {
				var saveNextAssociation = function () {
					if (Associations.length === 0) {
						return cb();
					}

					var Association = Associations.pop();

					Association.save(function (err) {
						if (err) {
							return cb(err);
						}

						var data = {};
						data[association.mergeId] = Instance[Model.id];
						data[association.mergeAssocId] = Association[association.model.id];

						for (var k in opts) {
							data[k] = opts[k];
						}

						Driver.insert(association.mergeTable, data, null, function (err) {
							if (err) {
								return cb(err);
							}

							return saveNextAssociation();
						});
					});
				};

				return saveNextAssociation();
			};

			for (var i = 0; i < arguments.length; i++) {
				switch (typeof arguments[i]) {
					case "function":
						cb = arguments[i];
						break;
					case "object":
						if (Array.isArray(arguments[i])) {
							Associations = Associations.concat(arguments[i]);
						} else if (arguments[i].isInstance) {
							Associations.push(arguments[i]);
						} else {
							opts = arguments[i];
						}
						break;
				}
			}

			if (this.saved()) {
				run();
			} else {
				this.save(function (err) {
					if (err) {
						return cb(err);
					}

					return run();
				});
			}

			return this;
		},
		enumerable: false
	});

	if (!opts.hasOwnProperty("autoFetchLimit") || typeof opts.autoFetchLimit == "undefined") {
		opts.autoFetchLimit = association.autoFetchLimit;
	}

	if (opts.autoFetchLimit === 0 || (!opts.autoFetch && !association.autoFetch)) {
		return cb();
	}

	Instance[association.getAccessor]({}, { autoFetchLimit: opts.autoFetchLimit - 1 }, function (err, Assoc) {
		if (!err) {
			Instance[association.name] = Assoc;
		}

		return cb();
	});
}

function ucfirst(text) {
	return text[0].toUpperCase() + text.substr(1).replace(/_([a-z])/, function (m, l) {
		return l.toUpperCase();
	});
}

function noOperation() {
}
<commit_msg>Adds ability to pass an Array to hasMany.hasAccessor and also not passing any instance to hasAccessor and have it check for any associated item<commit_after>var InstanceConstructor = require("../Instance").Instance;
var Settings            = require("../Settings");
var Property            = require("../Property");
var ErrorCodes          = require("../ErrorCodes");

exports.prepare = function (Model, associations) {
	if (Model.keys.length > 1) {
		Model.hasMany = function () {
			throw ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Model.hasMany() does not support multiple keys models");
		};
		return;
	}

	Model.hasMany = function () {
		var name;
		var OtherModel = Model;
		var props = null;
		var opts = {};

		for (var i = 0; i < arguments.length; i++) {
			switch (typeof arguments[i]) {
				case "string":
					name = arguments[i];
					break;
				case "function":
					OtherModel = arguments[i];
					break;
				case "object":
					if (props === null) {
						props = arguments[i];
					} else {
						opts = arguments[i];
					}
					break;
			}
		}
		if (props === null) {
			props = {};
		} else {
			for (var k in props) {
				props[k] = Property.normalize(props[k], Model.settings);
			}
		}

		var assocName = opts.name || ucfirst(name);
		var association = {
			name           : name,
			model          : OtherModel || Model,
			props          : props,
			autoFetch      : opts.autoFetch || false,
			autoFetchLimit : opts.autoFetchLimit || 2,
			// I'm not sure the next key is used..
			field          : opts.field || Model.settings.get("properties.association_key").replace("{name}", name),
			mergeTable     : opts.mergeTable || (Model.table + "_" + name),
			mergeId        : opts.mergeId || Model.settings.get("properties.association_key").replace("{name}", Model.table),
			mergeAssocId   : opts.mergeAssocId || Model.settings.get("properties.association_key").replace("{name}", name),
			getAccessor    : opts.getAccessor || ("get" + assocName),
			setAccessor    : opts.setAccessor || ("set" + assocName),
			hasAccessor    : opts.hasAccessor || ("has" + assocName),
			delAccessor    : opts.delAccessor || ("remove" + assocName),
			addAccessor    : opts.addAccessor || ("add" + assocName)
		};
		associations.push(association);

		if (opts.reverse) {
			OtherModel.hasMany(opts.reverse, Model, association.props, {
				reversed       : true,
				mergeTable     : association.mergeTable,
				mergeId        : association.mergeAssocId,
				mergeAssocId   : association.mergeId,
				field          : association.field,
				autoFetch      : association.autoFetch,
				autoFetchLimit : association.autoFetchLimit
			});
		}
		return this;
	};
};

exports.extend = function (Model, Instance, Driver, associations, opts, cb) {
	if (associations.length === 0) {
		return cb();
	}

	var pending = associations.length;
	var extendDone = function extendDone() {
		pending -= 1;

		if (pending === 0) {
			return cb();
		}
	};

	for (var i = 0; i < associations.length; i++) {
		extendInstance(Model, Instance, Driver, associations[i], opts, extendDone);
	}
};

function extendInstance(Model, Instance, Driver, association, opts, cb) {
	if (Model.settings.get("instance.cascadeRemove")) {
		Instance.on("beforeRemove", function () {
			Instance[association.delAccessor]();
		});
	}

	Object.defineProperty(Instance, association.hasAccessor, {
		value: function () {
			var Instances = Array.prototype.slice.apply(arguments);
			var cb = Instances.pop();
			var conditions = {}, options = {};

			options.__merge = {
				from: { table: association.mergeTable, field: association.mergeAssocId },
				to: { table: association.model.table, field: association.model.id },
				where: [ association.mergeTable, {} ]
			};
			options.extra = association.props;
			options.extra_info = {
				table: association.mergeTable,
				id: Instance[Model.id],
				id_prop: association.mergeId,
				assoc_prop: association.mergeAssocId
			};

			options.__merge.where[1][association.mergeId] = Instance[Model.id];

			if (Instances.length) {
				if (Array.isArray(Instances[0])) {
					Instances = Instances[0];
				}
				options.__merge.where[1][association.mergeAssocId] = [];

				for (var i = 0; i < Instances.length; i++) {
					options.__merge.where[1][association.mergeAssocId].push(Instances[i][association.model.id]);
				}
			}

			association.model.find(conditions, options, function (err, instances) {
				if (err) {
					return cb(err);
				}
				if (!Instances.length) {
					return cb(null, instances.length > 0);
				}
				return cb(null, instances.length == Instances.length);
			});
			return this;
		},
		enumerable: false
	});
	Object.defineProperty(Instance, association.getAccessor, {
		value: function () {
			var options    = {};
			var conditions = null;
			var order      = null;
			var cb         = null;

			for (var i = 0; i < arguments.length; i++) {
				switch (typeof arguments[i]) {
					case "function":
						cb = arguments[i];
						break;
					case "object":
						if (Array.isArray(arguments[i])) {
							order = arguments[i];
							order[0] = [ association.model.table, order[0] ];
						} else {
							if (conditions === null) {
								conditions = arguments[i];
							} else {
								options = arguments[i];
							}
						}
						break;
					case "string":
						if (arguments[i][0] == "-") {
							order = [ [ association.model.table, arguments[i].substr(1) ], "Z" ];
						} else {
							order = [ [ association.model.table, arguments[i] ] ];
						}
						break;
					case "number":
						options.limit = arguments[i];
						break;
				}
			}

			options.__merge = {
				from  : { table: association.mergeTable, field: association.mergeAssocId },
				to    : { table: association.model.table, field: association.model.id },
				where : [ association.mergeTable, {} ]
			};
			options.extra = association.props;
			options.extra_info = {
				table: association.mergeTable,
				id: Instance[Model.id],
				id_prop: association.mergeId,
				assoc_prop: association.mergeAssocId
			};
			if (order !== null) {
				options.order = order;
			}

			if (conditions === null) {
				conditions = {};
			}

			options.__merge.where[1][association.mergeId] = Instance[Model.id];

			if (cb === null) {
				return association.model.find(conditions, options);
			}

			association.model.find(conditions, options, cb);
			return this;
		},
		enumerable: false
	});
	Object.defineProperty(Instance, association.setAccessor, {
		value: function () {
			var Instances = Array.prototype.slice.apply(arguments);
			var cb        = (Instances.length &&
			                 typeof Instances[Instances.length - 1] == "function" ? Instances.pop() : noOperation);

			if (Instances.length === 0) {
				throw ErrorCodes.generateError(ErrorCodes.PARAM_MISSMATCH, "No associations defined");
			}

			if (Array.isArray(Instances[0])) {
				Instances = Instances[0];
			}

			Instance[association.delAccessor](function (err) {
				if (err) {
					return cb(err);
				}
				Instances.push(cb);
				Instance[association.addAccessor].apply(Instance, Instances);
			});
			return this;
		},
		enumerable: false
	});
	Object.defineProperty(Instance, association.delAccessor, {
		value: function () {
			var Associations = Array.prototype.slice.apply(arguments);
			var cb = (typeof Associations[Associations.length - 1] == "function" ? Associations.pop() : noOperation);
			var conditions = {};
			var associationIds = [];
			var run = function () {
				if (Associations.length === 0) {
					return Driver.remove(association.mergeTable, conditions, cb);
				}

				for (var i = 0; i < Associations.length; i++) {
					if (Associations[i][association.model.id]) {
						associationIds.push(Associations[i][association.model.id]);
					}
				}

				if (associationIds.length === 0) {
					return cb(null);
				}

				conditions[association.mergeAssocId] = associationIds;

				Driver.remove(association.mergeTable, conditions, cb);
			};
			conditions[association.mergeId] = Instance[Model.id];

			if (this.saved()) {
				run();
			} else {
				this.save(function (err) {
					if (err) {
						return cb(err);
					}

					return run();
				});
			}
			return this;
		},
		enumerable: false
	});
	Object.defineProperty(Instance, association.addAccessor, {
		value: function () {
			var Associations = [];
			var opts = {};
			var cb = noOperation;
			var run = function () {
				var saveNextAssociation = function () {
					if (Associations.length === 0) {
						return cb();
					}

					var Association = Associations.pop();

					Association.save(function (err) {
						if (err) {
							return cb(err);
						}

						var data = {};
						data[association.mergeId] = Instance[Model.id];
						data[association.mergeAssocId] = Association[association.model.id];

						for (var k in opts) {
							data[k] = opts[k];
						}

						Driver.insert(association.mergeTable, data, null, function (err) {
							if (err) {
								return cb(err);
							}

							return saveNextAssociation();
						});
					});
				};

				return saveNextAssociation();
			};

			for (var i = 0; i < arguments.length; i++) {
				switch (typeof arguments[i]) {
					case "function":
						cb = arguments[i];
						break;
					case "object":
						if (Array.isArray(arguments[i])) {
							Associations = Associations.concat(arguments[i]);
						} else if (arguments[i].isInstance) {
							Associations.push(arguments[i]);
						} else {
							opts = arguments[i];
						}
						break;
				}
			}

			if (this.saved()) {
				run();
			} else {
				this.save(function (err) {
					if (err) {
						return cb(err);
					}

					return run();
				});
			}

			return this;
		},
		enumerable: false
	});

	if (!opts.hasOwnProperty("autoFetchLimit") || typeof opts.autoFetchLimit == "undefined") {
		opts.autoFetchLimit = association.autoFetchLimit;
	}

	if (opts.autoFetchLimit === 0 || (!opts.autoFetch && !association.autoFetch)) {
		return cb();
	}

	Instance[association.getAccessor]({}, { autoFetchLimit: opts.autoFetchLimit - 1 }, function (err, Assoc) {
		if (!err) {
			Instance[association.name] = Assoc;
		}

		return cb();
	});
}

function ucfirst(text) {
	return text[0].toUpperCase() + text.substr(1).replace(/_([a-z])/, function (m, l) {
		return l.toUpperCase();
	});
}

function noOperation() {
}
<|endoftext|>


<commit_before>var gulp = require('gulp'),
  config = require('../gulpconfig'),
  bowerFiles = require('main-bower-files'),
  cssmin = require('gulp-minify-css'),
  cssconcat = require('gulp-concat-css'),
  jsconcat = require('gulp-concat'),
  uglify = require('gulp-uglify'),
  gulpIf = require('gulp-if'),
  argv = require('yargs').argv,
  production = argv.production;

gulp.task('build:bower', function () {
  var baseDir = production ? config.paths.dist : config.paths.build;

  gulp.src(bowerFiles('**/*.css'))
    .pipe(cssconcat(config.outputs.libs('css')))
    .pipe(gulpIf(production, cssmin({processImport: false})))
    .pipe(gulp.dest(baseDir.css));

  gulp.src(bowerFiles('**/*.js'))
    .pipe(jsconcat(config.outputs.libs('js')))
    .pipe(gulpIf(production, uglify()))
    .pipe(gulp.dest(baseDir.js));
});<commit_msg>Enhancement to bower gulptask.<commit_after>var gulp = require('gulp'),
  config = require('../gulpconfig'),
  bowerFiles = require('main-bower-files'),
  cssmin = require('gulp-minify-css'),
  cssconcat = require('gulp-concat-css'),
  jsconcat = require('gulp-concat'),
  uglify = require('gulp-uglify'),
  gulpIf = require('gulp-if'),
  argv = require('yargs').argv,
  production = argv.production;

gulp.task('build:bower', function () {
  var baseDir = production ? config.paths.dist : config.paths.build,
    fontExtensions = [
      '**/*.eot',
      '**/*.svg',
      '**/*.ttf',
      '**/*.woff',
      '**/*.woff2',
      '**/*.otf'
    ];

  gulp.src(bowerFiles('**/*.css'))
    .pipe(cssconcat(config.outputs.libs('css')))
    .pipe(gulpIf(production, cssmin({processImport: false})))
    .pipe(gulp.dest(baseDir.css));

  gulp.src(bowerFiles('**/*.js'))
    .pipe(jsconcat(config.outputs.libs('js')))
    .pipe(gulpIf(production, uglify()))
    .pipe(gulp.dest(baseDir.js));

  gulp.src(bowerFiles(fontExtensions))
    .pipe(gulp.dest(baseDir.fonts));
});<|endoftext|>


<commit_before>import chai from "chai";
import promised from "chai-as-promised";
import <%= className %> from "../lib/<%= fileName %>";

chai.should();
chai.use(promised);

const handler = (request) => Handler(request, {
  succeed() {},
  fail() {}
});

describe("<%= className %>", function() {
  describe("LaunchRequest", function() {
    it("should say that it was launched", function() {
      const result = handler({ request: { type: "LaunchRequest" } });
      return result.should.eventually.deep.equal({
        version: "1.0",
        sessionAttributes: {},
        response: {
          shouldEndSession: true,
          outputSpeech: { type: "PlainText", text: "Echo launched!" }
        }
      });
    });
  });

  describe("IntentRequest", function() {
    describe("hello intent", function() {
      it("should say hello world", function() {
        const result = Handler({ request: { type: "IntentRequest", intent: { name: "hello" } } }, context);
        return result.should.eventually.deep.equal({
          version: "1.0",
          sessionAttributes: {},
          response: {
            shouldEndSession: true,
            outputSpeech: { type: "PlainText", text: "Hello world" }
          }
        });
      });

      it("should say hello name when provided", function() {
        const result = Handler({ request: { type: "IntentRequest", intent: { name: "hello", slots: { name: { name: "name", value: "echo" } } } } }, context);
        return result.should.eventually.deep.equal({
          version: "1.0",
          sessionAttributes: {},
          response: {
            shouldEndSession: true,
            outputSpeech: { type: "PlainText", text: "Hello world" }
          }
        });
      });
    });
  });
});
<commit_msg>Update skill spec<commit_after>import chai from "chai";
import promised from "chai-as-promised";
import { Response } from "alexa-lambda-skill";
import <%= className %> from "../lib/<%= fileName %>";

chai.should();
chai.use(promised);

describe("<%= className %>", function() {
  describe("launch", function() {
    it("should say that it was launched", function() {
      const result = new <%= className %>().launch();
      return result.should.eventually.be.an.instanceOf(Response);
    });
  });
});
<|endoftext|>


<commit_before>/*#ifndef(UMD)*/
(function () { /* Begin of privacy scope */
    "use strict";
/*#endif*/

    var
        gpfI = gpf.interfaces,
        _escapes = {

            javascript: {
                "\\": "\\\\",
                "\"": "\\\"",
                "\n": "\\n",
                "\r": "\\r",
                "\t": "\\t"
            },

            xml: {
                "&": "&",
                "<": "<",
                ">": ">"
            },

            html: {
                "&": "&",
                "<": "<",
                ">": ">",
                "é": "é",
                "è": "è",
                "ê": "ê",
                "á": "á",
                "à": "à"
            }

        },

        /**
         * Implements ITextStream on top of a string (FIFO read / write)
         *
         * @class StringStream
         * @extend gpf.events.Target
         * @implements gpf.interfaces.ITextStream
         * @private
         */
        StringStream = gpf.define("StringStream", gpf.events.Target, {

            "[Class]": [gpf.$InterfaceImplement(gpf.interfaces.ITextStream)],

            public: {

                /**
                 * @param {String} [string=undefined] string
                 * @constructor
                 */
                constructor: function(string){
                    if (undefined !== string && string.length) {
                        this._buffer = [string];
                    } else {
                        this._buffer = [];
                    }
                },

                //region gpf.interfaces.ITextStream

                /**
                 * @implements gpf.interfaces.ITextStream:read
                 */
                read: function(count, eventsHandler) {
                    var
                        result;
                    if (0 === this._buffer.length) {
                        gpf.defer(gpf.events.fire, 0, this, [
                            gpfI.IReadableStream.EVENT_END_OF_STREAM,
                            eventsHandler
                        ]);
                    } else {
                        result = gpf.stringExtractFromStringArray(this._buffer,
                            count);
                        gpf.defer(gpf.events.fire, 0, this, [
                            gpfI.IReadableStream.EVENT_DATA,
                            {
                                buffer: result
                            },
                            eventsHandler
                        ]);
                    }
                },

                /**
                 * @implements gpf.interfaces.ITextStream:write
                 */
                write: function (buffer, eventsHandler) {
                    gpf.ASSERT(buffer && buffer.length,
                        "Write must contain data");
                    this._buffer.push(buffer);
                    gpf.events.fire.apply(this, [
                        gpfI.IReadableStream.EVENT_READY,
                        eventsHandler
                    ]);
                },

                //endregion

                /**
                 * Consolidate the result string
                 * @return {String}
                 */
                consolidateString: function() {
                    return this._buffer.join("");
                }

            },

            private: {

                /**
                 * @type {String[]}
                 * @private
                 */
                _buffer: []

            }

        });

    gpf.extend(gpf, {

        "[capitalize]": [gpf.$ClassExtension(String)],
        // Declared in base.js

        "[replaceEx]": [gpf.$ClassExtension(String)],

        /**
         * Substitutes all occurrences of the keys found in the replacements
         * object with their values
         *
         * @param {String} that
         * @param {Object} replacements map of strings to search and replace
         * @return {String}
         */
        replaceEx: function (that, replacements) {
            var
                result = that,
                key;
            for (key in replacements) {
                if (replacements.hasOwnProperty(key)) {
                    if (-1 < result.indexOf(key)) {
                        result = result.split(key).join(replacements[key]);
                    }
                }
            }
            return result;
        },

        "[escapeFor]": [gpf.$ClassExtension(String)],

        /**
         * Adapt the string content to be compatible with the provided language
         *
         * @param {String} that
         * @param {String} language
         * @return {String}
         */
        escapeFor: function (that, language) {
            var replacements = _escapes[language];
            if (undefined !== replacements) {
                that = gpf.replaceEx(that, replacements);
                if ("javascript" === language) {
                    that = "\"" + that + "\"";
                    /* TODO: handle UNICODE characters
                     l = t.length;
                     r = [];
                     for( i = 0; i < l ; ++i )
                     {
                     c = t.charCodeAt( i );
                     if( c < 128 )
                     r.push( String.fromCharCode( c ) );
                     else
                     r.push( "&#" + c + ";" );
                     }
                     return r.join("");
                     */
                }
            }
            return that;
        },

        // TODO Should be a static extension as 'that' is not used
        "[stringExtractFromStringArray]": [gpf.$ClassExtension(String,
            "fromStringArray")],

        /**
         * Extract the first characters of a string array
         *
         * @param {Strings[]} strings This array is modified after extraction
         * @param {Number} [size=0] size Number of characters to get, all if 0
         * @returns {string}
         */
        stringExtractFromStringArray: function (strings, size) {
            var
                stringsCount = strings.length,
                result,
                count,
                string,
                len;
            if (!size) {
                // Take the whole content & clear the array
                result = strings.splice(0, stringsCount).join("");
            } else {
                // Check how many string can be included in the result
                count = 0;
                do {
                    string = strings[count];
                    len = string.length;
                    if (len <= size) {
                        ++count;
                        size -= len;
                    } else {
                        break;
                    }
                } while (0 < size && count < stringsCount);
                if (0 === size) {
                    // Simple case (no need to cut the last item)
                    result = strings.splice(0, count).join("");
                } else if (count < stringsCount) {
                    // Last item has to be cut
                    result = [];
                    if (0 < count) {
                        result.push(strings.splice(0, count - 1).join(""));
                    }
                    // Remove first item
                    string = strings.shift();
                    // Add the missing characters
                    result.push(string.substr(0, size));
                    // Put back the remaining characters
                    strings.unshift(string.substr(size));
                    // Consolidate the string
                    result = result.join("");
                } else {
                    // No last item to cut, the whole array fit
                    result = strings.splice(0, stringsCount).join("");
                }
            }
            return result;
        },

        "[stringToStream]": [gpf.$ClassExtension(String, "toStream")],

        /**
         * Converts the string into a stream
         *
         * @param {String} that
         * @return {Object} Implementing gpf.interfaces.ITextStream
         */
        stringToStream: function (that) {
            return new StringStream(that);
        },

        // TODO Should be a static extension as 'that' is not used
        "[stringFromStream]": [gpf.$ClassExtension(String, "fromStream")],

        /**
         * Converts the stream into a string
         *
         * @param {gpf.interfaces.ITextStream} stream
         * @param {gpf.events.Handler} eventsHandler
         *
         * @event data finished reading the stream, the buffer is provided
         * @eventParam {String} buffer
         *
         */
        stringFromStream: function (stream, eventsHandler) {
            if (stream instanceof StringStream) {
                gpf.events.fire.apply(this, [
                    gpfI.IReadableStream.EVENT_DATA,
                    {
                        buffer: stream.consolidateString()
                    },
                    eventsHandler
                ]);
            } else {
                gpf.stream.readAll(stream, _stringStreamConcat, eventsHandler);
            }
        }

    });

    function _stringStreamConcat(previous, buffer) {
        if (undefined === previous) {
            return [buffer];
        } else if (undefined !== buffer) {
            previous.push(buffer);
            return previous;
        } else {
            return previous.join("");
        }
    }

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/<commit_msg>Inserted UNICODE equivalent chars<commit_after>/*#ifndef(UMD)*/
(function () { /* Begin of privacy scope */
    "use strict";
/*#endif*/

    var
        gpfI = gpf.interfaces,
        _escapes = {

            javascript: {
                "\\": "\\\\",
                "\"": "\\\"",
                "\n": "\\n",
                "\r": "\\r",
                "\t": "\\t"
            },

            xml: {
                "&": "&",
                "<": "<",
                ">": ">"
            },

            html: {
                "&": "&",
                "<": "<",
                ">": ">",
                "\u00E9": "é",
                "\u00E8": "è",
                "\u00EA": "ê",
                "\u00E1": "á",
                "\u00E0": "à"
            }

        },

        /**
         * Implements ITextStream on top of a string (FIFO read / write)
         *
         * @class StringStream
         * @extend gpf.events.Target
         * @implements gpf.interfaces.ITextStream
         * @private
         */
        StringStream = gpf.define("StringStream", gpf.events.Target, {

            "[Class]": [gpf.$InterfaceImplement(gpf.interfaces.ITextStream)],

            public: {

                /**
                 * @param {String} [string=undefined] string
                 * @constructor
                 */
                constructor: function(string){
                    if (undefined !== string && string.length) {
                        this._buffer = [string];
                    } else {
                        this._buffer = [];
                    }
                },

                //region gpf.interfaces.ITextStream

                /**
                 * @implements gpf.interfaces.ITextStream:read
                 */
                read: function(count, eventsHandler) {
                    var
                        result;
                    if (0 === this._buffer.length) {
                        gpf.defer(gpf.events.fire, 0, this, [
                            gpfI.IReadableStream.EVENT_END_OF_STREAM,
                            eventsHandler
                        ]);
                    } else {
                        result = gpf.stringExtractFromStringArray(this._buffer,
                            count);
                        gpf.defer(gpf.events.fire, 0, this, [
                            gpfI.IReadableStream.EVENT_DATA,
                            {
                                buffer: result
                            },
                            eventsHandler
                        ]);
                    }
                },

                /**
                 * @implements gpf.interfaces.ITextStream:write
                 */
                write: function (buffer, eventsHandler) {
                    gpf.ASSERT(buffer && buffer.length,
                        "Write must contain data");
                    this._buffer.push(buffer);
                    gpf.events.fire.apply(this, [
                        gpfI.IReadableStream.EVENT_READY,
                        eventsHandler
                    ]);
                },

                //endregion

                /**
                 * Consolidate the result string
                 * @return {String}
                 */
                consolidateString: function() {
                    return this._buffer.join("");
                }

            },

            private: {

                /**
                 * @type {String[]}
                 * @private
                 */
                _buffer: []

            }

        });

    gpf.extend(gpf, {

        "[capitalize]": [gpf.$ClassExtension(String)],
        // Declared in base.js

        "[replaceEx]": [gpf.$ClassExtension(String)],

        /**
         * Substitutes all occurrences of the keys found in the replacements
         * object with their values
         *
         * @param {String} that
         * @param {Object} replacements map of strings to search and replace
         * @return {String}
         */
        replaceEx: function (that, replacements) {
            var
                result = that,
                key;
            for (key in replacements) {
                if (replacements.hasOwnProperty(key)) {
                    if (-1 < result.indexOf(key)) {
                        result = result.split(key).join(replacements[key]);
                    }
                }
            }
            return result;
        },

        "[escapeFor]": [gpf.$ClassExtension(String)],

        /**
         * Adapt the string content to be compatible with the provided language
         *
         * @param {String} that
         * @param {String} language
         * @return {String}
         */
        escapeFor: function (that, language) {
            var replacements = _escapes[language];
            if (undefined !== replacements) {
                that = gpf.replaceEx(that, replacements);
                if ("javascript" === language) {
                    that = "\"" + that + "\"";
                    /* TODO: handle UNICODE characters
                     l = t.length;
                     r = [];
                     for( i = 0; i < l ; ++i )
                     {
                     c = t.charCodeAt( i );
                     if( c < 128 )
                     r.push( String.fromCharCode( c ) );
                     else
                     r.push( "&#" + c + ";" );
                     }
                     return r.join("");
                     */
                }
            }
            return that;
        },

        // TODO Should be a static extension as 'that' is not used
        "[stringExtractFromStringArray]": [gpf.$ClassExtension(String,
            "fromStringArray")],

        /**
         * Extract the first characters of a string array
         *
         * @param {Strings[]} strings This array is modified after extraction
         * @param {Number} [size=0] size Number of characters to get, all if 0
         * @returns {string}
         */
        stringExtractFromStringArray: function (strings, size) {
            var
                stringsCount = strings.length,
                result,
                count,
                string,
                len;
            if (!size) {
                // Take the whole content & clear the array
                result = strings.splice(0, stringsCount).join("");
            } else {
                // Check how many string can be included in the result
                count = 0;
                do {
                    string = strings[count];
                    len = string.length;
                    if (len <= size) {
                        ++count;
                        size -= len;
                    } else {
                        break;
                    }
                } while (0 < size && count < stringsCount);
                if (0 === size) {
                    // Simple case (no need to cut the last item)
                    result = strings.splice(0, count).join("");
                } else if (count < stringsCount) {
                    // Last item has to be cut
                    result = [];
                    if (0 < count) {
                        result.push(strings.splice(0, count - 1).join(""));
                    }
                    // Remove first item
                    string = strings.shift();
                    // Add the missing characters
                    result.push(string.substr(0, size));
                    // Put back the remaining characters
                    strings.unshift(string.substr(size));
                    // Consolidate the string
                    result = result.join("");
                } else {
                    // No last item to cut, the whole array fit
                    result = strings.splice(0, stringsCount).join("");
                }
            }
            return result;
        },

        "[stringToStream]": [gpf.$ClassExtension(String, "toStream")],

        /**
         * Converts the string into a stream
         *
         * @param {String} that
         * @return {Object} Implementing gpf.interfaces.ITextStream
         */
        stringToStream: function (that) {
            return new StringStream(that);
        },

        // TODO Should be a static extension as 'that' is not used
        "[stringFromStream]": [gpf.$ClassExtension(String, "fromStream")],

        /**
         * Converts the stream into a string
         *
         * @param {gpf.interfaces.ITextStream} stream
         * @param {gpf.events.Handler} eventsHandler
         *
         * @event data finished reading the stream, the buffer is provided
         * @eventParam {String} buffer
         *
         */
        stringFromStream: function (stream, eventsHandler) {
            if (stream instanceof StringStream) {
                gpf.events.fire.apply(this, [
                    gpfI.IReadableStream.EVENT_DATA,
                    {
                        buffer: stream.consolidateString()
                    },
                    eventsHandler
                ]);
            } else {
                gpf.stream.readAll(stream, _stringStreamConcat, eventsHandler);
            }
        }

    });

    function _stringStreamConcat(previous, buffer) {
        if (undefined === previous) {
            return [buffer];
        } else if (undefined !== buffer) {
            previous.push(buffer);
            return previous;
        } else {
            return previous.join("");
        }
    }

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/<|endoftext|>


<commit_before>// Inspired by the Python struct class but not an attempt to duplicate it.
//
// You can give it a format string and it will attempt to parse a stream
// of bytes or into numbers of different sizes, or format a stream of bytes
// from a set of values, corresponding to the format string.
// Usage:
// format strings define the layout of a stream of values in an array, assumed
// to be an array of byte values; the array must be indexable.
// Format strings consist of fields; a field is a numeric size followed by a
// non-numeric character indicating the type. Whitespace between fields is ignored.
// The size parameter has 2 meanings -- for numeric values, it's the number of repetitions of
// this field. For strings, it's the storage length of the string.
// The legal type characters are:
// b -- a 1-byte unsigned value
// s -- a 2-byte unsigned short in little-endian format (0x01 0x00 is returned as 1, not 256)
// S -- a 2-byte unsigned short in big-endian format (0x01 0x00 is returned as 256, not 1)
// i -- a 4-byte unsigned integer in little-endian format
// I -- a 4-byte unsigned integer in big-endian format
// n -- a 4-byte signed integer in little-endian format
// N -- a 4-byte signed integer in big-endian format
// h -- a 2-byte signed integer in little-endian format
// H -- a 2-byte signed integer in big-endian format
// z -- a zero-terminated string of maximum length controlled by the size parameter.
// Z -- a string of bytes with the length controlled by the size parameter.
// . -- the appropriate number of bytes is ignored (used for padding)
//
// To put bytes into a structure, use pack as follows:
// len = pack(buf, offset, format, value...)
// for example:
// pack(buf, 0, "bbsi", 1, 2, 3, 4) would yield: 01 02 03 00 04 00 00 00 and return 8.
//
// To pull data back out, give unpack the format string and a list of parameter names.
// unpack(buf, 0, "2bsi", ["a", "b", "c", "d"]) will give you { a: 1, b: 2, c: 3, d: 4 }
// unpack can pack into an existing object if you pass it as the last argument. Otherwise,
// it will create one.

structTools = function() {
    var extractString = function(bytes, start, len) {
        if (!len) {
            len = bytes.length;
        }
        var s = '';
        for (var i=start; i<start + len; ++i) {
            s += String.fromCharCode(bytes[i]);
        }
        return s;
    };
    // extract a null-terminated string of at most len bytes
    var extractZString = function(bytes, start, len) {
        if (!len) {
            len = bytes.length;
        }
        var s = '';
        for (var i=start; i<start + len; ++i) {
            if (bytes[i]) {
                s += String.fromCharCode(bytes[i]);
            } else {
                break;
            }
        }
        return s;
    };
    var extractInt = function(b, st) {
        // because js always does bit shift on 32-bit values and treats them
        // as signed, we have to fake it out by doing a multiply instead of
        // a shift on the largest value.
        return ((16777216 * b[st+3]) + (b[st+2] << 16) + (b[st+1] << 8) + b[st]);
    };
    var extractSignedInt = function(b, st) {
        return ((b[st+3] << 24) + (b[st+2] << 16) + (b[st+1] << 8) + b[st]);
    };
    var extractShort = function(b, st) {
        return ((b[st+1] << 8) + b[st]);
    };
    var extractSignedShort = function(b, st) {
        var sign = 1;
        if (b[st+1] & 0x80) {
            sign = -1;
        }
        return sign * (((b[st+1] & 0x7F) << 8) + b[st]);
    };
    var extractByte = function(b, st) {
        return b[st];
    };
    // get a big-endian int
    var extractBEInt = function(b, st) {
        // because js always does bit shift on 32-bit values and treats them
        // as signed, we have to fake it out by doing a multiply instead of
        // a shift on the largest value.
        return ((16777216 * b[st]) + (b[st+1] << 16) + (b[st+2] << 8) + b[st+3]);
    };
    var extractSignedBEInt = function(b, st) {
        return ((b[st] << 24) + (b[st+1] << 16) + (b[st+2] << 8) + b[st+3]);
    };
    // get a big-endian short
    var extractBEShort = function(b, st) {
        return ((b[st] << 8) + b[st+1]);
    };
    var extractSignedBEShort = function(b, st) {
        var sign = 1;
        if (b[st] & 0x80) {
            sign = -1;
        }
        return sign * (((b[st] & 0x7F) << 8) + b[st+1]);
    };
    var extractNothing = function() {
        return 0;
    };
    var storeInt = function(v, b, st) {
        b[st] = v & 0xFF;
        b[st + 1] = (v >> 8) & 0xFF;
        b[st + 2] = (v >> 16) & 0xFF;
        b[st + 3] = (v >> 24) & 0xFF;
    };
    var storeBEInt = function(v, b, st) {
        b[st + 3] = v & 0xFF;
        b[st + 2] = (v >> 8) & 0xFF;
        b[st + 1] = (v >> 16) & 0xFF;
        b[st] = (v >> 24) & 0xFF;
    };
    var storeShort = function(v, b, st) {
        b[st] = v & 0xFF;
        b[st + 1] = (v >> 8) & 0xFF;
    };
    var storeBEShort = function(v, b, st) {
        b[st + 1] = v & 0xFF;
        b[st] = (v >> 8) & 0xFF;
    };
    var storeByte = function(v, b, st) {
        b[st] = v & 0xFF;
    };
    var storeString = function(v, b, st) {
        var i=0;
        while (i<v.length) {
            b[st + i] = v[i];
            ++i;
        }
        b[st + i] = 0;
    };
    var storeNothing = function() {
    };

    var fields = {
        'i': { len: 4, put: storeInt, get: extractInt },
        'n': { len: 4, put: storeInt, get: extractSignedInt },
        's': { len: 2, put: storeShort, get: extractShort },
        'h': { len: 2, put: storeShort, get: extractSignedShort },
        'b': { len: 1, put: storeByte, get: extractByte },
        'I': { len: 4, put: storeBEInt, get: extractBEInt },
        'N': { len: 4, put: storeBEInt, get: extractSignedBEInt },
        'S': { len: 2, put: storeBEShort, get: extractBEShort },
        'H': { len: 2, put: storeBEShort, get: extractSignedBEShort },
        'z': { len: 0, put: storeString, get: extractZString },
        'Z': { len: 0, put: storeString, get: extractString },
    };

    var parseformat = function(format, names) {
        var formats = [];
        if (names == null)
            names = [];
        var nameindex = 0;
        var pat = / *([0-9]*)([a-zA-Z.]) */g;
        var a;
        var offset = 0;
        while ((a = pat.exec(format)) !== null) {
            var fmt = {};
            var count = 1;
            if (a[1]) {
                count = parseInt(a[1], 10);
            }
            var f = a[2];
            // we treat Z and z specially -- the length field is their
            // maximum length
            if (f === 'z' || f === 'Z') {
                fmt.len = count;
                fmt.offset = offset;
                offset += fmt.len;
                fmt.get = fields[f].get;
                fmt.put = fields[f].put;
                if (names[nameindex]) {
                    fmt.name = names[nameindex++];
                } else {
                    fmt.name = '_field_' + nameindex++;
                }
                formats.push(fmt);
            }
            else if (fields[f]) {
                for (var j=0; j<count; ++j) {
                    fmt.len = fields[f].len;
                    fmt.offset = offset;
                    offset += fmt.len;
                    fmt.get = fields[f].get;
                    fmt.put = fields[f].put;
                    if (names[nameindex]) {
                        fmt.name = names[nameindex++];
                    } else {
                        fmt.name = '_field_' + nameindex++;
                    }
                    formats.push(_.clone(fmt));
                }
            } else {
                fmt.len = count;
                fmt.offset = offset;
                offset += fmt.len;
                fmt.get = extractNothing;
                fmt.put = storeNothing;
                fmt.name = null;
                formats.push(fmt);
            }
        }
        return formats;

    };

    var structlen = function(s) {
        var fmts = parseformat(s);
        var t = 0;
        for (var i = 0; i < fmts.length; ++i) {
            t += fmts[i].len;
        }
        return t;
    };

    var pack = function(buf, offset, format) {
        var fmts = parseformat(format);

        for (var i=0; i < fmts.length; ++i) {
            fmts[i].put(arguments[i+3], buf, offset + fmts[i].offset, fmts[i].len);
        }
        // inefficient but probably doesn't ever matter
        return structlen(format);
    };

    // This is if you prefer to build things with a chaining api
    var createUnpacker = function() {
        return {
            format: '',
            names: [],
            add: function(fmts, nms) {
                this.format += fmts;
                this.names = this.names.concat(nms);
            },
            go: function(buf, offset, o) {
                return unpack(buf, offset, this.format, this.names, o);
            }
        };
    };

    // buf is an indexable array of bytes
    // offset is the starting offset within buf to unpack the structure
    // format is a format string
    // names is an array of names (within the result object) to store the
    // unpacked data; if a name is '_' the field is not stored.
    // o is the result object -- if it does not exist, it will be created
    // returns (and potentially modifies o)
    var unpack = function(buf, offset, format, names, o) {
        var result;
        if (o != null) {
            result = o;
        } else {
            result = {};
        }

        var fmts = parseformat(format, names);

        for (var i=0; i < fmts.length; ++i) {
            var value = fmts[i].get(buf, offset + fmts[i].offset, fmts[i].len);
            if (fmts[i].name != null) {
                result[fmts[i].name] = value;
            }
        }
        // result.formats = fmts;
        return result;
    };

    var copyBytes = function(buf, offset, bytes, bytecount) {
        for (var i = 0; i < bytecount; ++i) {
            buf[offset + i] = bytes[i];
        }
        return bytecount;
    };

    var test = function() {
        var l = structlen('b6.Si');
        console.log(l);
        var p = parseformat('2ibsi4.b32z', 'abcd');
        console.log(p);
        var buf = new Uint8Array(32);
        var len = pack(buf, 0, 'iI2bsSb', 254, 65534, 55, 66, 1023, 256, 7);
        console.log(len);
        console.log(buf);
        var result = unpack(buf, 0, 'iI2bsS.', ['a', 'b', 'c', 'd', 'e', 'f']);
        console.log(result);
        buf[0] = 0xff;
        buf[1] = 0xff;
        buf[2] = 0xff;
        buf[3] = 0xff;
        result = unpack(buf, 0, 'i', ['x']);
        console.log(result);
        var v = [0, 30, 0, 0, 0, 1, 0, 7, 0, 0, 0, 1, 0, 7, 73, 110, 115, 117, 108, 101, 116, 0, 79, 109, 110, 105, 80, 111, 100, 0, 5, 170];
        for (var i = 0; i < v.length; ++i) {
            buf[i] = v[i];
        }
        result = unpack(buf, 0, '7S8z8zS', ['l', 'b', 'c', 'd', 'e', 'f', 'g', 's1', 's2', 'ck']);
        len = structlen('7S8z8zS');
        console.log(v.length, len);
        console.log(result);
    };

    return {
        extractString: extractString,
        extractZString: extractZString,
        extractInt: extractInt,
        extractShort: extractShort,
        extractByte: extractByte,
        extractBEInt: extractBEInt,
        extractBEShort: extractBEShort,
        storeInt: storeInt,
        storeBEInt: storeBEInt,
        storeShort: storeShort,
        storeBEShort: storeBEShort,
        storeByte: storeByte,
        storeString: storeString,
        pack: pack,
        createUnpacker: createUnpacker,
        unpack: unpack,
        structlen: structlen,
        copyBytes: copyBytes,
        test: test
    };
};

<commit_msg>Fix signed short code; add array storage to unpack<commit_after>// Inspired by the Python struct class but not an attempt to duplicate it.
//
// You can give it a format string and it will attempt to parse a stream
// of bytes or into numbers of different sizes, or format a stream of bytes
// from a set of values, corresponding to the format string.
// Usage:
// format strings define the layout of a stream of values in an array, assumed
// to be an array of byte values; the array must be indexable.
// Format strings consist of fields; a field is a numeric size followed by a
// non-numeric character indicating the type. Whitespace between fields is ignored.
// The size parameter has 2 meanings -- for numeric values, it's the number of repetitions of
// this field. For strings, it's the storage length of the string.
// The legal type characters are:
// b -- a 1-byte unsigned value
// s -- a 2-byte unsigned short in little-endian format (0x01 0x00 is returned as 1, not 256)
// S -- a 2-byte unsigned short in big-endian format (0x01 0x00 is returned as 256, not 1)
// i -- a 4-byte unsigned integer in little-endian format
// I -- a 4-byte unsigned integer in big-endian format
// n -- a 4-byte signed integer in little-endian format
// N -- a 4-byte signed integer in big-endian format
// h -- a 2-byte signed integer in little-endian format
// H -- a 2-byte signed integer in big-endian format
// z -- a zero-terminated string of maximum length controlled by the size parameter.
// Z -- a string of bytes with the length controlled by the size parameter.
// . -- the appropriate number of bytes is ignored (used for padding)
//
// To put bytes into a structure, use pack as follows:
// len = pack(buf, offset, format, value...)
// for example:
// pack(buf, 0, "bbsi", 1, 2, 3, 4) would yield: 01 02 03 00 04 00 00 00 and return 8.
//
// To pull data back out, give unpack the format string and a list of parameter names.
// unpack(buf, 0, "2bsi", ["a", "b", "c", "d"]) will give you { a: 1, b: 2, c: 3, d: 4 }
// unpack can pack into an existing object if you pass it as the last argument. Otherwise,
// it will create one.

structTools = function() {
    var extractString = function(bytes, start, len) {
        if (!len) {
            len = bytes.length;
        }
        var s = '';
        for (var i=start; i<start + len; ++i) {
            s += String.fromCharCode(bytes[i]);
        }
        return s;
    };
    // extract a null-terminated string of at most len bytes
    var extractZString = function(bytes, start, len) {
        if (!len) {
            len = bytes.length;
        }
        var s = '';
        for (var i=start; i<start + len; ++i) {
            if (bytes[i]) {
                s += String.fromCharCode(bytes[i]);
            } else {
                break;
            }
        }
        return s;
    };
    var extractInt = function(b, st) {
        // because js always does bit shift on 32-bit values and treats them
        // as signed, we have to fake it out by doing a multiply instead of
        // a shift on the largest value.
        return ((16777216 * b[st+3]) + (b[st+2] << 16) + (b[st+1] << 8) + b[st]);
    };
    var extractSignedInt = function(b, st) {
        return ((b[st+3] << 24) + (b[st+2] << 16) + (b[st+1] << 8) + b[st]);
    };
    var extractShort = function(b, st) {
        return ((b[st+1] << 8) + b[st]);
    };
    var extractSignedShort = function(b, st) {
        var s = extractShort(b, st);
        if (s & 0x8000) {
            // it's a negative number, so do a bitwise negation to get
            // the positive equivalent, and then flip the sign.
            s = -((~s & 0xffff) + 1);
        }
        return s;
    };
    var extractByte = function(b, st) {
        return b[st];
    };
    // get a big-endian int
    var extractBEInt = function(b, st) {
        // because js always does bit shift on 32-bit values and treats them
        // as signed, we have to fake it out by doing a multiply instead of
        // a shift on the largest value.
        return ((16777216 * b[st]) + (b[st+1] << 16) + (b[st+2] << 8) + b[st+3]);
    };
    var extractSignedBEInt = function(b, st) {
        return ((b[st] << 24) + (b[st+1] << 16) + (b[st+2] << 8) + b[st+3]);
    };
    // get a big-endian short
    var extractBEShort = function(b, st) {
        return ((b[st] << 8) + b[st+1]);
    };
    var extractSignedBEShort = function(b, st) {
        var s = extractBEShort(b, st);
        if (s & 0x8000) {
            // it's a negative number, so do a bitwise negation to get
            // the positive equivalent, and then flip the sign.
            s = -((~s & 0xffff) + 1);
        }
        return s;
    };
    var extractNothing = function() {
        return 0;
    };
    var storeInt = function(v, b, st) {
        b[st] = v & 0xFF;
        b[st + 1] = (v >> 8) & 0xFF;
        b[st + 2] = (v >> 16) & 0xFF;
        b[st + 3] = (v >> 24) & 0xFF;
    };
    var storeBEInt = function(v, b, st) {
        b[st + 3] = v & 0xFF;
        b[st + 2] = (v >> 8) & 0xFF;
        b[st + 1] = (v >> 16) & 0xFF;
        b[st] = (v >> 24) & 0xFF;
    };
    var storeShort = function(v, b, st) {
        b[st] = v & 0xFF;
        b[st + 1] = (v >> 8) & 0xFF;
    };
    var storeBEShort = function(v, b, st) {
        b[st + 1] = v & 0xFF;
        b[st] = (v >> 8) & 0xFF;
    };
    var storeByte = function(v, b, st) {
        b[st] = v & 0xFF;
    };
    var storeString = function(v, b, st) {
        var i=0;
        while (i<v.length) {
            b[st + i] = v[i];
            ++i;
        }
        b[st + i] = 0;
    };
    var storeNothing = function() {
    };

    var fields = {
        'i': { len: 4, put: storeInt, get: extractInt },
        'n': { len: 4, put: storeInt, get: extractSignedInt },
        's': { len: 2, put: storeShort, get: extractShort },
        'h': { len: 2, put: storeShort, get: extractSignedShort },
        'b': { len: 1, put: storeByte, get: extractByte },
        'I': { len: 4, put: storeBEInt, get: extractBEInt },
        'N': { len: 4, put: storeBEInt, get: extractSignedBEInt },
        'S': { len: 2, put: storeBEShort, get: extractBEShort },
        'H': { len: 2, put: storeBEShort, get: extractSignedBEShort },
        'z': { len: 0, put: storeString, get: extractZString },
        'Z': { len: 0, put: storeString, get: extractString },
    };

    var parseformat = function(format, names) {
        var formats = [];
        if (names == null)
            names = [];
        var nameindex = 0;
        var pat = / *([0-9]*)([a-zA-Z.]) */g;
        var a;
        var offset = 0;
        while ((a = pat.exec(format)) !== null) {
            var fmt = {};
            var count = 1;
            if (a[1]) {
                count = parseInt(a[1], 10);
            }
            var f = a[2];
            // we treat Z and z specially -- the length field is their
            // maximum length
            if (f === 'z' || f === 'Z') {
                fmt.len = count;
                fmt.offset = offset;
                offset += fmt.len;
                fmt.get = fields[f].get;
                fmt.put = fields[f].put;
                if (names[nameindex]) {
                    fmt.name = names[nameindex++];
                } else {
                    fmt.name = '_field_' + nameindex++;
                }
                formats.push(fmt);
            }
            else if (fields[f]) {
                for (var j=0; j<count; ++j) {
                    fmt.len = fields[f].len;
                    fmt.offset = offset;
                    offset += fmt.len;
                    fmt.get = fields[f].get;
                    fmt.put = fields[f].put;
                    if (names[nameindex]) {
                        fmt.name = names[nameindex++];
                    } else {
                        fmt.name = '_field_' + nameindex++;
                    }
                    formats.push(_.clone(fmt));
                }
            } else {
                fmt.len = count;
                fmt.offset = offset;
                offset += fmt.len;
                fmt.get = extractNothing;
                fmt.put = storeNothing;
                fmt.name = null;
                formats.push(fmt);
            }
        }
        return formats;

    };

    var structlen = function(s) {
        var fmts = parseformat(s);
        var t = 0;
        for (var i = 0; i < fmts.length; ++i) {
            t += fmts[i].len;
        }
        return t;
    };

    var pack = function(buf, offset, format) {
        var fmts = parseformat(format);

        for (var i=0; i < fmts.length; ++i) {
            fmts[i].put(arguments[i+3], buf, offset + fmts[i].offset, fmts[i].len);
        }
        // inefficient but probably doesn't ever matter
        return structlen(format);
    };

    // This is if you prefer to build things with a chaining api
    var createUnpacker = function() {
        return {
            format: '',
            names: [],
            add: function(fmts, nms) {
                this.format += fmts;
                this.names = this.names.concat(nms);
                return this;
            },
            go: function(buf, offset, o) {
                return unpack(buf, offset, this.format, this.names, o);
            }
        };
    };

    // buf is an indexable array of bytes
    // offset is the starting offset within buf to unpack the structure
    // format is a format string
    // names is an array of names (within the result object) to store the
    // unpacked data; if a name is '_' the field is not stored.
    // o is the result object -- if it does not exist, it will be created
    // returns (and potentially modifies o)
    var unpack = function(buf, offset, format, names, o) {
        var result;
        if (o != null) {
            result = o;
        } else {
            result = {};
        }

        var fmts = parseformat(format, names);

        for (var i=0; i < fmts.length; ++i) {
            var value = fmts[i].get(buf, offset + fmts[i].offset, fmts[i].len);
            var name = fmts[i].name;
            if (name != null) {
                if (typeof(name) == "string") {
                    result[name] = value;
                } else {
                    // if not a string it will be an array of keys we should nest
                    // so ["a", "b", 1] does result[a][b][1]=value
                    var v = result;
                    for (var j=0; j<name.length-1; ++j) {
                        if (!v[name[j]]) {
                            v[name[j]] = [];
                        }
                        v = v[name[j]];
                    }
                    v[name[j]] = value;
                }
            }
        }
        // result.formats = fmts;
        return result;
    };

    var copyBytes = function(buf, offset, bytes, bytecount) {
        for (var i = 0; i < bytecount; ++i) {
            buf[offset + i] = bytes[i];
        }
        return bytecount;
    };

    var test = function() {
        var l = structlen('b6.Si');
        console.log(l);
        var p = parseformat('2ibsi4.b32z', 'abcd');
        console.log(p);
        var buf = new Uint8Array(32);
        var len = pack(buf, 0, 'iI2bsSb', 254, 65534, 55, 66, 1023, 256, 7);
        console.log(len);
        console.log(buf);
        var result = unpack(buf, 0, 'iI2bsS.', ['a', 'b', 'c', 'd', 'e', 'f']);
        console.log(result);
        buf[0] = 0xff;
        buf[1] = 0xff;
        buf[2] = 0xff;
        buf[3] = 0xff;
        result = unpack(buf, 0, 'i', ['x']);
        console.log(result);
        var v = [0, 30, 0, 0, 0, 1, 0, 7, 0, 0, 0, 1, 0, 7, 73, 110, 115, 117, 108, 101, 116, 0, 79, 109, 110, 105, 80, 111, 100, 0, 5, 170];
        for (var i = 0; i < v.length; ++i) {
            buf[i] = v[i];
        }
        result = unpack(buf, 0, '7S8z8zS', ['l', 'b', 'c', 'd', 'e', 'f', 'g', 's1', 's2', 'ck']);
        len = structlen('7S8z8zS');
        console.log(v.length, len);
        console.log(result);
    };

    return {
        extractString: extractString,
        extractZString: extractZString,
        extractInt: extractInt,
        extractShort: extractShort,
        extractByte: extractByte,
        extractBEInt: extractBEInt,
        extractBEShort: extractBEShort,
        storeInt: storeInt,
        storeBEInt: storeBEInt,
        storeShort: storeShort,
        storeBEShort: storeBEShort,
        storeByte: storeByte,
        storeString: storeString,
        pack: pack,
        createUnpacker: createUnpacker,
        unpack: unpack,
        structlen: structlen,
        copyBytes: copyBytes,
        test: test
    };
};

<|endoftext|>


<commit_before>/*
LightboxeS V 0.1 'Florence'
This file features all JS needed for LightboxeS to work.
Just add it to your project files and include it at the end of your body.

Created by Daniel Swoboda - @snoato
*/

//number of images in current lightbox
var lbs_imgCounter = 0;
//index of displayed image
var lbs_imgIndex = 0;
//array holding the sources of all images
var lbs_images = null;
///array holding all lightboxes
var lbs_lightboxes = document.getElementsByClassName('lightboxes');

//inits the lightboxes and adds tags needed
function lbs_init(){
    for(var i=0; i<lbs_lightboxes.length; i++){
        var lbs_pictures = lbs_lightboxes[i].getElementsByTagName('img');
        for(var j=0; j<lbs_pictures.length; j++){
            lbs_pictures[j].addEventListener("click", function(event){
                lbs_pose(event.target || event.srcElement);
            });
            lbs_pictures[j].setAttribute("lbs_lbx_index", i.toString());
            lbs_pictures[j].setAttribute("lbs_pic_index", j.toString());
        }
    }
}

//removes the lightbox and reinits the images
function lbs_dispose(){
    var lbs_box = document.getElementById("lightboxesBox");
    lbs_box.parentElement.removeChild(lbs_box);
    lbs_init();
}

//shows a new lightbox starting with the given caller (a lightbox img)
function lbs_pose(caller){
    var lbs_lightboxindex = parseInt(caller.getAttribute("lbs_lbx_index"));

    document.body.innerHTML = document.body.innerHTML + '
\n
\n\n
\n

Loading...

\n<button class="lightboxesButton" id="lightboxesButtonBack"></button>\n<button class="lightboxesButton" id="lightboxesButtonForward"></button>\n<button id="lightboxesButtonClose" onclick="lbs_dispose();"></button>\n
';

    document.getElementById("lightboxesButtonBack").addEventListener("click", function(){
        lbs_slide("back");
    });

    document.getElementById("lightboxesButtonForward").addEventListener("click", function(){
        lbs_slide("forward");
    });

    window.addEventListener("resize", function(){
        var img = document.getElementById("lbs_currImg");
        lbs_resize(img);
    });

    lbs_imgCounter = lbs_lightboxes[lbs_lightboxindex].getElementsByTagName('img').length;

    lbs_imgIndex = parseInt(caller.getAttribute("lbs_pic_index"));
    lbs_images = lbs_getSource(lbs_lightboxindex);

    lbs_swap();

    if(lbs_imgIndex == 0){
        document.getElementById("lightboxesButtonBack").style.display = "none";
    }
    if(lbs_imgIndex == lbs_imgCounter-1){
        document.getElementById("lightboxesButtonForward").style.display = "none";
    }
}

//gets the sources of all images within the given lightbox
function lbs_getSource(lbs_lightboxindex){
    var lbs_pictures = lbs_lightboxes[lbs_lightboxindex].getElementsByTagName('img');
    var lbs_images = Array();
    for(var i=0; i<lbs_pictures.length; i++){
        lbs_images.push(lbs_pictures[i].getAttribute('src'));
    }
    return lbs_images;
}

//resizes the images in order to fit them perfectly on the screen (maybe too complicated)
function lbs_resize(img){
    var imgWidth = parseInt(img.width);
    var imgHeight = parseInt(img.height);
    var imgWrapper = document.getElementById('lightboxesImgWrapper');

    var w = window,
        d = document,
        e = d.documentElement,
        g = d.getElementsByTagName('body')[0],
        width = parseInt((w.innerWidth || e.clientWidth || g.clientWidth)),
        height = parseInt((w.innerHeight|| e.clientHeight|| g.clientHeight));

    var newWidth = img.style.width;
    var newLeftDist = img.style.left;
    var newHeight = img.style.height;
    var newTopDist = img.style.top;
    if((imgWidth >= (width - width*0.1) && imgHeight <= (height - width*0.1))){
        img.style.width = "100%";
        img.style.left = "0%";
        img.style.height = "auto";
        img.style.top = ((parseInt(imgWrapper.clientHeight) - parseInt(img.clientHeight))/2);
    } else if((imgWidth <= (width - width*0.1) && imgHeight >= (height - width*0.1))){
        img.style.height = "100%";
        img.style.top = "0%";
        img.style.width = "auto";
        img.style.left = ((parseInt(imgWrapper.clientWidth) - parseInt(img.clientWidth))/2);
    } else if((imgWidth <= (width - width*0.1) && imgHeight <= (height - width*0.1))){
        img.style.left = ((parseInt(imgWrapper.clientWidth) - parseInt(img.clientWidth))/2);
        img.style.top = ((parseInt(imgWrapper.clientHeight) - parseInt(img.clientHeight))/2);
    } else {
        if(imgWidth >= imgHeight){
            img.style.width = "100%";
            img.style.left = "0%";
            img.style.height = "auto";
            img.style.top = ((parseInt(imgWrapper.clientHeight) - parseInt(img.clientHeight))/2);
        } else {
            img.style.height = "100%";
            img.style.top = "0%";
            img.style.width = "auto";
            img.style.left = ((parseInt(imgWrapper.clientWidth) - parseInt(img.clientWidth))/2);
        }
    }
}

//slider function used with the buttons, changes displayed image index, calls swap method and corrects buttons
function lbs_slide(direction){
    document.getElementById("lightboxesLoading").style.display = "block";
    if(direction == "forward"){
        lbs_imgIndex++;
        if(lbs_imgIndex == lbs_imgCounter-1){
             document.getElementById("lightboxesButtonForward").style.display = "none";
        }
        document.getElementById("lightboxesButtonBack").style.display = "block";
    } else if(direction == "back"){
        lbs_imgIndex--;
        if(lbs_imgIndex==0){
            document.getElementById("lightboxesButtonBack").style.display = "none";
        }
        document.getElementById("lightboxesButtonForward").style.display = "block";
    }
    lbs_swap();
}

//swaps the displayed image to the current image
function lbs_swap(){
    document.getElementById("lightboxesImgWrapper").innerHTML = '';
    document.getElementById("lbs_currImg").addEventListener("load", function(){
        lbs_resize(document.getElementById("lbs_currImg"));
        lbs_resize(document.getElementById("lbs_currImg"));
        document.getElementById("lightboxesLoading").style.display = "none";
    });
}

lbs_init();
<commit_msg>*added preventDefault for click events on images in lightbox divs<commit_after>/*
LightboxeS V 0.1 'Florence'
This file features all JS needed for LightboxeS to work.
Just add it to your project files and include it at the end of your body.

Created by Daniel Swoboda - @snoato
*/

//number of images in current lightbox
var lbs_imgCounter = 0;
//index of displayed image
var lbs_imgIndex = 0;
//array holding the sources of all images
var lbs_images = null;
///array holding all lightboxes
var lbs_lightboxes = document.getElementsByClassName('lightboxes');

//inits the lightboxes and adds tags needed
function lbs_init(){
    for(var i=0; i<lbs_lightboxes.length; i++){
        var lbs_pictures = lbs_lightboxes[i].getElementsByTagName('img');
        for(var j=0; j<lbs_pictures.length; j++){
            lbs_pictures[j].addEventListener("click", function(event){
                event.preventDefault();
                lbs_pose(event.target || event.srcElement);
            });
            lbs_pictures[j].setAttribute("lbs_lbx_index", i.toString());
            lbs_pictures[j].setAttribute("lbs_pic_index", j.toString());
        }
    }
}

//removes the lightbox and reinits the images
function lbs_dispose(){
    var lbs_box = document.getElementById("lightboxesBox");
    lbs_box.parentElement.removeChild(lbs_box);
    lbs_init();
}

//shows a new lightbox starting with the given caller (a lightbox img)
function lbs_pose(caller){
    var lbs_lightboxindex = parseInt(caller.getAttribute("lbs_lbx_index"));

    document.body.innerHTML = document.body.innerHTML + '
\n
\n\n
\n

Loading...

\n<button class="lightboxesButton" id="lightboxesButtonBack"></button>\n<button class="lightboxesButton" id="lightboxesButtonForward"></button>\n<button id="lightboxesButtonClose" onclick="lbs_dispose();"></button>\n
';

    document.getElementById("lightboxesButtonBack").addEventListener("click", function(){
        lbs_slide("back");
    });

    document.getElementById("lightboxesButtonForward").addEventListener("click", function(){
        lbs_slide("forward");
    });

    window.addEventListener("resize", function(){
        var img = document.getElementById("lbs_currImg");
        lbs_resize(img);
    });

    lbs_imgCounter = lbs_lightboxes[lbs_lightboxindex].getElementsByTagName('img').length;

    lbs_imgIndex = parseInt(caller.getAttribute("lbs_pic_index"));
    lbs_images = lbs_getSource(lbs_lightboxindex);

    lbs_swap();

    if(lbs_imgIndex == 0){
        document.getElementById("lightboxesButtonBack").style.display = "none";
    }
    if(lbs_imgIndex == lbs_imgCounter-1){
        document.getElementById("lightboxesButtonForward").style.display = "none";
    }
}

//gets the sources of all images within the given lightbox
function lbs_getSource(lbs_lightboxindex){
    var lbs_pictures = lbs_lightboxes[lbs_lightboxindex].getElementsByTagName('img');
    var lbs_images = Array();
    for(var i=0; i<lbs_pictures.length; i++){
        lbs_images.push(lbs_pictures[i].getAttribute('src'));
    }
    return lbs_images;
}

//resizes the images in order to fit them perfectly on the screen (maybe too complicated)
function lbs_resize(img){
    var imgWidth = parseInt(img.width);
    var imgHeight = parseInt(img.height);
    var imgWrapper = document.getElementById('lightboxesImgWrapper');

    var w = window,
        d = document,
        e = d.documentElement,
        g = d.getElementsByTagName('body')[0],
        width = parseInt((w.innerWidth || e.clientWidth || g.clientWidth)),
        height = parseInt((w.innerHeight|| e.clientHeight|| g.clientHeight));

    var newWidth = img.style.width;
    var newLeftDist = img.style.left;
    var newHeight = img.style.height;
    var newTopDist = img.style.top;
    if((imgWidth >= (width - width*0.1) && imgHeight <= (height - width*0.1))){
        img.style.width = "100%";
        img.style.left = "0%";
        img.style.height = "auto";
        img.style.top = ((parseInt(imgWrapper.clientHeight) - parseInt(img.clientHeight))/2);
    } else if((imgWidth <= (width - width*0.1) && imgHeight >= (height - width*0.1))){
        img.style.height = "100%";
        img.style.top = "0%";
        img.style.width = "auto";
        img.style.left = ((parseInt(imgWrapper.clientWidth) - parseInt(img.clientWidth))/2);
    } else if((imgWidth <= (width - width*0.1) && imgHeight <= (height - width*0.1))){
        img.style.left = ((parseInt(imgWrapper.clientWidth) - parseInt(img.clientWidth))/2);
        img.style.top = ((parseInt(imgWrapper.clientHeight) - parseInt(img.clientHeight))/2);
    } else {
        if(imgWidth >= imgHeight){
            img.style.width = "100%";
            img.style.left = "0%";
            img.style.height = "auto";
            img.style.top = ((parseInt(imgWrapper.clientHeight) - parseInt(img.clientHeight))/2);
        } else {
            img.style.height = "100%";
            img.style.top = "0%";
            img.style.width = "auto";
            img.style.left = ((parseInt(imgWrapper.clientWidth) - parseInt(img.clientWidth))/2);
        }
    }
}

//slider function used with the buttons, changes displayed image index, calls swap method and corrects buttons
function lbs_slide(direction){
    document.getElementById("lightboxesLoading").style.display = "block";
    if(direction == "forward"){
        lbs_imgIndex++;
        if(lbs_imgIndex == lbs_imgCounter-1){
             document.getElementById("lightboxesButtonForward").style.display = "none";
        }
        document.getElementById("lightboxesButtonBack").style.display = "block";
    } else if(direction == "back"){
        lbs_imgIndex--;
        if(lbs_imgIndex==0){
            document.getElementById("lightboxesButtonBack").style.display = "none";
        }
        document.getElementById("lightboxesButtonForward").style.display = "block";
    }
    lbs_swap();
}

//swaps the displayed image to the current image
function lbs_swap(){
    document.getElementById("lightboxesImgWrapper").innerHTML = '';
    document.getElementById("lbs_currImg").addEventListener("load", function(){
        lbs_resize(document.getElementById("lbs_currImg"));
        lbs_resize(document.getElementById("lbs_currImg"));
        document.getElementById("lightboxesLoading").style.display = "none";
    });
}

lbs_init();
<|endoftext|>


<commit_before>(function($) {
  jQuery.fn.lineeditor = function (options, callback) {
    return this.each(function () {
      var el = this;
      var $el = $(this);

      if (el.nodeName.toLowerCase() != 'textarea') { return; }

      var hidden = $('<input type="hidden">').attr('id', el.id).attr('name',el.name).val($el.val());
      var container = $('

');
      var val = $el.val();

      var buttons = $('');
      $.each({
        'delete': removeLine,
        'moveup': move('up'),
        'movedown': move('down')
      },function(k,v){
        var node = $('' + k + '').click(v);
        buttons.append(node);
      });

      hidden.val(val);
      container.append(hidden);

      container.append($('Add Line').click(addLine));

      $.each(val.split("\n"),function(){
        addLine(this);
      });

      $el.replaceWith(container);
      calculatePositioning();

      function addLine(value) {
        value = (value && value.target ? value.preventDefault() && '' : value);
        var input = $('<input type="text">').val(value?value.toString():'').keydown(processValues);
        var input_container = $('');
        container.find('a:last').before(input_container.append(input).append(buttons.clone(true)));
        processValues();
        calculatePositioning();
        return input;
      }

      function removeLine(ev) {
        ev.preventDefault();
        $(this).closest('.input').remove();
        processValues();
      }

      function move(direction) {
        return (function(ev) {
          ev.preventDefault();
          var target = $(this).closest('.input');
          if (direction == 'down') {
            target.insertAfter(target.next('.input'));
          } else if ( direction == 'up' ) {
            target.insertBefore(target.prev('.input'));
          }
          calculatePositioning();
        })
      }

      function processValues(e) {
        if (e && e.keyCode == 9) {
          e.preventDefault();
          target = $(e.target).closest('.input');
          if (!e.shiftKey) {
            target.nextAll('.input:first').find('input[type=text]:first').focus().size() || addLine().focus();
          } else {
            target.prevAll('.input:first').find('input[type=text]').focus();
          }
          return;
        }
        hidden.val(
          container.find('input[type=text]').map(function(){
            return $(this).val();
          }).toArray().join("\n").trim()
        );
      }

      function calculatePositioning() {
        container.find('.input').each(function(){
          if ( $(this).prev('.input').size() ) {
            $(this).find('.moveup').size() || $(this).find('.buttons').append(buttons.find('.moveup').clone(true));
          } else {
            $(this).find('.moveup').remove();
          }

          if ( $(this).next('.input').size() ) {
             $(this).find('.movedown').size() || $(this).find('.buttons').append(buttons.find('.movedown').clone(true));
          } else {
            $(this).find('.movedown').remove();
          }

          $(this).width($(this).find('input[type=text]').outerWidth());
        })
      }

    })
  }
})(jQuery);<commit_msg>Change the event handlers for the line editor so we don't miss the last character<commit_after>(function($) {
  jQuery.fn.lineeditor = function (options, callback) {
    return this.each(function () {
      var el = this;
      var $el = $(this);

      if (el.nodeName.toLowerCase() != 'textarea') { return; }

      var hidden = $('<input type="hidden">').attr('id', el.id).attr('name',el.name).val($el.val());
      var container = $('

');
      var val = $el.val();

      var buttons = $('');
      $.each({
        'delete': removeLine,
        'moveup': move('up'),
        'movedown': move('down')
      },function(k,v){
        var node = $('' + k + '').click(v);
        buttons.append(node);
      });

      hidden.val(val);
      container.append(hidden);

      container.append($('Add Line').click(addLine));

      $.each(val.split("\n"),function(){
        addLine(this);
      });

      $el.replaceWith(container);
      calculatePositioning();

      function addLine(value) {
        value = (value && value.target ? value.preventDefault() && '' : value);
        var input = $('<input type="text">').val(value?value.toString():'').change(processValues).keyup(processValues).keypress(processValues);
        var input_container = $('');
        container.find('a:last').before(input_container.append(input).append(buttons.clone(true)));
        processValues();
        calculatePositioning();
        return input;
      }

      function removeLine(ev) {
        ev.preventDefault();
        $(this).closest('.input').remove();
        processValues();
      }

      function move(direction) {
        return (function(ev) {
          ev.preventDefault();
          var target = $(this).closest('.input');
          if (direction == 'down') {
            target.insertAfter(target.next('.input'));
          } else if ( direction == 'up' ) {
            target.insertBefore(target.prev('.input'));
          }
          calculatePositioning();
        })
      }

      function processValues(e) {
        if (e && e.keyCode == 9) {
          e.preventDefault();
          target = $(e.target).closest('.input');
          if (!e.shiftKey) {
            target.nextAll('.input:first').find('input[type=text]:first').focus().size() || addLine().focus();
          } else {
            target.prevAll('.input:first').find('input[type=text]').focus();
          }
          return;
        }
        hidden.val(
          container.find('input[type=text]').map(function(){
            return $(this).val();
          }).toArray().join("\n").trim()
        );
      }

      function calculatePositioning() {
        container.find('.input').each(function(){
          if ( $(this).prev('.input').size() ) {
            $(this).find('.moveup').size() || $(this).find('.buttons').append(buttons.find('.moveup').clone(true));
          } else {
            $(this).find('.moveup').remove();
          }

          if ( $(this).next('.input').size() ) {
             $(this).find('.movedown').size() || $(this).find('.buttons').append(buttons.find('.movedown').clone(true));
          } else {
            $(this).find('.movedown').remove();
          }

          $(this).width($(this).find('input[type=text]').outerWidth());
        })
      }

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