<commit_before>// jscs:disable jsDoc
'use strict';

var _ = require('underscore'),
	path = require('path'),
	fs = require('fs-extended'),
	gm = require('gm'),
	colors = require('colors'),
	config = require('./config'),
	specs = require('./specs'),
	logger = require('./logger');

/**
 * Creates task to run async
 * @param  {String}   name     spec to use
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, task)
 */
exports.createTask = function createTask(name, opts, callback) {
	var self = this;

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get spec
		self.getSpec(name, cfg, function (err, spec) {

			if (err) {
				return callback(err);
			}

			// pass back task
			callback(null, function (_callback) {
				self.run(spec, cfg, _callback);
			});
		});
	});
};

/**
 * Creates tasks to run async
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, task)
 */
exports.createTasks = function createTasks(opts, callback) {
	var self = this;

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get specs
		self.getSpecs(cfg, function (err, specs) {

			if (err) {
				return callback(err);
			}

			var tasks = {};

			// for each spec
			_.each(specs, function (spec) {

				// push task
				tasks[spec.name] = function (_callback) {
					self.run(spec, cfg, _callback);
				};
			});

			// pass back tasks
			callback(null, tasks);
		});
	});
};

/**
 * Run a task
 * @param  {Object}   specs    initialized specificiation
 * @param  {Object}   cfg      initialized configuration
 * @param  {Function} callback callback(err, output)
 */
exports.run = function run(spec, cfg, callback) {

	// do not output to input
	if (spec.output === cfg.input) {

		// inform CLI
		if (cfg.cli) {
			logger.info('Skipping input as output: ' + spec.output.cyan);
		}

		return callback();
	}

	fs.exists(cfg.input, function (exists) {

		// input doesn't exist
		if (!exists) {
			return callback('Input not found: ' + cfg.input);
		}

		// NOTE: non-sync version didn't work in paralel
		fs.createDirSync(path.dirname(spec.output));

		var im = gm.subClass({
			imageMagick: true
		});

		// read
		var convert = im(cfg.input);

		var flatten = !spec.alpha && !(cfg.nine && spec.ninePath);

		// flatten
		if (flatten) {
			convert = convert.flatten();
		}

		var specRatio = spec.width / spec.height;

		var containWidth, containHeight;

		// nine or fill
		if (!cfg.crop || (cfg.nine && (spec.ninePath || spec.alsoNine))) {

			// only scale down
			if (cfg.inputWidth > spec.width || cfg.inputHeight > spec.height) {

				if (cfg.inputRatio > specRatio) {
					containWidth = spec.width;
					containHeight = spec.width / cfg.inputRatio;
				} else {
					containWidth = spec.height * cfg.inputRatio;
					containHeight = spec.height;
				}

				// resize fitting dimensions (using in() or order will be wrong for fill)
				convert = convert
					.in('-resize').in(spec.width + 'x' + spec.height);

			} else {
				containWidth = cfg.inputWidth;
				containHeight = cfg.inputHeight;
			}

			// nine
			if (cfg.nine && spec.ninePath) {

				// extent with 1px transparent border
				convert = convert
					.borderColor('none')
					.border(1, 1);

				// draw black pixels
				convert = convert
					.fill('black')
					.drawPoint(1, 0)
					.drawPoint(containWidth, 0)
					.drawPoint(0, 1)
					.drawPoint(0, containHeight);

				// fill
			} else {

				// calculate required padding
				var padLeft = Math.floor((spec.width - containWidth) / 2);
				var padTop = Math.floor((spec.height - containHeight) / 2);

				// enlarge canvas using outer pixel to fill (all using in() or order will be wrong)
				convert = convert
					.in('-define')
					.in('distort:viewport=' + spec.width + 'x' + spec.height + '-' + padLeft + '-' + padTop)
					.in('-distort').in('SRT').in('0')
					.in('+repage');
			}

			// crop
		} else {

			var resize = (function () {
				var contentRatio, z, smallerWidth, smallerHeight, factor,
					result = {
						width: spec.width,
						height: spec.height
					};

				// user has given content dimensions
				if (cfg.width && cfg.height) {
					contentRatio = cfg.width / cfg.height;

					// e.g. specs landscape, content is portrait
					if (specRatio > contentRatio) {
						z = spec.height / cfg.height;

						// e.g. specs portrait, content is landscape
					} else {
						z = spec.width / cfg.width;
					}

					// don't scale up
					if (z > 1) {
						logger.warn('Target size is bigger then content: ' + spec.output.cyan);

					} else {
						smallerWidth = Math.round(cfg.inputWidth * z);
						smallerHeight = Math.round(cfg.inputHeight * z);

						// width or height won't cover specs anymore if content needs to fit as well
						if (smallerWidth < spec.width || smallerHeight < spec.height) {
							factor = Math.max(spec.width / smallerWidth, spec.height / smallerHeight);
							logger.warn('For '.white + (spec.width + 'x' + spec.height).cyan + ' to contain '.white + (cfg.width + 'x' + cfg.height).cyan + ' the input needs to be at least: '.white + (Math.ceil(cfg.inputWidth * factor) + 'x' + Math.ceil(cfg.inputHeight * factor)).cyan);

						} else {
							result.width = smallerWidth;
							result.height = smallerHeight;
						}
					}
				}

				return result;

			})();

			// resize covering dimensions
			convert = convert.resize(resize.width, resize.height, '^');

			// crop from center to exact dimensions
			convert = convert
				.gravity('Center')
				.crop(spec.width, spec.height, 0, 0);
		}

		// border radius (http://www.rubblewebs.co.uk/imagemagick/display_example.php?example=69)
		if (spec.type === 'icon' && !flatten && cfg.radius > 0 && _.size(_.difference(spec.platforms, ['ios', 'ipad', 'iphone', 'apple-watch'])) > 0) {
			var radius = Math.ceil((spec.width / 100) * cfg.radius);

			// in
			convert = convert
				.in('-size').in(spec.width + 'x' + spec.height)
				.in('xc:none')
				.in('-fill').in('white')
				.in('-draw').in('roundRectangle 0,0 ' + spec.width + ',' + spec.height + ' ' + radius + ',' + radius);

			// out
			convert = convert
				.compose('SrcIn')
				.out('-composite');
		}

		// annotate
		if (cfg.label) {
			var fontSize = Math.round((containWidth || spec.width) / 11);

			convert = convert
				.fill('#F00')
				.font(path.join(__dirname, 'Calibri.ttf'))
				.fontSize(fontSize);

			if (cfg.nine && spec.ninePath) {
				convert = convert.drawText(1, (containHeight || spec.height) / 2, spec.name);
			} else {
				convert = convert.drawText(0, 0, spec.name);
			}
		}

		// headers
		convert = convert
			.noProfile()
			.units('PixelsPerInch')
			.density(spec.dpi, spec.dpi);

		// rotate
		if (spec.rotate) {
			convert = convert.rotate('white', spec.rotate);
		}

		// remove alpha
		if (flatten) {
			convert = convert.out('-background', 'white', '-alpha', 'off');
		}

		// write
		convert.write(spec.output, function (err) {

			if (err) {
				return callback(err);
			}

			// async feedback for CLI
			if (cfg.cli) {
				logger.info('Generated: ' + spec.output.cyan);
			}

			// pass back output
			callback(null, spec.output);

		});

		// show command
		if (cfg.trace) {
			logger.debug('Executing: ' + convert.args().join(' ').cyan);
		}

	});
};

/**
 * Get a spec
 * @param  {String}   name     spec to get
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, output)
 */
exports.getSpec = function getSpec(name, opts, callback) {

	// not really needed, but for consistency ;)
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get initted spec
		var spec = _initSpec(name, cfg);

		// spec not found
		if (!spec) {
			return callback('Spec not found: ' + name);
		}

		// pass back spec
		callback(null, spec);
	});
};

/**
 * Get specs
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, output)
 */
exports.getSpecs = function getSpecs(opts, callback) {

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		var _specs = {};

		// filter specs
		_.each(specs, function (spec, name) {

			// does not meet type
			if (cfg.type && spec.type !== cfg.type) {
				return;
			}

			// doest not meet orientation
			if (spec.type === 'splash' && spec.orientation && cfg.orientation && cfg.orientation !== spec.orientation) {
				return;
			}

			// does not meet min dpi
			if (spec.dpi && cfg.minDpi && spec.dpi < cfg.minDpi) {
				return;
			}

			// does not meet max dpi
			if (spec.dpi && cfg.maxDpi && spec.dpi > cfg.maxDpi) {
				return;
			}

			// has no localized path
			if (cfg.locale && !spec.localePath) {
				return;
			}

			// has no 9-patch path
			if (cfg.nine && spec.type === 'splash' && _.indexOf(spec.platforms, 'android') !== -1 && !(spec.ninePath || spec.alsoNine)) {
				return;
			}

			// does not meet at least one platform
			if (!_.some(spec.platforms, function (platform) {
					return !!cfg[platform];
				})) {

				return false;
			}

			// push initted spec
			_specs[name] = _initSpec(name, cfg);
		});

		// no specs not found
		if (_.size(_specs) === 0) {
			return callback('No specs found');
		}

		// pass back specs
		callback(null, _specs);
	});
};

function _initSpec(name, cfg) {

	// doesn't exist
	if (!specs[name]) {
		return;
	}

	// cloning
	var spec = _.clone(specs[name]);

	// name
	spec.name = name;

	// default dpi
	spec.dpi = spec.dpi || 72;

	// size to widthxheight
	if (spec.size) {
		spec.width = spec.height = spec.size;
	}

	// fix
	if (cfg.fix) {
		if (spec.widthFix) {
			spec.width = spec.widthFix;
		}
		if (spec.heightFix) {
			spec.height = spec.heightFix;
		}
	}

	// full output path
	var _path;

	if (cfg.nine && spec.ninePath) {
		_path = cfg.locale ? spec.nineLocalePath.replace(':locale:', cfg.locale) : spec.ninePath;

	} else {
		_path = (cfg.locale && spec.localePath) ? spec.localePath.replace(':locale:', cfg.locale) : spec.path;
		_path = _path.replace(':assets:', cfg.assetsDir);
	}

	spec.output = path.join(cfg.outputDir, _path);

	return spec;
}
<commit_msg>9-patch compatibility for API 21+<commit_after>// jscs:disable jsDoc
'use strict';

var _ = require('underscore'),
	path = require('path'),
	fs = require('fs-extended'),
	gm = require('gm'),
	colors = require('colors'),
	config = require('./config'),
	specs = require('./specs'),
	logger = require('./logger');

/**
 * Creates task to run async
 * @param  {String}   name     spec to use
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, task)
 */
exports.createTask = function createTask(name, opts, callback) {
	var self = this;

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get spec
		self.getSpec(name, cfg, function (err, spec) {

			if (err) {
				return callback(err);
			}

			// pass back task
			callback(null, function (_callback) {
				self.run(spec, cfg, _callback);
			});
		});
	});
};

/**
 * Creates tasks to run async
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, task)
 */
exports.createTasks = function createTasks(opts, callback) {
	var self = this;

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get specs
		self.getSpecs(cfg, function (err, specs) {

			if (err) {
				return callback(err);
			}

			var tasks = {};

			// for each spec
			_.each(specs, function (spec) {

				// push task
				tasks[spec.name] = function (_callback) {
					self.run(spec, cfg, _callback);
				};
			});

			// pass back tasks
			callback(null, tasks);
		});
	});
};

/**
 * Run a task
 * @param  {Object}   specs    initialized specificiation
 * @param  {Object}   cfg      initialized configuration
 * @param  {Function} callback callback(err, output)
 */
exports.run = function run(spec, cfg, callback) {

	// do not output to input
	if (spec.output === cfg.input) {

		// inform CLI
		if (cfg.cli) {
			logger.info('Skipping input as output: ' + spec.output.cyan);
		}

		return callback();
	}

	fs.exists(cfg.input, function (exists) {

		// input doesn't exist
		if (!exists) {
			return callback('Input not found: ' + cfg.input);
		}

		// NOTE: non-sync version didn't work in paralel
		fs.createDirSync(path.dirname(spec.output));

		var im = gm.subClass({
			imageMagick: true
		});

		// read
		var convert = im(cfg.input);

		var flatten = !spec.alpha && !(cfg.nine && spec.ninePath);

		// flatten
		if (flatten) {
			convert = convert.flatten();
		}

		var specRatio = spec.width / spec.height;

		var containWidth, containHeight;

		// nine or fill
		if (!cfg.crop || (cfg.nine && (spec.ninePath || spec.alsoNine))) {

			// only scale down
			if (cfg.inputWidth > spec.width || cfg.inputHeight > spec.height) {

				if (cfg.inputRatio > specRatio) {
					containWidth = spec.width;
					containHeight = spec.width / cfg.inputRatio;
				} else {
					containWidth = spec.height * cfg.inputRatio;
					containHeight = spec.height;
				}

				// resize fitting dimensions (using in() or order will be wrong for fill)
				convert = convert
					.in('-resize').in(spec.width + 'x' + spec.height);

			} else {
				containWidth = cfg.inputWidth;
				containHeight = cfg.inputHeight;
			}

			// nine
			if (cfg.nine && spec.ninePath) {

				// extent with 1px transparent border
				convert = convert
					.borderColor('none')
					.border(1, 1);

				// draw black pixels
				convert = convert
					.fill('black')

					// stretchable area
					.drawPoint(1, 0)
					.drawPoint(containWidth, 0)
					.drawPoint(0, 1)
					.drawPoint(0, containHeight)

					// padding box (required since API 21)
					.drawLine(containWidth + 1, 2, containWidth + 1, containHeight - 1)
					.drawLine(2, containHeight + 1, containWidth - 1, containHeight + 1);

				// fill
			} else {

				// calculate required padding
				var padLeft = Math.floor((spec.width - containWidth) / 2);
				var padTop = Math.floor((spec.height - containHeight) / 2);

				// enlarge canvas using outer pixel to fill (all using in() or order will be wrong)
				convert = convert
					.in('-define')
					.in('distort:viewport=' + spec.width + 'x' + spec.height + '-' + padLeft + '-' + padTop)
					.in('-distort').in('SRT').in('0')
					.in('+repage');
			}

			// crop
		} else {

			var resize = (function () {
				var contentRatio, z, smallerWidth, smallerHeight, factor,
					result = {
						width: spec.width,
						height: spec.height
					};

				// user has given content dimensions
				if (cfg.width && cfg.height) {
					contentRatio = cfg.width / cfg.height;

					// e.g. specs landscape, content is portrait
					if (specRatio > contentRatio) {
						z = spec.height / cfg.height;

						// e.g. specs portrait, content is landscape
					} else {
						z = spec.width / cfg.width;
					}

					// don't scale up
					if (z > 1) {
						logger.warn('Target size is bigger then content: ' + spec.output.cyan);

					} else {
						smallerWidth = Math.round(cfg.inputWidth * z);
						smallerHeight = Math.round(cfg.inputHeight * z);

						// width or height won't cover specs anymore if content needs to fit as well
						if (smallerWidth < spec.width || smallerHeight < spec.height) {
							factor = Math.max(spec.width / smallerWidth, spec.height / smallerHeight);
							logger.warn('For '.white + (spec.width + 'x' + spec.height).cyan + ' to contain '.white + (cfg.width + 'x' + cfg.height).cyan + ' the input needs to be at least: '.white + (Math.ceil(cfg.inputWidth * factor) + 'x' + Math.ceil(cfg.inputHeight * factor)).cyan);

						} else {
							result.width = smallerWidth;
							result.height = smallerHeight;
						}
					}
				}

				return result;

			})();

			// resize covering dimensions
			convert = convert.resize(resize.width, resize.height, '^');

			// crop from center to exact dimensions
			convert = convert
				.gravity('Center')
				.crop(spec.width, spec.height, 0, 0);
		}

		// border radius (http://www.rubblewebs.co.uk/imagemagick/display_example.php?example=69)
		if (spec.type === 'icon' && !flatten && cfg.radius > 0 && _.size(_.difference(spec.platforms, ['ios', 'ipad', 'iphone', 'apple-watch'])) > 0) {
			var radius = Math.ceil((spec.width / 100) * cfg.radius);

			// in
			convert = convert
				.in('-size').in(spec.width + 'x' + spec.height)
				.in('xc:none')
				.in('-fill').in('white')
				.in('-draw').in('roundRectangle 0,0 ' + spec.width + ',' + spec.height + ' ' + radius + ',' + radius);

			// out
			convert = convert
				.compose('SrcIn')
				.out('-composite');
		}

		// annotate
		if (cfg.label) {
			var fontSize = Math.round((containWidth || spec.width) / 11);

			convert = convert
				.fill('#F00')
				.font(path.join(__dirname, 'Calibri.ttf'))
				.fontSize(fontSize);

			if (cfg.nine && spec.ninePath) {
				convert = convert.drawText(1, (containHeight || spec.height) / 2, spec.name);
			} else {
				convert = convert.drawText(0, 0, spec.name);
			}
		}

		// headers
		convert = convert
			.noProfile()
			.units('PixelsPerInch')
			.density(spec.dpi, spec.dpi);

		// rotate
		if (spec.rotate) {
			convert = convert.rotate('white', spec.rotate);
		}

		// remove alpha
		if (flatten) {
			convert = convert.out('-background', 'white', '-alpha', 'off');
		}

		// write
		convert.write(spec.output, function (err) {

			if (err) {
				return callback(err);
			}

			// async feedback for CLI
			if (cfg.cli) {
				logger.info('Generated: ' + spec.output.cyan);
			}

			// pass back output
			callback(null, spec.output);

		});

		// show command
		if (cfg.trace) {
			logger.debug('Executing: ' + convert.args().join(' ').cyan);
		}

	});
};

/**
 * Get a spec
 * @param  {String}   name     spec to get
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, output)
 */
exports.getSpec = function getSpec(name, opts, callback) {

	// not really needed, but for consistency ;)
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		// get initted spec
		var spec = _initSpec(name, cfg);

		// spec not found
		if (!spec) {
			return callback('Spec not found: ' + name);
		}

		// pass back spec
		callback(null, spec);
	});
};

/**
 * Get specs
 * @param  {Object}   opts     options
 * @param  {Function} callback callback(err, output)
 */
exports.getSpecs = function getSpecs(opts, callback) {

	// config
	config(opts, function (err, cfg) {

		if (err) {
			return callback(err);
		}

		var _specs = {};

		// filter specs
		_.each(specs, function (spec, name) {

			// does not meet type
			if (cfg.type && spec.type !== cfg.type) {
				return;
			}

			// doest not meet orientation
			if (spec.type === 'splash' && spec.orientation && cfg.orientation && cfg.orientation !== spec.orientation) {
				return;
			}

			// does not meet min dpi
			if (spec.dpi && cfg.minDpi && spec.dpi < cfg.minDpi) {
				return;
			}

			// does not meet max dpi
			if (spec.dpi && cfg.maxDpi && spec.dpi > cfg.maxDpi) {
				return;
			}

			// has no localized path
			if (cfg.locale && !spec.localePath) {
				return;
			}

			// has no 9-patch path
			if (cfg.nine && spec.type === 'splash' && _.indexOf(spec.platforms, 'android') !== -1 && !(spec.ninePath || spec.alsoNine)) {
				return;
			}

			// does not meet at least one platform
			if (!_.some(spec.platforms, function (platform) {
					return !!cfg[platform];
				})) {

				return false;
			}

			// push initted spec
			_specs[name] = _initSpec(name, cfg);
		});

		// no specs not found
		if (_.size(_specs) === 0) {
			return callback('No specs found');
		}

		// pass back specs
		callback(null, _specs);
	});
};

function _initSpec(name, cfg) {

	// doesn't exist
	if (!specs[name]) {
		return;
	}

	// cloning
	var spec = _.clone(specs[name]);

	// name
	spec.name = name;

	// default dpi
	spec.dpi = spec.dpi || 72;

	// size to widthxheight
	if (spec.size) {
		spec.width = spec.height = spec.size;
	}

	// fix
	if (cfg.fix) {
		if (spec.widthFix) {
			spec.width = spec.widthFix;
		}
		if (spec.heightFix) {
			spec.height = spec.heightFix;
		}
	}

	// full output path
	var _path;

	if (cfg.nine && spec.ninePath) {
		_path = cfg.locale ? spec.nineLocalePath.replace(':locale:', cfg.locale) : spec.ninePath;

	} else {
		_path = (cfg.locale && spec.localePath) ? spec.localePath.replace(':locale:', cfg.locale) : spec.path;
		_path = _path.replace(':assets:', cfg.assetsDir);
	}

	spec.output = path.join(cfg.outputDir, _path);

	return spec;
}
<|endoftext|>


<commit_before>import './polyfill'
import * as framework from './lib/framework'
import {version, optionalDependencies} from './package.json'

for (let methodName in framework) {
    global[methodName] = function (...args) {
        const ret = framework[methodName](...args)
        if (ret instanceof Error) {
            console.error(ret.toString())
        } else (ret instanceof Array) {
            ret.forEach(r => {
                if (ret instanceof Error) {
                    console.error(r.toString())
                }
            })
        }
        return ret
    }
}

Object.assign(global, {
    frameworkVersion: version,
    needTransformerVersion: optionalDependencies['weex-transformer']
})

/**
 * register methods
 */
const methods = require('./lib/api/methods')
const {registerMethods} = global
registerMethods(methods)
<commit_msg>* [jsfm] fix typo<commit_after>import './polyfill'
import * as framework from './lib/framework'
import {version, optionalDependencies} from './package.json'

for (let methodName in framework) {
    global[methodName] = function (...args) {
        const ret = framework[methodName](...args)
        if (ret instanceof Error) {
            console.error(ret.toString())
        } else if (ret instanceof Array) {
            ret.forEach(r => {
                if (ret instanceof Error) {
                    console.error(r.toString())
                }
            })
        }
        return ret
    }
}

Object.assign(global, {
    frameworkVersion: version,
    needTransformerVersion: optionalDependencies['weex-transformer']
})

/**
 * register methods
 */
const methods = require('./lib/api/methods')
const {registerMethods} = global
registerMethods(methods)
<|endoftext|>


<commit_before>/**
 * Parted JSON Parser
 * Copyright (c) 2011, Christopher Jeffrey (MIT License)
 */

var EventEmitter = require('events').EventEmitter
  , StringDecoder = require('string_decoder').StringDecoder
  , FreeList = require('freelist').FreeList;

/**
 * Parser
 */

var Parser = function(options) {
  if (!(this instanceof Parser)) {
    return new Parser(options);
  }

  EventEmitter.call(this);

  this.writable = true;
  this.readable = true;

  this.options = options || {};

  this._reset();
};

Parser.prototype.__proto__ = EventEmitter.prototype;

/**
 * Parsing
 */

Parser.prototype.write = function(data) {
  if (!this.writable) return;

  try {
    this._parse(data);
    this.written += data.length;
    this.emit('data', data.length);
  } catch(e) {
    this._error(e);
  }

  return true;
};

Parser.prototype.end = function(data) {
  if (!this.writable) return;

  if (data) this.write(data);

  this.writable = false;
  this.readable = false;

  if (this.value) {
    switch (this.state) {
      case 'number':
        this.assemble('number', +this.value);
        break;
      case 'value':
        this.assemble('string', this.value);
        break;
      default:
        return this._error('Unexpected EOF.');
    }
  }

  this.emit('end', this.data);

  this._reset();

  return true;
};

Parser.prototype._expect = function(ch, ex) {
  if (ch !== ex) {
    throw new
      Error('Unexpected `' + ch + '`.'
        + (ex ? ' Expected: `' + ex + '`.' : ''));
  }
};

Parser.prototype._unexpected = Parser.prototype._expect;

Parser.prototype._parse = function(data) {
  var data = this.decode.write(data)
    , i = 0
    , l = data.length
    , ch;

  for (; i < l; i++) {
    ch = data[i];
    switch (this.state) {
      case 'value': {
        switch (ch) {
          case '{':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.assemble('object start');
            break;
          case '}':
            if (this.value) {
              this.assemble('string', this.value);
              this.value = '';
            }
            this.assemble('object end');
            break;
          case '[':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.assemble('array start');
            break;
          case ']':
            if (this.value) {
              this.assemble('string', this.value);
              this.value = '';
            }
            this.assemble('array end');
            break;
          case '"':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = 'string';
            break;
          case ',':
            if (this.value) {
              this.assemble('string', this.value);
              this.value = '';
            }
            break;
          case ':':
            if (!this.value) {
              return this._unexpected(ch);
            }
            this.assemble('object key', this.value);
            this.value = '';
            break;
          case '-':
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = 'number';
            this.value += ch;
            break;
          case 'f':
          case 'n':
          case 't':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = ch;
            break;
          default:
            if (ch > ' ') {
              return this._unexpected(ch);
            }
            break;
        }
        break;
      }
      case 'number': {
        switch (ch) {
          case '}':
            // check last byte of .value
            // make sure it is a digit
            this.state = 'value';
            this.assemble('number', +this.value);
            this.value = '';
            this.assemble('object end');
            break;
          case ']':
            this.state = 'value';
            this.assemble('number', +this.value);
            this.value = '';
            this.assemble('array end');
            break;
          case ',':
            this.state = 'value';
            this.assemble('number', +this.value);
            this.value = '';
            break;
          case 'e':
          case 'E':
            if (!/\d$/.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '-':
          case '+':
            if (!/e$/i.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '.':
            if (/\.|e|.-|\+/i.test(this.value)
                || !/\d$/.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            // json doesnt allow 0x numbers
            if (this.length === 1
                && this.value[0] === '0') {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          default:
            if (ch <= ' ') {
              this.state = 'value';
              this.assemble('number', +this.value);
              this.value = '';
            } else {
              return this._unexpected(ch);
            }
            break;
        }
        break;
      }
      case 'string': {
        switch (ch) {
          case '"':
            this.state = 'value';
            // hack for empty strings
            if (!this.value) this.value = ' ';
            break;
          case '\\':
            this.state = 'escape';
            break;
          default:
            this.value += ch;
            break;
        }
        break;
      }
      case 'escape': {
        switch (ch) {
          case 'u':
            this.unicode = '';
            break;
          case 'b':
            this.value += '\b';
            break;
          case 'f':
            this.value += '\f';
            break;
          case 'n':
            this.value += '\n';
            break;
          case 'r':
            this.value += '\r';
            break;
          case 't':
            this.value += '\t';
            break;
          case '"':
          case '/':
          case '\\':
            this.value += ch;
            break;
          default:
            // json is supposed to throw
            // if there's a backslash
            // in the wrong spot
            return this._unexpected(ch);
        }
        if (ch === 'u') {
          this.state = 'unicode';
        } else {
          this.state = 'string';
        }
        break;
      }
      case 'unicode': {
        if ((ch >= '0' && ch <= '9')
            || (ch >= 'A' && ch <= 'F')
            || (ch >= 'a' && ch <= 'f')) {
          this.unicode += ch;
          if (this.unicode.length === 4) {
            this.unicode = parseInt(this.unicode, 16);
            this.value += String.fromCharCode(this.unicode);
            this.unicode = '';
            this.state = 'string';
          }
        } else {
          return this._unexpected(ch);
        }
        break;
      }
      case 'n': {
        this._expect(ch, 'u');
        this.state = 'nu';
        break;
      }
      case 'nu': {
        this._expect(ch, 'l');
        this.state = 'nul';
        break;
      }
      case 'nul': {
        this._expect(ch, 'l');
        this.assemble('null', null);
        this.state = 'value';
        break;
      }
      case 'f': {
        this._expect(ch, 'a');
        this.state = 'fa';
        break;
      }
      case 'fa': {
        this._expect(ch, 'l');
        this.state = 'fal';
        break;
      }
      case 'fal': {
        this._expect(ch, 's');
        this.state = 'fals';
        break;
      }
      case 'fals': {
        this._expect(ch, 'e');
        this.assemble('boolean', false);
        this.state = 'value';
        break;
      }
      case 't': {
        this._expect(ch, 'r');
        this.state = 'tr';
        break;
      }
      case 'tr': {
        this._expect(ch, 'u');
        this.state = 'tru';
        break;
      }
      case 'tru': {
        this._expect(ch, 'e');
        this.assemble('boolean', true);
        this.state = 'value';
        break;
      }
    }
  }
};

/**
 * Object Assembler
 */

Parser.prototype.assemble = function(type, val) {
  switch (type) {
    case 'object start':
      var object = {};
      this.assemble('value', object);
      this.stack.push(object);
      break;
    case 'object key':
      this.key = val;
      break;
    case 'object end':
      this.emit('object', this.stack.pop());
      break;
    case 'array start':
      var array = [];
      this.assemble('value', array);
      this.stack.push(array);
      break;
    case 'array end':
      this.emit('array', this.stack.pop());
      break;
    case 'number':
    case 'string':
    case 'boolean':
    case 'null':
      this.emit(type, val);
      this.assemble('value', val);
      break;
    case 'value':
      var top = this.stack[this.stack.length-1];
      if (!top) {
        if (this.key) {
          return this._error('Unexpected key.');
        }
        this.data = val;
        this.stack.push(val);
        return;
      }
      if (!this.key) {
        if (!Array.isArray(top)) {
          return this._error('Expected key.');
        }
        top.push(val);
      } else {
        if (Array.isArray(top)) {
          return this._error('Unexpected key.');
        }
        top[this.key] = val;
        this.key = null;
      }
      break;
  }
};

Parser.prototype._reset = function() {
  this.stack = [];
  this.key = null;
  this.data = null;

  this.state = 'value';
  this.value = '';
  this.unicode = '';
  this.written = 0;
  this.decode = new StringDecoder('utf8');
  this._events = {};
};

Parser.prototype._error = function(err) {
  this.destroy();
  this.emit('error', typeof err === 'string'
    ? new Error(err)
    : err);
};

Parser.prototype.destroy = function(err) {
  this.writable = false;
  this.readable = false;
  this._reset();
};

/**
 * Legacy
 */

Parser.create = function() {
  return new Parser();
};

/**
 * Pool
 */

//var parsers = new FreeList('parsers', 20, function() {
//  return new Parser();
//});

/**
 * Expose
 */

module.exports = exports = Parser;

exports.middleware = function(options) {
  return function(req, res, next) {
    if (req.method === 'GET'
        || req.method === 'HEAD'
        || req._json) return next();

    req._json = true;

    var type = req.headers['content-type'];

    if (type) type = type.split(';')[0].trim().toLowerCase();

    if (type === 'application/json') {
      exports.handle(req, res, next, options);
    } else {
      if (options.ensureBody) {
        req.body = {};
      }
      next();
    }
  };
};

exports.handle = function(req, res, next, options) {
  var parser = new Parser() //parsers.alloc()
    , limit = options.jsonLimit || options.limit;

  parser.on('error', function(err) {
    req.destroy();
    next(err);
  });

  parser.on('end', function(data) {
    //parsers.free(parser);
    req.body = data;
    next();
  });

  if (limit) {
    parser.on('data', function() {
      if (this.written > limit) {
        this.emit('error', new Error('Overflow.'));
        this.destroy();
      }
    });
  }

  req.body = {};
  req.pipe(parser);
};
<commit_msg>strict json checks<commit_after>/**
 * Parted JSON Parser
 * Copyright (c) 2011, Christopher Jeffrey (MIT License)
 */

var EventEmitter = require('events').EventEmitter
  , StringDecoder = require('string_decoder').StringDecoder
  , FreeList = require('freelist').FreeList;

/**
 * Parser
 */

var Parser = function(options) {
  if (!(this instanceof Parser)) {
    return new Parser(options);
  }

  EventEmitter.call(this);

  this.writable = true;
  this.readable = true;

  this.options = options || {};

  this._reset();
};

Parser.prototype.__proto__ = EventEmitter.prototype;

/**
 * Parsing
 */

Parser.prototype.write = function(data) {
  if (!this.writable) return;

  try {
    this._parse(data);
    this.written += data.length;
    this.emit('data', data.length);
  } catch(e) {
    this._error(e);
  }

  return true;
};

Parser.prototype.end = function(data) {
  if (!this.writable) return;

  if (data) this.write(data);

  this.writable = false;
  this.readable = false;

  if (this.value) {
    switch (this.state) {
      case 'number':
        this.assemble('number', +this.value);
        break;
      case 'value':
        this.assemble('string', this.value);
        break;
      default:
        return this._error('Unexpected EOF.');
    }
  }

  this.emit('end', this.data);

  this._reset();

  return true;
};

Parser.prototype._expect = function(ch, ex) {
  if (ch !== ex) {
    throw new
      Error('Unexpected `' + ch + '`.'
        + (ex ? ' Expected: `' + ex + '`.' : ''));
  }
};

Parser.prototype._unexpected = Parser.prototype._expect;

Parser.prototype._parse = function(data) {
  var data = this.decode.write(data)
    , i = 0
    , l = data.length
    , ch;

  for (; i < l; i++) {
    ch = data[i];
    switch (this.state) {
      case 'value': {
        switch (ch) {
          case '{':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.assemble('object start');
            break;
          case '}':
            if (this.value) {
              this.assemble('string', this.value);
              this.value = '';
            }
            this.assemble('object end');
            break;
          case '[':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.assemble('array start');
            break;
          case ']':
            if (this.value) {
              this.assemble('string', this.value);
              this.value = '';
            }
            this.assemble('array end');
            break;
          case '"':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = 'string';
            break;
          case ',':
            if (this.value) {
              this.assemble('string', this.value);
              this.value = '';
            }
            break;
          case ':':
            if (!this.value) {
              return this._unexpected(ch);
            }
            this.assemble('object key', this.value);
            this.value = '';
            break;
          case '-':
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = 'number';
            this.value += ch;
            break;
          case 'f':
          case 'n':
          case 't':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = ch;
            break;
          default:
            if (ch > ' ') {
              return this._unexpected(ch);
            }
            break;
        }
        break;
      }
      case 'number': {
        switch (ch) {
          case '}':
            // check last byte of .value
            // make sure it is a digit
            this.state = 'value';
            this.assemble('number', +this.value);
            this.value = '';
            this.assemble('object end');
            break;
          case ']':
            this.state = 'value';
            this.assemble('number', +this.value);
            this.value = '';
            this.assemble('array end');
            break;
          case ',':
            this.state = 'value';
            this.assemble('number', +this.value);
            this.value = '';
            break;
          case 'e':
          case 'E':
            if (!/\d$/.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '-':
          case '+':
            if (!/e$/i.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '.':
            if (/\.|e|.-|\+/i.test(this.value)
                || !/\d$/.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            // json doesnt allow 0x numbers
            if (this.length === 1
                && this.value[0] === '0') {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          default:
            if (ch <= ' ') {
              this.state = 'value';
              this.assemble('number', +this.value);
              this.value = '';
            } else {
              return this._unexpected(ch);
            }
            break;
        }
        break;
      }
      case 'string': {
        switch (ch) {
          case '"':
            this.state = 'value';
            // hack for empty strings
            if (!this.value) this.value = ' ';
            break;
          case '\\':
            this.state = 'escape';
            break;
          default:
            this.value += ch;
            break;
        }
        break;
      }
      case 'escape': {
        switch (ch) {
          case 'u':
            this.unicode = '';
            break;
          case 'b':
            this.value += '\b';
            break;
          case 'f':
            this.value += '\f';
            break;
          case 'n':
            this.value += '\n';
            break;
          case 'r':
            this.value += '\r';
            break;
          case 't':
            this.value += '\t';
            break;
          case '"':
          case '/':
          case '\\':
            this.value += ch;
            break;
          default:
            // json is supposed to throw
            // if there's a backslash
            // in the wrong spot
            return this._unexpected(ch);
        }
        if (ch === 'u') {
          this.state = 'unicode';
        } else {
          this.state = 'string';
        }
        break;
      }
      case 'unicode': {
        if ((ch >= '0' && ch <= '9')
            || (ch >= 'A' && ch <= 'F')
            || (ch >= 'a' && ch <= 'f')) {
          this.unicode += ch;
          if (this.unicode.length === 4) {
            this.unicode = parseInt(this.unicode, 16);
            this.value += String.fromCharCode(this.unicode);
            this.unicode = '';
            this.state = 'string';
          }
        } else {
          return this._unexpected(ch);
        }
        break;
      }
      case 'n': {
        this._expect(ch, 'u');
        this.state = 'nu';
        break;
      }
      case 'nu': {
        this._expect(ch, 'l');
        this.state = 'nul';
        break;
      }
      case 'nul': {
        this._expect(ch, 'l');
        this.assemble('null', null);
        this.state = 'value';
        break;
      }
      case 'f': {
        this._expect(ch, 'a');
        this.state = 'fa';
        break;
      }
      case 'fa': {
        this._expect(ch, 'l');
        this.state = 'fal';
        break;
      }
      case 'fal': {
        this._expect(ch, 's');
        this.state = 'fals';
        break;
      }
      case 'fals': {
        this._expect(ch, 'e');
        this.assemble('boolean', false);
        this.state = 'value';
        break;
      }
      case 't': {
        this._expect(ch, 'r');
        this.state = 'tr';
        break;
      }
      case 'tr': {
        this._expect(ch, 'u');
        this.state = 'tru';
        break;
      }
      case 'tru': {
        this._expect(ch, 'e');
        this.assemble('boolean', true);
        this.state = 'value';
        break;
      }
    }
  }
};

/**
 * Object Assembler
 */

Parser.prototype.assemble = function(type, val) {
  switch (type) {
    case 'object start':
      var object = {};
      this.assemble('value', object);
      this.stack.push(object);
      break;
    case 'object key':
      if (this.key) {
        return this._unexpected(type);
      }
      this.key = val;
      break;
    case 'object end':
      if (this.key) {
        return this._unexpected(type);
      }
      this.emit('object', this.stack.pop());
      break;
    case 'array start':
      var array = [];
      this.assemble('value', array);
      this.stack.push(array);
      break;
    case 'array end':
      this.emit('array', this.stack.pop());
      break;
    case 'number':
    case 'string':
    case 'boolean':
    case 'null':
      this.emit(type, val);
      this.assemble('value', val);
      break;
    case 'value':
      var top = this.stack[this.stack.length-1];
      if (!top) {
        if (this.key) {
          return this._error('Unexpected key.');
        }
        this.data = val;
        this.stack.push(val);
        return;
      }
      if (!this.key) {
        if (!Array.isArray(top)) {
          return this._error('Expected key.');
        }
        top.push(val);
      } else {
        if (Array.isArray(top)) {
          return this._error('Unexpected key.');
        }
        top[this.key] = val;
        this.key = null;
      }
      break;
  }
};

Parser.prototype._reset = function() {
  this.stack = [];
  this.key = null;
  this.data = null;

  this.state = 'value';
  this.value = '';
  this.unicode = '';
  this.written = 0;
  this.decode = new StringDecoder('utf8');
  this._events = {};
};

Parser.prototype._error = function(err) {
  this.destroy();
  this.emit('error', typeof err === 'string'
    ? new Error(err)
    : err);
};

Parser.prototype.destroy = function(err) {
  this.writable = false;
  this.readable = false;
  this._reset();
};

/**
 * Legacy
 */

Parser.create = function() {
  return new Parser();
};

/**
 * Pool
 */

//var parsers = new FreeList('parsers', 20, function() {
//  return new Parser();
//});

/**
 * Expose
 */

module.exports = exports = Parser;

exports.middleware = function(options) {
  return function(req, res, next) {
    if (req.method === 'GET'
        || req.method === 'HEAD'
        || req._json) return next();

    req._json = true;

    var type = req.headers['content-type'];

    if (type) type = type.split(';')[0].trim().toLowerCase();

    if (type === 'application/json') {
      exports.handle(req, res, next, options);
    } else {
      if (options.ensureBody) {
        req.body = {};
      }
      next();
    }
  };
};

exports.handle = function(req, res, next, options) {
  var parser = new Parser() //parsers.alloc()
    , limit = options.jsonLimit || options.limit;

  parser.on('error', function(err) {
    req.destroy();
    next(err);
  });

  parser.on('end', function(data) {
    //parsers.free(parser);
    req.body = data;
    next();
  });

  if (limit) {
    parser.on('data', function() {
      if (this.written > limit) {
        this.emit('error', new Error('Overflow.'));
        this.destroy();
      }
    });
  }

  req.body = {};
  req.pipe(parser);
};
<|endoftext|>


<commit_before>/**
 * Parted JSON Parser
 * Copyright (c) 2011, Christopher Jeffrey (MIT License)
 */

var EventEmitter = require('events').EventEmitter
  , StringDecoder = require('string_decoder').StringDecoder;

/**
 * Parser
 */

var Parser = function(options) {
  if (!(this instanceof Parser)) {
    return new Parser(options);
  }

  EventEmitter.call(this);

  this.writable = true;
  this.readable = true;

  this.options = options || {};

  this._reset();
};

Parser.prototype.__proto__ = EventEmitter.prototype;

/**
 * Parsing
 */

Parser.prototype.write = function(data) {
  if (!this.writable) return;

  try {
    this._parse(data);
    this.written += data.length;
    this.emit('data', data.length);
  } catch (e) {
    this._error(e);
  }

  return true;
};

Parser.prototype.end = function(data) {
  if (!this.writable) return;

  if (data) this.write(data);

  this.writable = false;
  this.readable = false;

  if (this.value) {
    switch (this.state) {
      case 'number':
        this.emit('number', +this.value);
        break;
      case 'value':
        this.emit('string', this.value);
        break;
      default:
        return this._unexpected('EOF');
    }
  }

  this.emit('end');

  this._reset();

  return true;
};

Parser.prototype._parse = function(data) {
  var data = this.decode.write(data)
    , i = 0
    , l = data.length
    , ch;

  for (; i < l; i++) {
    ch = data[i];
    switch (this.state) {
      case 'value': {
        switch (ch) {
          case '{':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.emit('object start');
            break;
          case '}':
            if (this.value) {
              this.emit('string', this.value);
              this.value = '';
            }
            this.emit('object end');
            break;
          case '[':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.emit('array start');
            break;
          case ']':
            if (this.value) {
              this.emit('string', this.value);
              this.value = '';
            }
            this.emit('array end');
            break;
          case '"':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = 'string';
            break;
          case ',':
            if (this.value) {
              this.emit('string', this.value);
              this.value = '';
            }
            break;
          case ':':
            if (!this.value) {
              return this._unexpected(ch);
            }
            this.emit('object key', this.value);
            this.value = '';
            break;
          case '-':
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = 'number';
            this.value += ch;
            break;
          case 'f':
          case 'n':
          case 't':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = ch;
            break;
          default:
            if (ch > ' ') {
              return this._unexpected(ch);
            }
            break;
        }
        break;
      }
      case 'number': {
        switch (ch) {
          case '}':
            // check last byte of .value
            // make sure it is a digit
            this.state = 'value';
            this.emit('number', +this.value);
            this.value = '';
            this.emit('object end');
            break;
          case ']':
            this.state = 'value';
            this.emit('number', +this.value);
            this.value = '';
            this.emit('array end');
            break;
          case ',':
            this.state = 'value';
            this.emit('number', +this.value);
            this.value = '';
            break;
          case 'e':
          case 'E':
            if (!/\d$/.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '-':
          case '+':
            if (!/e$/i.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '.':
            if (/\.|e|.-|\+/i.test(this.value)
                || !/\d$/.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            // json doesnt allow 0x numbers
            if (this.value.length === 1
                && this.value[0] === '0') {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          default:
            if (ch <= ' ') {
              this.state = 'value';
              this.emit('number', +this.value);
              this.value = '';
            } else {
              return this._unexpected(ch);
            }
            break;
        }
        break;
      }
      case 'string': {
        switch (ch) {
          case '"':
            this.state = 'value';
            // hack for empty strings
            if (!this.value) this.value = ' ';
            break;
          case '\\':
            this.state = 'escape';
            break;
          default:
            this.value += ch;
            break;
        }
        break;
      }
      case 'escape': {
        switch (ch) {
          case 'u':
            this.unicode = '';
            break;
          case 'b':
            this.value += '\b';
            break;
          case 'f':
            this.value += '\f';
            break;
          case 'n':
            this.value += '\n';
            break;
          case 'r':
            this.value += '\r';
            break;
          case 't':
            this.value += '\t';
            break;
          case '"':
          case '/':
          case '\\':
            this.value += ch;
            break;
          default:
            // json is supposed to throw
            // if there's a backslash
            // in the wrong spot
            return this._unexpected(ch);
        }
        if (ch === 'u') {
          this.state = 'unicode';
        } else {
          this.state = 'string';
        }
        break;
      }
      case 'unicode': {
        if ((ch >= '0' && ch <= '9')
            || (ch >= 'A' && ch <= 'F')
            || (ch >= 'a' && ch <= 'f')) {
          this.unicode += ch;
          if (this.unicode.length === 4) {
            this.unicode = parseInt(this.unicode, 16);
            this.value += String.fromCharCode(this.unicode);
            this.unicode = '';
            this.state = 'string';
          }
        } else {
          return this._unexpected(ch);
        }
        break;
      }
      case 'n': {
        this._expect(ch, 'u');
        this.state = 'nu';
        break;
      }
      case 'nu': {
        this._expect(ch, 'l');
        this.state = 'nul';
        break;
      }
      case 'nul': {
        this._expect(ch, 'l');
        this.emit('null', null);
        this.state = 'value';
        break;
      }
      case 'f': {
        this._expect(ch, 'a');
        this.state = 'fa';
        break;
      }
      case 'fa': {
        this._expect(ch, 'l');
        this.state = 'fal';
        break;
      }
      case 'fal': {
        this._expect(ch, 's');
        this.state = 'fals';
        break;
      }
      case 'fals': {
        this._expect(ch, 'e');
        this.emit('boolean', false);
        this.state = 'value';
        break;
      }
      case 't': {
        this._expect(ch, 'r');
        this.state = 'tr';
        break;
      }
      case 'tr': {
        this._expect(ch, 'u');
        this.state = 'tru';
        break;
      }
      case 'tru': {
        this._expect(ch, 'e');
        this.emit('boolean', true);
        this.state = 'value';
        break;
      }
    }
  }
};

Parser.prototype._reset = function() {
  this.state = 'value';
  this.value = '';
  this.unicode = '';
  this.written = 0;
  this.decode = new StringDecoder('utf8');
  this._events = {};
};

Parser.prototype._expect = function(ch, ex) {
  if (ch !== ex) {
    throw new
      Error('Unexpected `' + ch + '`.'
        + (ex ? ' Expected: `' + ex + '`.' : ''));
  }
};

Parser.prototype._unexpected = function(tok) {
  return this._error('Unexpected `' + tok + '`.');
};

Parser.prototype._error = function(err) {
  this.emit('error', typeof err === 'string'
    ? new Error(err)
    : err);
  this.destroy();
};

Parser.prototype.destroy = function(err) {
  this.writable = false;
  this.readable = false;
  this._reset();
};

/**
 * Create
 */

Parser.create = function(options) {
  var parser = new Parser(options)
    , limit = options.limit
    , stack = []
    , data
    , key;

  function output() {
    parser.emit('output', data);
    if (!options.noMultiple) data = undefined;
  }

  parser.on('object start', function() {
    var object = {};
    onval(object);
    stack.push(object);
  });

  parser.on('object key', function(val) {
    if (key != null) {
      return this._unexpected(':');
    }
    // not terribly necessary
    // but good for error messages
    if (!stack.length) {
      return this._unexpected(val);
    }
    key = val;
  });

  parser.on('object end', function() {
    var top = stack.pop();
    if (!top || Array.isArray(top) || key != null) {
      return this._unexpected('}');
    }
    if (!stack.length) output();
  });

  parser.on('array start', function() {
    var array = [];
    onval(array);
    stack.push(array);
  });

  parser.on('array end', function() {
    var top = stack.pop();
    if (!top || !Array.isArray(top)) {
      return this._unexpected(']');
    }
    if (!stack.length) output();
  });

  parser.on('number', onval);
  parser.on('string', onval);
  parser.on('boolean', onval);
  parser.on('null', onval);

  function onval(val) {
    if (data === undefined) {
      if (key != null) {
        return parser._unexpected(':');
      }
      data = val;
      if (data === null || typeof data !== 'object') output();
      return;
    }

    var top = stack[stack.length-1];
    if (!top) {
      // another value is being entered
      // we're only allowed one in json
      return parser._unexpected(typeof val);
    }

    if (key == null) {
      if (!Array.isArray(top)) {
        return parser._unexpected(typeof val);
      }
      top.push(val);
    } else {
      if (Array.isArray(top)) {
        return parser._unexpected(':');
      }
      top[key] = val;
      key = null;
    }
  }

  parser.on('data', function() {
    if (this.written > limit) {
      this.emit('error', new Error('Overflow.'));
      this.destroy();
    }
  });

  return parser;
};

/**
 * Expose
 */

module.exports = exports = Parser;

exports.middleware = function(options) {
  return function(req, res, next) {
    if (options.ensureBody) {
      req.body = {};
    }

    if (req.method === 'GET'
        || req.method === 'HEAD'
        || req._json) return next();

    req._json = true;

    var type = req.headers['content-type'];

    if (type) type = type.split(';')[0].trim().toLowerCase();

    if (type === 'application/json') {
      exports.handle(req, res, next, options);
    } else {
      next();
    }
  };
};

exports.handle = function(req, res, next, options) {
  if (options.noMultiple == null) {
    options.noMultiple = true;
  }

  var parser = Parser.create(options)
    , data = [];

  parser.on('output', function(value) {
    data.push(value);
  });

  parser.on('error', function(err) {
    req.destroy();
    next(err);
  });

  parser.on('end', function() {
    if (options.noMultiple) {
      data = data[0];
    }
    req.body = data;
    next();
  });

  req.body = {};
  req.pipe(parser);
};
<commit_msg>Stop iteration within _parse if the parser has been destroyed<commit_after>/**
 * Parted JSON Parser
 * Copyright (c) 2011, Christopher Jeffrey (MIT License)
 */

var EventEmitter = require('events').EventEmitter
  , StringDecoder = require('string_decoder').StringDecoder;

/**
 * Parser
 */

var Parser = function(options) {
  if (!(this instanceof Parser)) {
    return new Parser(options);
  }

  EventEmitter.call(this);

  this.writable = true;
  this.readable = true;

  this.options = options || {};

  this._reset();
};

Parser.prototype.__proto__ = EventEmitter.prototype;

/**
 * Parsing
 */

Parser.prototype.write = function(data) {
  if (!this.writable) return;

  try {
    this._parse(data);
    this.written += data.length;
    this.emit('data', data.length);
  } catch (e) {
    this._error(e);
  }

  return true;
};

Parser.prototype.end = function(data) {
  if (!this.writable) return;

  if (data) this.write(data);

  this.writable = false;
  this.readable = false;

  if (this.value) {
    switch (this.state) {
      case 'number':
        this.emit('number', +this.value);
        break;
      case 'value':
        this.emit('string', this.value);
        break;
      default:
        return this._unexpected('EOF');
    }
  }

  this.emit('end');

  this._reset();

  return true;
};

Parser.prototype._parse = function(data) {
  var data = this.decode.write(data)
    , i = 0
    , l = data.length
    , ch;

  for (; i < l; i++) {
    ch = data[i];
    if (!this.writable && !this.readable) {
      return
    }
    switch (this.state) {
      case 'value': {
        switch (ch) {
          case '{':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.emit('object start');
            break;
          case '}':
            if (this.value) {
              this.emit('string', this.value);
              this.value = '';
            }
            this.emit('object end');
            break;
          case '[':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.emit('array start');
            break;
          case ']':
            if (this.value) {
              this.emit('string', this.value);
              this.value = '';
            }
            this.emit('array end');
            break;
          case '"':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = 'string';
            break;
          case ',':
            if (this.value) {
              this.emit('string', this.value);
              this.value = '';
            }
            break;
          case ':':
            if (!this.value) {
              return this._unexpected(ch);
            }
            this.emit('object key', this.value);
            this.value = '';
            break;
          case '-':
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = 'number';
            this.value += ch;
            break;
          case 'f':
          case 'n':
          case 't':
            if (this.value) {
              return this._unexpected(ch);
            }
            this.state = ch;
            break;
          default:
            if (ch > ' ') {
              return this._unexpected(ch);
            }
            break;
        }
        break;
      }
      case 'number': {
        switch (ch) {
          case '}':
            // check last byte of .value
            // make sure it is a digit
            this.state = 'value';
            this.emit('number', +this.value);
            this.value = '';
            this.emit('object end');
            break;
          case ']':
            this.state = 'value';
            this.emit('number', +this.value);
            this.value = '';
            this.emit('array end');
            break;
          case ',':
            this.state = 'value';
            this.emit('number', +this.value);
            this.value = '';
            break;
          case 'e':
          case 'E':
            if (!/\d$/.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '-':
          case '+':
            if (!/e$/i.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '.':
            if (/\.|e|.-|\+/i.test(this.value)
                || !/\d$/.test(this.value)) {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
          case '8':
          case '9':
            // json doesnt allow 0x numbers
            if (this.value.length === 1
                && this.value[0] === '0') {
              return this._unexpected(ch);
            }
            this.value += ch;
            break;
          default:
            if (ch <= ' ') {
              this.state = 'value';
              this.emit('number', +this.value);
              this.value = '';
            } else {
              return this._unexpected(ch);
            }
            break;
        }
        break;
      }
      case 'string': {
        switch (ch) {
          case '"':
            this.state = 'value';
            // hack for empty strings
            if (!this.value) this.value = ' ';
            break;
          case '\\':
            this.state = 'escape';
            break;
          default:
            this.value += ch;
            break;
        }
        break;
      }
      case 'escape': {
        switch (ch) {
          case 'u':
            this.unicode = '';
            break;
          case 'b':
            this.value += '\b';
            break;
          case 'f':
            this.value += '\f';
            break;
          case 'n':
            this.value += '\n';
            break;
          case 'r':
            this.value += '\r';
            break;
          case 't':
            this.value += '\t';
            break;
          case '"':
          case '/':
          case '\\':
            this.value += ch;
            break;
          default:
            // json is supposed to throw
            // if there's a backslash
            // in the wrong spot
            return this._unexpected(ch);
        }
        if (ch === 'u') {
          this.state = 'unicode';
        } else {
          this.state = 'string';
        }
        break;
      }
      case 'unicode': {
        if ((ch >= '0' && ch <= '9')
            || (ch >= 'A' && ch <= 'F')
            || (ch >= 'a' && ch <= 'f')) {
          this.unicode += ch;
          if (this.unicode.length === 4) {
            this.unicode = parseInt(this.unicode, 16);
            this.value += String.fromCharCode(this.unicode);
            this.unicode = '';
            this.state = 'string';
          }
        } else {
          return this._unexpected(ch);
        }
        break;
      }
      case 'n': {
        this._expect(ch, 'u');
        this.state = 'nu';
        break;
      }
      case 'nu': {
        this._expect(ch, 'l');
        this.state = 'nul';
        break;
      }
      case 'nul': {
        this._expect(ch, 'l');
        this.emit('null', null);
        this.state = 'value';
        break;
      }
      case 'f': {
        this._expect(ch, 'a');
        this.state = 'fa';
        break;
      }
      case 'fa': {
        this._expect(ch, 'l');
        this.state = 'fal';
        break;
      }
      case 'fal': {
        this._expect(ch, 's');
        this.state = 'fals';
        break;
      }
      case 'fals': {
        this._expect(ch, 'e');
        this.emit('boolean', false);
        this.state = 'value';
        break;
      }
      case 't': {
        this._expect(ch, 'r');
        this.state = 'tr';
        break;
      }
      case 'tr': {
        this._expect(ch, 'u');
        this.state = 'tru';
        break;
      }
      case 'tru': {
        this._expect(ch, 'e');
        this.emit('boolean', true);
        this.state = 'value';
        break;
      }
    }
  }
};

Parser.prototype._reset = function() {
  this.state = 'value';
  this.value = '';
  this.unicode = '';
  this.written = 0;
  this.decode = new StringDecoder('utf8');
  this._events = {};
};

Parser.prototype._expect = function(ch, ex) {
  if (ch !== ex) {
    throw new
      Error('Unexpected `' + ch + '`.'
        + (ex ? ' Expected: `' + ex + '`.' : ''));
  }
};

Parser.prototype._unexpected = function(tok) {
  return this._error('Unexpected `' + tok + '`.');
};

Parser.prototype._error = function(err) {
  this.emit('error', typeof err === 'string'
    ? new Error(err)
    : err);
  this.destroy();
};

Parser.prototype.destroy = function(err) {
  this.writable = false;
  this.readable = false;
  this._reset();
};

/**
 * Create
 */

Parser.create = function(options) {
  var parser = new Parser(options)
    , limit = options.limit
    , stack = []
    , data
    , key;

  function output() {
    parser.emit('output', data);
    if (!options.noMultiple) data = undefined;
  }

  parser.on('object start', function() {
    var object = {};
    onval(object);
    stack.push(object);
  });

  parser.on('object key', function(val) {
    if (key != null) {
      return this._unexpected(':');
    }
    // not terribly necessary
    // but good for error messages
    if (!stack.length) {
      return this._unexpected(val);
    }
    key = val;
  });

  parser.on('object end', function() {
    var top = stack.pop();
    if (!top || Array.isArray(top) || key != null) {
      return this._unexpected('}');
    }
    if (!stack.length) output();
  });

  parser.on('array start', function() {
    var array = [];
    onval(array);
    stack.push(array);
  });

  parser.on('array end', function() {
    var top = stack.pop();
    if (!top || !Array.isArray(top)) {
      return this._unexpected(']');
    }
    if (!stack.length) output();
  });

  parser.on('number', onval);
  parser.on('string', onval);
  parser.on('boolean', onval);
  parser.on('null', onval);

  function onval(val) {
    if (data === undefined) {
      if (key != null) {
        return parser._unexpected(':');
      }
      data = val;
      if (data === null || typeof data !== 'object') output();
      return;
    }

    var top = stack[stack.length-1];
    if (!top) {
      // another value is being entered
      // we're only allowed one in json
      return parser._unexpected(typeof val);
    }

    if (key == null) {
      if (!Array.isArray(top)) {
        return parser._unexpected(typeof val);
      }
      top.push(val);
    } else {
      if (Array.isArray(top)) {
        return parser._unexpected(':');
      }
      top[key] = val;
      key = null;
    }
  }

  parser.on('data', function() {
    if (this.written > limit) {
      this.emit('error', new Error('Overflow.'));
      this.destroy();
    }
  });

  return parser;
};

/**
 * Expose
 */

module.exports = exports = Parser;

exports.middleware = function(options) {
  return function(req, res, next) {
    if (options.ensureBody) {
      req.body = {};
    }

    if (req.method === 'GET'
        || req.method === 'HEAD'
        || req._json) return next();

    req._json = true;

    var type = req.headers['content-type'];

    if (type) type = type.split(';')[0].trim().toLowerCase();

    if (type === 'application/json') {
      exports.handle(req, res, next, options);
    } else {
      next();
    }
  };
};

exports.handle = function(req, res, next, options) {
  if (options.noMultiple == null) {
    options.noMultiple = true;
  }

  var parser = Parser.create(options)
    , data = [];

  parser.on('output', function(value) {
    data.push(value);
  });

  parser.on('error', function(err) {
    if (typeof options.handleInvalidJson === 'function') {
      return options.handleInvalidJson(err, req, res, next);
    }
    next(err);
  });

  parser.on('end', function() {
    if (options.noMultiple) {
      data = data[0];
    }
    req.body = data;
    next();
  });

  req.body = {};
  req.pipe(parser);
};
<|endoftext|>


<commit_before>/*global FormFiller, JSONF, jQuery, Logger*/
/*eslint complexity:0 */
// This listens for messages coming from the background page
// This is a long running communication channel
chrome.runtime.onConnect.addListener(function (port) {
  var errors = [];
  var currentError = null;
  var workingOverlayId = "form-o-fill-working-overlay";
  var workingOverlayHtml = "
Form-O-Fill is working, please wait!
";

  var workingTimeout = null;
  var takingLongTimeout = null;
  var wontFinishTimeout = null;

  Logger.info("[content.js] Got a connection from " + port.name);

  if(port.name != "FormOFill") {
    return;
  }

  var hideOverlay = function() {
    jQuery("#" + workingOverlayId).hide();
    clearTimeout(workingTimeout);
    clearTimeout(takingLongTimeout);
    clearTimeout(wontFinishTimeout);
  };

  port.onMessage.addListener(function (message) {
    // Request to fill one field with a value
    if (message.action === "fillField" && message.selector && message.value) {
      Logger.info("[content.js] Filling " + message.selector + " with value " + JSONF.stringify(message.value));
      // REMOVE START
      if (message.beforeData && message.beforeData !== null) {
        Logger.info("[content.js] Also got beforeData = " + JSONF.stringify(message.beforeData));
      }
      // REMOVE END
      currentError = FormFiller.fill(message.selector, message.value, message.beforeData);
      if(typeof currentError !== "undefined" && currentError !== null) {
        Logger.info("[content.js] Got error " + JSONF.stringify(currentError));
        errors.push(currentError);
      }
    }

    // request to return all accumulated errors
    if (message.action === "getErrors") {
      Logger.info("[content.js] Returning " + errors.length + " errors to bg.js");
      var response = {
        "action": "getErrors",
        "errors": JSONF.stringify(errors)
      };
      port.postMessage(response);
    }

    // Show Working overlay
    if (message.action === "showWorkingOverlay") {
      Logger.info("[content.js] Showing working overlay");
      if(document.querySelectorAll("#" + workingOverlayId).length === 0) {
        jQuery("body").append(workingOverlayHtml);
      }

      // Show working overlay after some time
      workingTimeout = setTimeout(function() {
        jQuery("#" + workingOverlayId).show();
      }, 350);

      // Show another overlay when things take REALLY long to finishs
      takingLongTimeout = setTimeout(function () {
        jQuery("#" + workingOverlayId).html("This is really taking too long.");
      }, 4000);

      // Finally if everything fails, clear overlay after 10 seconds
      wontFinishTimeout = setTimeout(hideOverlay, 12000);
    }

    if (message.action === "hideWorkingOverlay") {
      Logger.info("[content.js] Hiding working overlay");
      hideOverlay();
    }
  });

  // Simple one-shot callbacks
  chrome.runtime.onMessage.addListener(function (message, sender, responseCb) {
    if (message.action === "grabContentBySelector") {
      Logger.info("[content.js] Grabber asked for '" + message.message + "'");
      responseCb(jQuery(message.message).eq(0).html());
    }
  });

});

<commit_msg>use outerHTML in findHtml<commit_after>/*global FormFiller, JSONF, jQuery, Logger*/
/*eslint complexity:0 */
// This listens for messages coming from the background page
// This is a long running communication channel
chrome.runtime.onConnect.addListener(function (port) {
  var errors = [];
  var currentError = null;
  var workingOverlayId = "form-o-fill-working-overlay";
  var workingOverlayHtml = "
Form-O-Fill is working, please wait!
";

  var workingTimeout = null;
  var takingLongTimeout = null;
  var wontFinishTimeout = null;

  Logger.info("[content.js] Got a connection from " + port.name);

  if(port.name != "FormOFill") {
    return;
  }

  var hideOverlay = function() {
    jQuery("#" + workingOverlayId).hide();
    clearTimeout(workingTimeout);
    clearTimeout(takingLongTimeout);
    clearTimeout(wontFinishTimeout);
  };

  port.onMessage.addListener(function (message) {
    // Request to fill one field with a value
    if (message.action === "fillField" && message.selector && message.value) {
      Logger.info("[content.js] Filling " + message.selector + " with value " + JSONF.stringify(message.value));
      // REMOVE START
      if (message.beforeData && message.beforeData !== null) {
        Logger.info("[content.js] Also got beforeData = " + JSONF.stringify(message.beforeData));
      }
      // REMOVE END
      currentError = FormFiller.fill(message.selector, message.value, message.beforeData);
      if(typeof currentError !== "undefined" && currentError !== null) {
        Logger.info("[content.js] Got error " + JSONF.stringify(currentError));
        errors.push(currentError);
      }
    }

    // request to return all accumulated errors
    if (message.action === "getErrors") {
      Logger.info("[content.js] Returning " + errors.length + " errors to bg.js");
      var response = {
        "action": "getErrors",
        "errors": JSONF.stringify(errors)
      };
      port.postMessage(response);
    }

    // Show Working overlay
    if (message.action === "showWorkingOverlay") {
      Logger.info("[content.js] Showing working overlay");
      if(document.querySelectorAll("#" + workingOverlayId).length === 0) {
        jQuery("body").append(workingOverlayHtml);
      }

      // Show working overlay after some time
      workingTimeout = setTimeout(function() {
        jQuery("#" + workingOverlayId).show();
      }, 350);

      // Show another overlay when things take REALLY long to finishs
      takingLongTimeout = setTimeout(function () {
        jQuery("#" + workingOverlayId).html("This is really taking too long.");
      }, 4000);

      // Finally if everything fails, clear overlay after 10 seconds
      wontFinishTimeout = setTimeout(hideOverlay, 12000);
    }

    if (message.action === "hideWorkingOverlay") {
      Logger.info("[content.js] Hiding working overlay");
      hideOverlay();
    }
  });

  // Simple one-shot callbacks
  chrome.runtime.onMessage.addListener(function (message, sender, responseCb) {
    if (message.action === "grabContentBySelector") {
      Logger.info("[content.js] Grabber asked for '" + message.message + "'");
      var domElements = jQuery(message.message).map(function (index, $el) {
        return $el;
      });
      if(domElements.length === 1) {
        responseCb(domElements[0].outerHTML);
      } else {
        responseCb(domElements.map(function(el) {
          return el.outerHTML;
        }));
      }
    }
  });

});

<|endoftext|>


<commit_before>/**
 * @file Activation class. Part of the MurmuresJS project.
 * @author github.com/azziliz
 * @author github.com/thyshimrod
 * {@link https://github.com/azziliz/MurmuresJS/ Project page}
 * @license MIT
 */

'use strict';
//debugger;

/**
 * An Activation is a time lapse during which a Characters prepares a Skill for launch.
 * It could also be called "casting" for a spell, "aiming" for an archery skill or "wielding / swinging" for a melee skill.
 * Activations are created when an Order is received from the client.
 * They are enqueued in the Timeline and progress with each Timeline tick.
 * When they are complete, the effect of the Skill contained in the Order is applied.
 *
 * @class
 */
murmures.Activation = function () {
    /** @type {?} */
    this.order = {};
    /** @type {number} */
    this.startTick = 0;
    /** @type {number} */
    this.endTick = 0;
    /** @type {number} */
    this.remainingWork = 0;
};

murmures.Activation.prototype = {
    /**
     * Initialization method reserved for the server.
     */
    build : function (src) {
        this.startTick = (typeof src.startTick === 'undefined') ? '' : src.startTick;
        this.endTick = (typeof src.endTick === 'undefined') ? '' : src.endTick;
        this.remainingWork = (typeof src.remainingWork === 'undefined') ? '' : src.remainingWork;
        this.order = (typeof src.order === 'undefined') ? '' : src.order;
    },

    /**
     * Initialization method reserved for the client.
     */
    initialize : function (src) {
        this.synchronize(src);

    },

    /**
     * Synchronization method reserved for the client.
     */
    synchronize : function (src) {
        if (typeof src === 'undefined') return;
        if (typeof src.startTick !== 'undefined') this.startTick = src.startTick;
        if (typeof src.endTick !== 'undefined') this.endTick = src.endTick;
        if (typeof src.remainingWork !== 'undefined') this.remainingWork = src.remainingWork;
        if (typeof src.order !== 'undefined') this.order = src.order;
    },

    /**
     * Cloning method reserved for the server.
     */
    clone : function () {
        return{
            order : this.order,
            startTick : this.startTick,
            endTick : this.endTick,
            remainingWork : this.remainingWork,
        };
    },

    /**
     * Comparison method reserved for the server.
     */
    compare : function (beforeState) {
        let ret = {};
        if (this.startTick !== beforeState.startTick) ret.startTick = this.startTick;
        if (this.endTick !== beforeState.endTick) ret.endTick = this.endTick;
        if (this.remainingWork !== beforeState.remainingWork) ret.remainingWork = this.remainingWork;
        if (JSON.stringify(this.order) !== JSON.stringify(beforeState.order)) ret.order = this.order;
        if(Object.getOwnPropertyNames(ret).length > 0) return ret;
    },

    /**
     * Applies the effect of the Skill contained in this Order.
     * This method is called by the Timeline when this Activation expires (i.e. remainingWork = 0).
     */
    applyOrder : function () {
        // TODO
    },
};
<commit_msg>#185 : Fix  clone,compare for activation class<commit_after>/**
 * @file Activation class. Part of the MurmuresJS project.
 * @author github.com/azziliz
 * @author github.com/thyshimrod
 * {@link https://github.com/azziliz/MurmuresJS/ Project page}
 * @license MIT
 */

'use strict';
//debugger;

/**
 * An Activation is a time lapse during which a Characters prepares a Skill for launch.
 * It could also be called "casting" for a spell, "aiming" for an archery skill or "wielding / swinging" for a melee skill.
 * Activations are created when an Order is received from the client.
 * They are enqueued in the Timeline and progress with each Timeline tick.
 * When they are complete, the effect of the Skill contained in the Order is applied.
 *
 * @class
 */
murmures.Activation = function () {
    /** @type {?} */
    this.order = {};
    /** @type {number} */
    this.startTick = 0;
    /** @type {number} */
    this.endTick = 0;
    /** @type {number} */
    this.remainingWork = 0;
};

murmures.Activation.prototype = {
    /**
     * Initialization method reserved for the server.
     */
    build : function (src) {
        this.startTick = (typeof src.startTick === 'undefined') ? '' : src.startTick;
        this.endTick = (typeof src.endTick === 'undefined') ? '' : src.endTick;
        this.remainingWork = (typeof src.remainingWork === 'undefined') ? '' : src.remainingWork;
        this.order = (typeof src.order === 'undefined') ? '' : src.order;
    },

    /**
     * Initialization method reserved for the client.
     */
    initialize : function (src) {
        this.synchronize(src);

    },

    /**
     * Synchronization method reserved for the client.
     */
    synchronize : function (src) {
        if (typeof src === 'undefined') return;
        if (typeof src.startTick !== 'undefined') this.startTick = src.startTick;
        if (typeof src.endTick !== 'undefined') this.endTick = src.endTick;
        if (typeof src.remainingWork !== 'undefined') this.remainingWork = src.remainingWork;
        if (typeof src.order !== 'undefined') this.order = src.order;
    },

    /**
     * Cloning method reserved for the server.
     */
    clone : function () {
        return{
            order : this.order.clone(),
            startTick : this.startTick,
            endTick : this.endTick,
            remainingWork : this.remainingWork,
        };
    },

    /**
     * Comparison method reserved for the server.
     */
    compare : function (beforeState) {
        let ret = {};
        if (this.startTick !== beforeState.startTick) ret.startTick = this.startTick;
        if (this.endTick !== beforeState.endTick) ret.endTick = this.endTick;
        if (this.remainingWork !== beforeState.remainingWork) ret.remainingWork = this.remainingWork;
        const retOrder = this.order.compare(beforeState.order);
        if (typeof retOrder !== 'undefined') ret.order = retOrder;
        if(Object.getOwnPropertyNames(ret).length > 0) return ret;
    },

    /**
     * Applies the effect of the Skill contained in this Order.
     * This method is called by the Timeline when this Activation expires (i.e. remainingWork = 0).
     */
    applyOrder : function () {
        // TODO
    },
};
<|endoftext|>


<commit_before>/**
 * @file Activation class. Part of the MurmuresJS project.
 * @author github.com/azziliz
 * @author github.com/thyshimrod
 * {@link https://github.com/azziliz/MurmuresJS/ Project page}
 * @license MIT
 */

'use strict';
//debugger;

/**
 * An Activation is a time lapse during which a Characters prepares a Skill for launch.
 * It could also be called "casting" for a spell, "aiming" for an archery skill or "wielding / swinging" for a melee skill.
 * Activations are created when an Order is received from the client.
 * They are enqueued in the Timeline and progress with each Timeline tick.
 * When they are complete, the effect of the Skill contained in the Order is applied.
 *
 * @class
 */
murmures.Activation = function () {
    /** @type {?} */
    this.order = {};
    /** @type {number} */
    this.startTick = 0;
    /** @type {number} */
    this.endTick = 0;
    /** @type {number} */
    this.remainingWork = 0;
};

murmures.Activation.prototype = {
    /**
     * Initialization method reserved for the server.
     */
    build : function (src) {
        // TODO
    },

    /**
     * Initialization method reserved for the client.
     */
    initialize : function (src) {
        // TODO
    },

    /**
     * Synchronization method reserved for the client.
     */
    synchronize : function (src) {
        // TODO
    },

    /**
     * Cloning method reserved for the server.
     */
    clone : function () {
        // TODO
    },

    /**
     * Comparison method reserved for the server.
     */
    compare : function (beforeState) {
        // TODO
    },

    /**
     * Applies the effect of the Skill contained in this Order.
     * This method is called by the Timeline when this Activation expires (i.e. remainingWork = 0).
     */
    applyOrder : function () {
        // TODO
    },
};
<commit_msg>#185 : complete build, initialize, clone,compare for activation class<commit_after>/**
 * @file Activation class. Part of the MurmuresJS project.
 * @author github.com/azziliz
 * @author github.com/thyshimrod
 * {@link https://github.com/azziliz/MurmuresJS/ Project page}
 * @license MIT
 */

'use strict';
//debugger;

/**
 * An Activation is a time lapse during which a Characters prepares a Skill for launch.
 * It could also be called "casting" for a spell, "aiming" for an archery skill or "wielding / swinging" for a melee skill.
 * Activations are created when an Order is received from the client.
 * They are enqueued in the Timeline and progress with each Timeline tick.
 * When they are complete, the effect of the Skill contained in the Order is applied.
 *
 * @class
 */
murmures.Activation = function () {
    /** @type {?} */
    this.order = {};
    /** @type {number} */
    this.startTick = 0;
    /** @type {number} */
    this.endTick = 0;
    /** @type {number} */
    this.remainingWork = 0;
};

murmures.Activation.prototype = {
    /**
     * Initialization method reserved for the server.
     */
    build : function (src) {
        this.startTick = (typeof src.startTick === 'undefined') ? '' : src.startTick;
        this.endTick = (typeof src.endTick === 'undefined') ? '' : src.endTick;
        this.remainingWork = (typeof src.remainingWork === 'undefined') ? '' : src.remainingWork;
        this.order = (typeof src.order === 'undefined') ? '' : src.order;
    },

    /**
     * Initialization method reserved for the client.
     */
    initialize : function (src) {
        this.synchronize(src);

    },

    /**
     * Synchronization method reserved for the client.
     */
    synchronize : function (src) {
        if (typeof src === 'undefined') return;
        if (typeof src.startTick !== 'undefined') this.startTick = src.startTick;
        if (typeof src.endTick !== 'undefined') this.endTick = src.endTick;
        if (typeof src.remainingWork !== 'undefined') this.remainingWork = src.remainingWork;
        if (typeof src.order !== 'undefined') this.order = src.order;
    },

    /**
     * Cloning method reserved for the server.
     */
    clone : function () {
        return{
            order : this.order,
            startTick : this.startTick,
            endTick : this.endTick,
            remainingWork : this.remainingWork,
        };
    },

    /**
     * Comparison method reserved for the server.
     */
    compare : function (beforeState) {
        let ret = {};
        if (this.startTick !== beforeState.startTick) ret.startTick = this.startTick;
        if (this.endTick !== beforeState.endTick) ret.endTick = this.endTick;
        if (this.remainingWork !== beforeState.remainingWork) ret.remainingWork = this.remainingWork;
        if (JSON.stringify(this.order) !== JSON.stringify(beforeState.order)) ret.order = this.order;
        if(Object.getOwnPropertyNames(ret).length > 0) return ret;
    },

    /**
     * Applies the effect of the Skill contained in this Order.
     * This method is called by the Timeline when this Activation expires (i.e. remainingWork = 0).
     */
    applyOrder : function () {
        // TODO
    },
};
<|endoftext|>