<commit_before>/**
 * SVG to webfont converter for Grunt
 *
 * @requires fontforge, ttfautohint, eotlitetool.py
 * @author Artem Sapegin (http://sapegin.me)
 */

/*jshint node:true, laxbreak:true, latedef:false */
module.exports = function(grunt) {
	'use strict';

	var fs = require('fs');
	var path = require('path');
	var temp = require('temp');
	var async = grunt.util.async;
	var _ = grunt.util._;

	// @font-face’s src values generation rules
	var fontsSrcs = {
		eot: [
			{
				ext: '.eot'
			},
			{
				ext: '.eot?#iefix',
				format: 'embedded-opentype'
			}
		],
		woff: [
			false,
			{
				ext: '.woff',
				format: 'woff',
				embeddable: true
			},
		],
		ttf: [
			false,
			{
				ext: '.ttf',
				format: 'truetype',
				embeddable: true
			},
		],
		svg: [
			false,
			{
				ext: '.svg?#{fontBaseName}',
				format: 'svg'
			},
		]
	};

	// CSS fileaname prefixes: _icons.scss
	var cssFilePrefixes = {
		sass: '_',
		scss: '_'
	};

	grunt.registerMultiTask('webfont', 'Compile separate SVG files to webfont', function() {
		this.requiresConfig([this.name, this.target, 'src'].join('.'));
		this.requiresConfig([this.name, this.target, 'dest'].join('.'));

		var allDone = this.async();
		var params = this.data;
		var options = params.options || {};

		if (options.skip) {
			allDone();
			return;
		}

		// Source files
		// @todo Check that source files are svg or eps
		var files = this.filesSrc;
		if (!files.length) {
			grunt.log.writeln('Source SVG or EPS files not found.'.grey);
			allDone();
			return;
		}

		// @todo Check that all needed tools installed: fontforge, ttf2eot, ttfautohint, sfnt2woff

		// Options
		var fontBaseName = options.font || 'icons';
		var fontName = fontBaseName;
		var destCss = params.destCss || params.dest;
		var dest = params.dest;
		var relativeFontPath = options.relativeFontPath;
		var addHashes = options.hashes !== false;
		var addLigatures = options.ligatures === true;
		var template = options.template;
		var syntax = options.syntax || 'bem';
		var stylesheet = options.stylesheet || 'css';
		var htmlDemo = options.htmlDemo !== false;
		var htmlDemoTemplate = options.htmlDemoTemplate;
		var destHtml = options.destHtml || destCss;
		var styles = optionToArray(options.styles, 'font,icon');
		var types = optionToArray(options.types, 'woff,ttf,eot,svg');
		var order = optionToArray(options.order, 'eot,woff,ttf,svg');
		var embed = options.embed === true ? ['woff'] : optionToArray(options.embed, false);
		var fontSrcSeparator = stylesheet === 'styl' ? ', ' : ',\n\t\t';
		var rename = options.rename || path.basename;

		var fontfaceStyles = has(styles, 'font');
		var baseStyles = has(styles, 'icon');
		var extraStyles = has(styles, 'extra');

		var glyphs = [];

		// Create output directory
		grunt.file.mkdir(destCss);
		grunt.file.mkdir(dest);

		// Clean output directory
		grunt.file.expand(path.join(destCss, fontBaseName + '*.{' + stylesheet + ',html}'))
			.concat(grunt.file.expand(path.join(dest, fontBaseName + '*.{woff,ttf,eot,svg}')))
			.forEach(function(file) {
				fs.unlinkSync(file);
			});

		// Create temporary directory
		var tempDir = temp.mkdirSync();

		async.waterfall([

			// Copy source files to temporary directory
			function(done) {
				async.forEach(files, function(file, next) {
					grunt.file.copy(file, path.join(tempDir, rename(file)));
					next();
				}, done);
			},

			// Run Fontforge
			function(done) {
				var args = [
					'-script',
					path.join(__dirname, 'scripts/generate.py'),
					tempDir,
					dest,
					fontBaseName,
					types.join(',')
				];
				if (addHashes) {
					args.push('--hashes');
				}
				if (addLigatures) {
					args.push('--ligatures');
				}

				grunt.util.spawn({
					cmd: 'fontforge',
					args: args
				}, function(err, json, code) {
					if (code === 127) {
						grunt.log.errorlns('Please install fontforge and all other requirements.');
						grunt.warn('fontforge not found', code);
					}
					else if (err || json.stderr) {
						// Skip some fontforege output and the "No glyphs" warning because fontforge shows it when font
						// contains only one glyph.
						// @todo There is a problem with "No glyphs" check, because it could be in any language (#38).
						//       Now we skip any warnings ("Warning" is always in English.) But we should find a better way.
						var notError = /(Copyright|License |with many parts BSD |Executable based on sources from|Library based on sources from|Based on source from git|Warning:)/;
						var lines = (err && err.stderr || json.stderr).split('\n');
						// write lines for verbose mode
						var warn = [];
						lines.forEach(function(line) {
							if (!line.match(notError)) {
								warn.push(line);
							}
							else {
								grunt.verbose.writeln("fontforge output ignored: ".grey + line);
							}
						});
						if (warn.length) {
							grunt.warn(warn.join('\n'));
							allDone();
						}
					}

					var result = JSON.parse(json.stdout);
					fontName = path.basename(result.file);
					glyphs = result.names;

					done();
				});
			},

			// Write CSS and HTML
			function(done) {
				// CSS
				if (!relativeFontPath) {
					relativeFontPath = path.relative(destCss, dest);
				}
				relativeFontPath = appendSlash(relativeFontPath);

				// Generate @font-face’s `src` values
				var fontSrc1 = [];
				var fontSrc2 = [];
				order.forEach(function(type) {
					if (!has(types, type)) return;
					var font = fontsSrcs[type];
					if (font[0]) fontSrc1.push(generateFontSrc(type, font[0]));
					if (font[1]) fontSrc2.push(generateFontSrc(type, font[1]));
				});
				fontSrc1 = fontSrc1.join(fontSrcSeparator);
				fontSrc2 = fontSrc2.join(fontSrcSeparator);

				// Prepage glyph names to use as CSS classes
				glyphs = _.map(glyphs, function(name) {
					return name.replace(/ /g, '-');
				});

				var cssContext = {
					fontBaseName: fontBaseName,
					fontName: fontName,
					fontSrc1: fontSrc1,
					fontSrc2: fontSrc2,
					fontfaceStyles: fontfaceStyles,
					baseStyles: baseStyles,
					extraStyles: extraStyles,
					stylesheet: stylesheet,
					iconsStyles: true,
					glyphs: glyphs,
					ligatures: addLigatures
				};

				var cssTemplate = readTemplate(template, syntax, '.css');
				var templateJson = JSON.parse(readTemplate(template, syntax, '.json'));
				var cssFilePrefix = cssFilePrefixes[stylesheet] || '';
				var cssFile = path.join(destCss, cssFilePrefix + fontBaseName + '.' + stylesheet);

				var css = grunt.template.process(cssTemplate, {data: cssContext});

				// Fix CSS preprocessors comments: single line comments will be removed after compilation
				if (has(['sass', 'scss', 'less', 'styl'], stylesheet)) {
					css = css.replace(/\/\* *(.*?) *\*\//g, '// $1');
				}

				grunt.file.write(cssFile, css);

				// Demo HTML
				if (htmlDemo) {
					generateDemoHtml(cssContext, cssTemplate, templateJson, fontSrc1, fontSrc2);
				}

				done();
			},

			// Print log
			function(done) {
				grunt.log.writeln("Font '" + fontName + "' with " + glyphs.length + " glyphs created." );
				done();
			}

		], allDone);

		function generateDemoHtml(baseContext, cssTemplate, templateJson, fontSrc1, fontSrc2) {
			// HTML should not contain relative paths
			// If some styles was not included in CSS we should include them in HTML to properly render icons
			var htmlRelativeFontPath = appendSlash(path.relative(destCss, dest));
			var relativeRe = new RegExp(relativeFontPath, 'g');
			var context = _.extend(baseContext, {
				fontSrc1: fontSrc1.replace(relativeRe, htmlRelativeFontPath),
				fontSrc2: fontSrc2.replace(relativeRe, htmlRelativeFontPath),
				fontfaceStyles: true,
				baseStyles: true,
				extraStyles: false,
				iconsStyles: true,
				stylesheet: 'css'
			});
			var htmlStyles = grunt.template.process(cssTemplate, {data: context});

			var htmlContext = _.extend(context, {
				baseClass: templateJson.baseClass,
				classPrefix: templateJson.classPrefix,
				styles: htmlStyles
			});

			var demoTemplate = readTemplate(htmlDemoTemplate, 'demo', '.html');
			var demoFile = path.join(destHtml, fontBaseName + '.html');

			var demo = grunt.template.process(demoTemplate, {data: htmlContext});

			grunt.file.write(demoFile, demo);
		}

		function optionToArray(val, defVal) {
			if (val === undefined) val = defVal;
			if (!val) return [];
			if (typeof val !== 'string') return val;
			if (val.indexOf(',') !== -1) {
				return val.split(',');
			}
			else {
				return [val];
			}
		}

		function has(haystack, needle) {
			return haystack.indexOf(needle) !== -1;
		}

		// Convert font file to data:uri and *remove* source file.
		function embedFont(fontFile) {
			// Convert to data:uri
			var dataUri = fs.readFileSync(fontFile, 'base64');
			var type = path.extname(fontFile).substring(1);
			var fontUrl = 'data:application/x-font-' + type + ';charset=utf-8;base64,' + dataUri;
			// Remove WOFF file
			fs.unlinkSync(fontFile);

			return fontUrl;
		}

		function appendSlash(filepath) {
			if (filepath.length && filepath[filepath.length-1] !== '/') {
				filepath += '/';
			}
			return filepath;
		}

		function generateFontSrc(type, font) {
			var filename = (fontName + font.ext).replace('{fontBaseName}', fontBaseName);

			var url;
			if (font.embeddable && has(embed, type)) {
				url = embedFont(path.join(dest, filename));
			}
			else {
				url = relativeFontPath + filename;
			}

			var src = 'url("' + url + '")';
			if (font.format) src += ' format("' + font.format + '")';

			return src;
		}

		function readTemplate(template, syntax, ext) {
			if (template) {
				return grunt.file.read(template.replace(/\.css$/, ext));
			}
			else {
				return fs.readFileSync(path.join(__dirname, 'templates/' + syntax + ext), 'utf8');
			}
		}

	});
};
<commit_msg>Use _.dasherize instead of custom function.<commit_after>/**
 * SVG to webfont converter for Grunt
 *
 * @requires fontforge, ttfautohint, eotlitetool.py
 * @author Artem Sapegin (http://sapegin.me)
 */

/*jshint node:true, laxbreak:true, latedef:false */
module.exports = function(grunt) {
	'use strict';

	var fs = require('fs');
	var path = require('path');
	var temp = require('temp');
	var async = grunt.util.async;
	var _ = grunt.util._;

	// @font-face’s src values generation rules
	var fontsSrcs = {
		eot: [
			{
				ext: '.eot'
			},
			{
				ext: '.eot?#iefix',
				format: 'embedded-opentype'
			}
		],
		woff: [
			false,
			{
				ext: '.woff',
				format: 'woff',
				embeddable: true
			},
		],
		ttf: [
			false,
			{
				ext: '.ttf',
				format: 'truetype',
				embeddable: true
			},
		],
		svg: [
			false,
			{
				ext: '.svg?#{fontBaseName}',
				format: 'svg'
			},
		]
	};

	// CSS fileaname prefixes: _icons.scss
	var cssFilePrefixes = {
		sass: '_',
		scss: '_'
	};

	grunt.registerMultiTask('webfont', 'Compile separate SVG files to webfont', function() {
		this.requiresConfig([this.name, this.target, 'src'].join('.'));
		this.requiresConfig([this.name, this.target, 'dest'].join('.'));

		var allDone = this.async();
		var params = this.data;
		var options = params.options || {};

		if (options.skip) {
			allDone();
			return;
		}

		// Source files
		// @todo Check that source files are svg or eps
		var files = this.filesSrc;
		if (!files.length) {
			grunt.log.writeln('Source SVG or EPS files not found.'.grey);
			allDone();
			return;
		}

		// @todo Check that all needed tools installed: fontforge, ttf2eot, ttfautohint, sfnt2woff

		// Options
		var fontBaseName = options.font || 'icons';
		var fontName = fontBaseName;
		var destCss = params.destCss || params.dest;
		var dest = params.dest;
		var relativeFontPath = options.relativeFontPath;
		var addHashes = options.hashes !== false;
		var addLigatures = options.ligatures === true;
		var template = options.template;
		var syntax = options.syntax || 'bem';
		var stylesheet = options.stylesheet || 'css';
		var htmlDemo = options.htmlDemo !== false;
		var htmlDemoTemplate = options.htmlDemoTemplate;
		var destHtml = options.destHtml || destCss;
		var styles = optionToArray(options.styles, 'font,icon');
		var types = optionToArray(options.types, 'woff,ttf,eot,svg');
		var order = optionToArray(options.order, 'eot,woff,ttf,svg');
		var embed = options.embed === true ? ['woff'] : optionToArray(options.embed, false);
		var fontSrcSeparator = stylesheet === 'styl' ? ', ' : ',\n\t\t';
		var rename = options.rename || path.basename;

		var fontfaceStyles = has(styles, 'font');
		var baseStyles = has(styles, 'icon');
		var extraStyles = has(styles, 'extra');

		var glyphs = [];

		// Create output directory
		grunt.file.mkdir(destCss);
		grunt.file.mkdir(dest);

		// Clean output directory
		grunt.file.expand(path.join(destCss, fontBaseName + '*.{' + stylesheet + ',html}'))
			.concat(grunt.file.expand(path.join(dest, fontBaseName + '*.{woff,ttf,eot,svg}')))
			.forEach(function(file) {
				fs.unlinkSync(file);
			});

		// Create temporary directory
		var tempDir = temp.mkdirSync();

		async.waterfall([

			// Copy source files to temporary directory
			function(done) {
				async.forEach(files, function(file, next) {
					grunt.file.copy(file, path.join(tempDir, rename(file)));
					next();
				}, done);
			},

			// Run Fontforge
			function(done) {
				var args = [
					'-script',
					path.join(__dirname, 'scripts/generate.py'),
					tempDir,
					dest,
					fontBaseName,
					types.join(',')
				];
				if (addHashes) {
					args.push('--hashes');
				}
				if (addLigatures) {
					args.push('--ligatures');
				}

				grunt.util.spawn({
					cmd: 'fontforge',
					args: args
				}, function(err, json, code) {
					if (code === 127) {
						grunt.log.errorlns('Please install fontforge and all other requirements.');
						grunt.warn('fontforge not found', code);
					}
					else if (err || json.stderr) {
						// Skip some fontforege output and the "No glyphs" warning because fontforge shows it when font
						// contains only one glyph.
						// @todo There is a problem with "No glyphs" check, because it could be in any language (#38).
						//       Now we skip any warnings ("Warning" is always in English.) But we should find a better way.
						var notError = /(Copyright|License |with many parts BSD |Executable based on sources from|Library based on sources from|Based on source from git|Warning:)/;
						var lines = (err && err.stderr || json.stderr).split('\n');
						// write lines for verbose mode
						var warn = [];
						lines.forEach(function(line) {
							if (!line.match(notError)) {
								warn.push(line);
							}
							else {
								grunt.verbose.writeln("fontforge output ignored: ".grey + line);
							}
						});
						if (warn.length) {
							grunt.warn(warn.join('\n'));
							allDone();
						}
					}

					var result = JSON.parse(json.stdout);
					fontName = path.basename(result.file);
					glyphs = result.names;

					done();
				});
			},

			// Write CSS and HTML
			function(done) {
				// CSS
				if (!relativeFontPath) {
					relativeFontPath = path.relative(destCss, dest);
				}
				relativeFontPath = appendSlash(relativeFontPath);

				// Generate @font-face’s `src` values
				var fontSrc1 = [];
				var fontSrc2 = [];
				order.forEach(function(type) {
					if (!has(types, type)) return;
					var font = fontsSrcs[type];
					if (font[0]) fontSrc1.push(generateFontSrc(type, font[0]));
					if (font[1]) fontSrc2.push(generateFontSrc(type, font[1]));
				});
				fontSrc1 = fontSrc1.join(fontSrcSeparator);
				fontSrc2 = fontSrc2.join(fontSrcSeparator);

				var cssTemplate = readTemplate(template, syntax, '.css');
				var templateJson = JSON.parse(readTemplate(template, syntax, '.json'));
				var cssFilePrefix = cssFilePrefixes[stylesheet] || '';
				var cssFile = path.join(destCss, cssFilePrefix + fontBaseName + '.' + stylesheet);

				// Prepage glyph names to use as CSS classes
				glyphs = _.map(glyphs, _.dasherize);

				var cssContext = {
					fontBaseName: fontBaseName,
					fontName: fontName,
					fontSrc1: fontSrc1,
					fontSrc2: fontSrc2,
					fontfaceStyles: fontfaceStyles,
					baseStyles: baseStyles,
					extraStyles: extraStyles,
					stylesheet: stylesheet,
					iconsStyles: true,
					glyphs: glyphs,
					ligatures: addLigatures
				};

				var css = grunt.template.process(cssTemplate, {data: cssContext});

				// Fix CSS preprocessors comments: single line comments will be removed after compilation
				if (has(['sass', 'scss', 'less', 'styl'], stylesheet)) {
					css = css.replace(/\/\* *(.*?) *\*\//g, '// $1');
				}

				grunt.file.write(cssFile, css);

				// Demo HTML
				if (htmlDemo) {
					generateDemoHtml(cssContext, cssTemplate, templateJson, fontSrc1, fontSrc2);
				}

				done();
			},

			// Print log
			function(done) {
				grunt.log.writeln("Font '" + fontName + "' with " + glyphs.length + " glyphs created." );
				done();
			}

		], allDone);

		function generateDemoHtml(baseContext, cssTemplate, templateJson, fontSrc1, fontSrc2) {
			// HTML should not contain relative paths
			// If some styles was not included in CSS we should include them in HTML to properly render icons
			var htmlRelativeFontPath = appendSlash(path.relative(destCss, dest));
			var relativeRe = new RegExp(relativeFontPath, 'g');
			var context = _.extend(baseContext, {
				fontSrc1: fontSrc1.replace(relativeRe, htmlRelativeFontPath),
				fontSrc2: fontSrc2.replace(relativeRe, htmlRelativeFontPath),
				fontfaceStyles: true,
				baseStyles: true,
				extraStyles: false,
				iconsStyles: true,
				stylesheet: 'css'
			});
			var htmlStyles = grunt.template.process(cssTemplate, {data: context});

			var htmlContext = _.extend(context, {
				baseClass: templateJson.baseClass,
				classPrefix: templateJson.classPrefix,
				styles: htmlStyles
			});

			var demoTemplate = readTemplate(htmlDemoTemplate, 'demo', '.html');
			var demoFile = path.join(destHtml, fontBaseName + '.html');

			var demo = grunt.template.process(demoTemplate, {data: htmlContext});

			grunt.file.write(demoFile, demo);
		}

		function optionToArray(val, defVal) {
			if (val === undefined) val = defVal;
			if (!val) return [];
			if (typeof val !== 'string') return val;
			if (val.indexOf(',') !== -1) {
				return val.split(',');
			}
			else {
				return [val];
			}
		}

		function has(haystack, needle) {
			return haystack.indexOf(needle) !== -1;
		}

		// Convert font file to data:uri and *remove* source file.
		function embedFont(fontFile) {
			// Convert to data:uri
			var dataUri = fs.readFileSync(fontFile, 'base64');
			var type = path.extname(fontFile).substring(1);
			var fontUrl = 'data:application/x-font-' + type + ';charset=utf-8;base64,' + dataUri;
			// Remove WOFF file
			fs.unlinkSync(fontFile);

			return fontUrl;
		}

		function appendSlash(filepath) {
			if (filepath.length && filepath[filepath.length-1] !== '/') {
				filepath += '/';
			}
			return filepath;
		}

		function generateFontSrc(type, font) {
			var filename = (fontName + font.ext).replace('{fontBaseName}', fontBaseName);

			var url;
			if (font.embeddable && has(embed, type)) {
				url = embedFont(path.join(dest, filename));
			}
			else {
				url = relativeFontPath + filename;
			}

			var src = 'url("' + url + '")';
			if (font.format) src += ' format("' + font.format + '")';

			return src;
		}

		function readTemplate(template, syntax, ext) {
			if (template) {
				return grunt.file.read(template.replace(/\.css$/, ext));
			}
			else {
				return fs.readFileSync(path.join(__dirname, 'templates/' + syntax + ext), 'utf8');
			}
		}

	});
};
<|endoftext|>


<commit_before>'use strict';

/*!
 * Module dependencies.
 */

var slice = function (arr, start, end) { return Array.prototype.slice.call(arr, start, end) };
var EventEmitter = require('events').EventEmitter;

/**
 * compat with node 0.10
 */

var next = process.nextTick;

/**
 * Promise constructor.
 *
 * _NOTE: The success and failure event names can be overridden by setting `Promise.SUCCESS` and `Promise.FAILURE` respectively._
 *
 * @param {Function} back a function that accepts `fn(err, ...){}` as signature
 * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
 * @event `reject`: Emits when the promise is rejected (event name may be overridden)
 * @event `fulfill`: Emits when the promise is fulfilled (event name may be overridden)
 * @api public
 */

function Promise (back) {
  EventEmitter.call(this);

  this.emitted = {};
  this.ended = false;
  if ('function' == typeof back)
    this.onResolve(back);
}

/*!
 * event names
 */

Promise.SUCCESS = 'fulfill';
Promise.FAILURE = 'reject';

/*!
 * Inherits from EventEmitter.
 */

Promise.prototype.__proto__ = EventEmitter.prototype;

/**
 * Adds `listener` to the `event`.
 *
 * If `event` is either the success or failure event and the event has already been emitted, the`listener` is called immediately and passed the results of the original emitted event.
 *
 * @param {String} event
 * @param {Function} callback
 * @return {Promise} this
 * @api public
 */

Promise.prototype.on = function (event, callback) {
  if (this.emitted[event])
    callback.apply(this, this.emitted[event]);
  else
    EventEmitter.prototype.on.call(this, event, callback);

  return this;
}

/**
 * Keeps track of emitted events to run them on `on`.
 *
 * @api private
 */

Promise.prototype.emit = function (event) {
  // ensures a promise can't be fulfill() or reject() more than once
  var success = this.constructor.SUCCESS;
  var failure = this.constructor.FAILURE;

  if (event == success || event == failure) {
    if (this.emitted[success] || this.emitted[failure]) {
      return this;
    }
    this.emitted[event] = slice(arguments, 1);
  }

  return EventEmitter.prototype.emit.apply(this, arguments);
}

/**
 * Fulfills this promise with passed arguments.
 *
 * If this promise has already been fulfilled or rejected, no action is taken.
 *
 * @api public
 */

Promise.prototype.fulfill = function () {
  var args = slice(arguments);
  return this.emit.apply(this, [this.constructor.SUCCESS].concat(args));
}

/**
 * Rejects this promise with `reason`.
 *
 * If this promise has already been fulfilled or rejected, no action is taken.
 *
 * @api public
 * @param {Object|String} reason
 * @return {Promise} this
 */

Promise.prototype.reject = function (reason) {
  return this.emit(this.constructor.FAILURE, reason);
}

/**
 * Resolves this promise to a rejected state if `err` is passed or
 * fulfilled state if no `err` is passed.
 *
 * @param {Error} [err] error or null
 * @param {Object} [val] value to fulfill the promise with
 * @api public
 */

Promise.prototype.resolve = function (err, val) {
  if (err) return this.reject(err);
  return this.fulfill(val);
}

/**
 * Adds a listener to the SUCCESS event.
 *
 * @return {Promise} this
 * @api public
 */

Promise.prototype.onFulfill = function (fn) {
  if (!fn || !fn.call || !fn.apply) return this;
  if (!this.listeners(this.constructor.SUCCESS).length) {
    this.fulfill = fn;
  } else {
    this.fulfill = Promise.prototype.fulfill;
  }
  return this.on(this.constructor.SUCCESS, fn);
}

/**
 * Adds a listener to the FAILURE event.
 *
 * @return {Promise} this
 * @api public
 */

Promise.prototype.onReject = function (fn) {
  if (!fn || !fn.call || !fn.apply) return this;
  if (!this.listeners(this.constructor.FAILURE).length) {
    this.reject = fn;
  } else {
    this.reject = Promise.prototype.reject;
  }
  return this.on(this.constructor.FAILURE, fn);
}

/**
 * Adds a single function as a listener to both SUCCESS and FAILURE.
 *
 * It will be executed with traditional node.js argument position:
 * function (err, args...) {}
 *
 * @param {Function} fn
 * @return {Promise} this
 */

Promise.prototype.onResolve = function (fn) {
  if (!fn || !fn.call || !fn.apply) return this;
  if (!this.listeners(this.constructor.FAILURE).length && !this.listeners(this.constructor.SUCCESS).length) {
    this.fulfill = function () {var args = [].slice.call(arguments); args.unshift(null); fn.apply(this, args)};
    this.reject = fn;
  } else {
    this.fulfill = Promise.prototype.fulfill;
    this.reject = Promise.prototype.reject;
  }

  this.on(this.constructor.FAILURE, function (err) {
    fn.apply(this, [err]);
  });

  this.on(this.constructor.SUCCESS, function(){
    var args = slice(arguments);
    fn.apply(this, [null].concat(args));
  });

  return this;
}

/**
 * Creates a new promise and returns it. If `onFulfill` or
 * `onReject` are passed, they are added as SUCCESS/ERROR callbacks
 * to this promise after the next tick.
 *
 * Conforms to [promises/A+](https://github.com/promises-aplus/promises-spec) specification. Read for more detail how to use this method.
 *
 * ####Example:
 *
 *     var p = new Promise;
 *     p.then(function (arg) {
 *       return arg + 1;
 *     }).then(function (arg) {
 *       throw new Error(arg + ' is an error!');
 *     }).then(null, function (err) {
 *       assert.ok(err instanceof Error);
 *       assert.equal('2 is an error', err.message);
 *     });
 *     p.complete(1);
 *
 * @see promises-A+ https://github.com/promises-aplus/promises-spec
 * @param {Function} onFulFill
 * @param {Function} onReject
 * @return {Promise} newPromise
 */

Promise.prototype.then = function (onFulfill, onReject) {
  var self = this
    , retPromise = new Promise;

  if ('function' == typeof onReject) {
    self.onReject(handler(retPromise, onReject));
  } else {
    self.onReject(retPromise.reject.bind(retPromise));
  }
  if ('function' == typeof onFulfill) {
    self.onFulfill(handler(retPromise, onFulfill));
  } else {
    self.onFulfill(retPromise.fulfill.bind(retPromise));
  }

  return retPromise;
}

function handler (retPromise, fn) {
  return function handle () {
    var args = arguments;
    next(function () {
      var x;

      try {
        x = fn.apply(undefined, args);
      } catch (err) {
        return retPromise.reject(err);
      }

      resolve(retPromise, x);
    });
  }
}

function resolve (promise, x) {
  var then;
  var type;
  var done;
  var reject_;
  var resolve_;

  type = typeof x;
  if ('undefined' == type) {
    return promise.fulfill(x);
  }

  if (promise === x) {
    return promise.reject(new TypeError("promise and x are the same"));
  }

  if (null != x) {

    if ('object' == type || 'function' == type) {
      try {
        then = x.then;
      } catch (err) {
        if (promise.ended) throw err;
        return promise.reject(err);
      }

      if ('function' == typeof then) {
        try {
          resolve_ = resolve.bind(this, promise);
          reject_ = promise.reject.bind(promise);
          done = false;
          return then.call(
              x
            , function fulfill () {
                if (done) return;
                done = true;
                return resolve_.apply(this, arguments);
              }
            , function reject () {
                if (done) return;
                done = true;
                return reject_.apply(this, arguments);
              })
        } catch (err) {
          if (done) return;
          done = true;
          if (promise.ended) throw err;
          return promise.reject(err);
        }
      }
    }
  }

  promise.fulfill(x);
}

/**
 * Signifies that this promise was the last in a chain of `then()s`: if a handler passed to the call to `then` which produced this promise throws, the exception will go uncaught.
 *
 * ####Example:
 *
 *     var p = new Promise;
 *     p.then(function(){ throw new Error('shucks') });
 *     setTimeout(function () {
 *       p.fulfill();
 *       // error was caught and swallowed by the promise returned from
 *       // p.then(). we either have to always register handlers on
 *       // the returned promises or we can do the following...
 *     }, 10);
 *
 *     // this time we use .end() which prevents catching thrown errors
 *     var p = new Promise;
 *     var p2 = p.then(function(){ throw new Error('shucks') }).end(); // <--
 *     setTimeout(function () {
 *       p.fulfill(); // throws "shucks"
 *     }, 10);
 *
 * @api public
 */

Promise.prototype.end = function () {
  this.ended = true;
}

/*!
 * Module exports.
 */

module.exports = Promise;
<commit_msg>Stricter type-check on ```fn```<commit_after>'use strict';

/*!
 * Module dependencies.
 */

var slice = function (arr, start, end) { return Array.prototype.slice.call(arr, start, end) };
var EventEmitter = require('events').EventEmitter;

/**
 * compat with node 0.10
 */

var next = process.nextTick;

/**
 * Promise constructor.
 *
 * _NOTE: The success and failure event names can be overridden by setting `Promise.SUCCESS` and `Promise.FAILURE` respectively._
 *
 * @param {Function} back a function that accepts `fn(err, ...){}` as signature
 * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
 * @event `reject`: Emits when the promise is rejected (event name may be overridden)
 * @event `fulfill`: Emits when the promise is fulfilled (event name may be overridden)
 * @api public
 */

function Promise (back) {
  EventEmitter.call(this);

  this.emitted = {};
  this.ended = false;
  if ('function' == typeof back)
    this.onResolve(back);
}

/*!
 * event names
 */

Promise.SUCCESS = 'fulfill';
Promise.FAILURE = 'reject';

/*!
 * Inherits from EventEmitter.
 */

Promise.prototype.__proto__ = EventEmitter.prototype;

/**
 * Adds `listener` to the `event`.
 *
 * If `event` is either the success or failure event and the event has already been emitted, the`listener` is called immediately and passed the results of the original emitted event.
 *
 * @param {String} event
 * @param {Function} callback
 * @return {Promise} this
 * @api public
 */

Promise.prototype.on = function (event, callback) {
  if (this.emitted[event])
    callback.apply(this, this.emitted[event]);
  else
    EventEmitter.prototype.on.call(this, event, callback);

  return this;
}

/**
 * Keeps track of emitted events to run them on `on`.
 *
 * @api private
 */

Promise.prototype.emit = function (event) {
  // ensures a promise can't be fulfill() or reject() more than once
  var success = this.constructor.SUCCESS;
  var failure = this.constructor.FAILURE;

  if (event == success || event == failure) {
    if (this.emitted[success] || this.emitted[failure]) {
      return this;
    }
    this.emitted[event] = slice(arguments, 1);
  }

  return EventEmitter.prototype.emit.apply(this, arguments);
}

/**
 * Fulfills this promise with passed arguments.
 *
 * If this promise has already been fulfilled or rejected, no action is taken.
 *
 * @api public
 */

Promise.prototype.fulfill = function () {
  var args = slice(arguments);
  return this.emit.apply(this, [this.constructor.SUCCESS].concat(args));
}

/**
 * Rejects this promise with `reason`.
 *
 * If this promise has already been fulfilled or rejected, no action is taken.
 *
 * @api public
 * @param {Object|String} reason
 * @return {Promise} this
 */

Promise.prototype.reject = function (reason) {
  return this.emit(this.constructor.FAILURE, reason);
}

/**
 * Resolves this promise to a rejected state if `err` is passed or
 * fulfilled state if no `err` is passed.
 *
 * @param {Error} [err] error or null
 * @param {Object} [val] value to fulfill the promise with
 * @api public
 */

Promise.prototype.resolve = function (err, val) {
  if (err) return this.reject(err);
  return this.fulfill(val);
}

/**
 * Adds a listener to the SUCCESS event.
 *
 * @return {Promise} this
 * @api public
 */

Promise.prototype.onFulfill = function (fn) {
  if (!fn) return this;
  if ('function' != typeof fn) throw new TypeError("fn should be a function");
  if (!this.listeners(this.constructor.SUCCESS).length) {
    this.fulfill = fn;
  } else {
    this.fulfill = Promise.prototype.fulfill;
  }
  return this.on(this.constructor.SUCCESS, fn);
}

/**
 * Adds a listener to the FAILURE event.
 *
 * @return {Promise} this
 * @api public
 */

Promise.prototype.onReject = function (fn) {
  if (!fn) return this;
  if ('function' != typeof fn) throw new TypeError("fn should be a function");
  if (!this.listeners(this.constructor.FAILURE).length) {
    this.reject = fn;
  } else {
    this.reject = Promise.prototype.reject;
  }
  return this.on(this.constructor.FAILURE, fn);
}

/**
 * Adds a single function as a listener to both SUCCESS and FAILURE.
 *
 * It will be executed with traditional node.js argument position:
 * function (err, args...) {}
 *
 * @param {Function} fn
 * @return {Promise} this
 */

Promise.prototype.onResolve = function (fn) {
  if (!fn) return this;
  if ('function' != typeof fn) throw new TypeError("fn should be a function");
  if (!this.listeners(this.constructor.FAILURE).length && !this.listeners(this.constructor.SUCCESS).length) {
    this.fulfill = function () {var args = [].slice.call(arguments); args.unshift(null); fn.apply(this, args)};
    this.reject = fn;
  } else {
    this.fulfill = Promise.prototype.fulfill;
    this.reject = Promise.prototype.reject;
  }

  this.on(this.constructor.FAILURE, function (err) {
    fn.apply(this, [err]);
  });

  this.on(this.constructor.SUCCESS, function(){
    var args = slice(arguments);
    fn.apply(this, [null].concat(args));
  });

  return this;
}

/**
 * Creates a new promise and returns it. If `onFulfill` or
 * `onReject` are passed, they are added as SUCCESS/ERROR callbacks
 * to this promise after the next tick.
 *
 * Conforms to [promises/A+](https://github.com/promises-aplus/promises-spec) specification. Read for more detail how to use this method.
 *
 * ####Example:
 *
 *     var p = new Promise;
 *     p.then(function (arg) {
 *       return arg + 1;
 *     }).then(function (arg) {
 *       throw new Error(arg + ' is an error!');
 *     }).then(null, function (err) {
 *       assert.ok(err instanceof Error);
 *       assert.equal('2 is an error', err.message);
 *     });
 *     p.complete(1);
 *
 * @see promises-A+ https://github.com/promises-aplus/promises-spec
 * @param {Function} onFulFill
 * @param {Function} onReject
 * @return {Promise} newPromise
 */

Promise.prototype.then = function (onFulfill, onReject) {
  var self = this
    , retPromise = new Promise;

  if ('function' == typeof onReject) {
    self.onReject(handler(retPromise, onReject));
  } else {
    self.onReject(retPromise.reject.bind(retPromise));
  }
  if ('function' == typeof onFulfill) {
    self.onFulfill(handler(retPromise, onFulfill));
  } else {
    self.onFulfill(retPromise.fulfill.bind(retPromise));
  }

  return retPromise;
}

function handler (retPromise, fn) {
  return function handle () {
    var args = arguments;
    next(function () {
      var x;

      try {
        x = fn.apply(undefined, args);
      } catch (err) {
        return retPromise.reject(err);
      }

      resolve(retPromise, x);
    });
  }
}

function resolve (promise, x) {
  var then;
  var type;
  var done;
  var reject_;
  var resolve_;

  type = typeof x;
  if ('undefined' == type) {
    return promise.fulfill(x);
  }

  if (promise === x) {
    return promise.reject(new TypeError("promise and x are the same"));
  }

  if (null != x) {

    if ('object' == type || 'function' == type) {
      try {
        then = x.then;
      } catch (err) {
        if (promise.ended) throw err;
        return promise.reject(err);
      }

      if ('function' == typeof then) {
        try {
          resolve_ = resolve.bind(this, promise);
          reject_ = promise.reject.bind(promise);
          done = false;
          return then.call(
              x
            , function fulfill () {
                if (done) return;
                done = true;
                return resolve_.apply(this, arguments);
              }
            , function reject () {
                if (done) return;
                done = true;
                return reject_.apply(this, arguments);
              })
        } catch (err) {
          if (done) return;
          done = true;
          if (promise.ended) throw err;
          return promise.reject(err);
        }
      }
    }
  }

  promise.fulfill(x);
}

/**
 * Signifies that this promise was the last in a chain of `then()s`: if a handler passed to the call to `then` which produced this promise throws, the exception will go uncaught.
 *
 * ####Example:
 *
 *     var p = new Promise;
 *     p.then(function(){ throw new Error('shucks') });
 *     setTimeout(function () {
 *       p.fulfill();
 *       // error was caught and swallowed by the promise returned from
 *       // p.then(). we either have to always register handlers on
 *       // the returned promises or we can do the following...
 *     }, 10);
 *
 *     // this time we use .end() which prevents catching thrown errors
 *     var p = new Promise;
 *     var p2 = p.then(function(){ throw new Error('shucks') }).end(); // <--
 *     setTimeout(function () {
 *       p.fulfill(); // throws "shucks"
 *     }, 10);
 *
 * @api public
 */

Promise.prototype.end = function () {
  this.ended = true;
}

/*!
 * Module exports.
 */

module.exports = Promise;
<|endoftext|>


<commit_before>/*
 * injects download buttons into the webpage
 */
function injectDownloadButtons(subjects) {

  // adds a download button that only downloads an array of selected objects
  var downloadSelected = document.createElement('input');
  downloadSelected.type = 'button';
  downloadSelected.id = 'downloadSelected';
  downloadSelected.className = 'downloadButton primary-btn';
  downloadSelected.value = 'Download Selected';

  var box = $('#downloadArea')[0];
  box.appendChild(downloadSelected);

  $("#downloadSelected").on('click', function(entryInfo){
    downloadSelection();
  });

  // adds a download button that downloads an array of all objects the plugin could find on the page
  var downloadAll = document.createElement('input');
  downloadAll.type = 'button';
  downloadAll.id = 'downloadAll';
  downloadAll.className = 'downloadButton default-btn';
  downloadAll.value = 'Download All';

  var box = $('#downloadArea')[0];
  box.appendChild(downloadAll);

  $("#downloadAll").on('click', function(entryInfo){
    if (subjects.length > 0) {
      try {
        downloadAll(subjects);
      } catch(e) {
        toastr.error("Download failed!", e);
      }
    } else {
      toastr.warning("Keine Veranstaltungen gefunden.")
    };
  });
}

/*
 * injects buttons to add subjects to an array, like a shopping cart
 */
function injectAddButtons(subjects) {
  for (var i = 0; i < subjects.length; i++){
    var object = subjects[i];
    var name = subjects[i].childNodes[1].childNodes[0].data;
    subjects[i].childNodes[1].childNodes[0].data = null;
    var r = $('<button class="addButton">' + name + '</button> &nbsp');
    r.insertBefore(subjects[i].childNodes[1].childNodes[0]);
  }

  $(".addButton").on('click', function(){
    $(this).toggleClass('active');
    var object = this.parentNode.parentNode;
    var selection = load('selection');
    if(!containsObject(getNameOfLecture(object), selection)) {
      addToCart(object);
    } else {
      removeFromCart(object);
    }
  });
}

 //

function addNewButton() {
  var objects = document.getElementsByClassName("stupla_fs09");

  var zeile = objects[8].parentNode;
  console.log(zeile);

  for (i = 0; i < 2; i++) {
    var button = $('<button class="downloadButton">Download</button>');
    button.insertBefore(objects[i+7].parentNode.childNodes[14]);
  }
}
<commit_msg>closes#61<commit_after>/*
 * injects download buttons into the webpage
 */
function injectDownloadButtons(subjects) {

  // adds a download button that only downloads an array of selected objects
  var downloadSelected = document.createElement('input');
  downloadSelected.type = 'button';
  downloadSelected.id = 'downloadSelected';
  downloadSelected.className = 'downloadButton primary-btn';
  downloadSelected.value = 'Download Selected';

  var box = $('#downloadArea')[0];
  box.appendChild(downloadSelected);

  $("#downloadSelected").on('click', function(entryInfo){
    downloadSelection();
  });

  // adds a download button that downloads an array of all objects the plugin could find on the page
  var downloadAll = document.createElement('input');
  downloadAll.type = 'button';
  downloadAll.id = 'downloadAll';
  downloadAll.className = 'downloadButton default-btn';
  downloadAll.value = 'Select All';

  var box = $('#downloadArea')[0];
  box.appendChild(downloadAll);

  $("#downloadAll").on('click', function(entryInfo){
    if (subjects.length > 0) {
      try {
        downloadAll(subjects);
      } catch(e) {
        toastr.error("Download failed!", e);
      }
    } else {
      toastr.warning("Keine Veranstaltungen gefunden.")
    };
  });
}

/*
 * injects buttons to add subjects to an array, like a shopping cart
 */
function injectAddButtons(subjects) {
  for (var i = 0; i < subjects.length; i++){
    var object = subjects[i];
    var name = subjects[i].childNodes[1].childNodes[0].data;
    subjects[i].childNodes[1].childNodes[0].data = null;
    var r = $('<button class="addButton">' + name + '</button> &nbsp');
    r.insertBefore(subjects[i].childNodes[1].childNodes[0]);
  }

  $(".addButton").on('click', function(){
    $(this).toggleClass('active');
    var object = this.parentNode.parentNode;
    var selection = load('selection');
    if(!containsObject(getNameOfLecture(object), selection)) {
      addToCart(object);
    } else {
      removeFromCart(object);
    }
  });
}

 //

function addNewButton() {
  var objects = document.getElementsByClassName("stupla_fs09");

  var zeile = objects[8].parentNode;
  console.log(zeile);

  for (i = 0; i < 2; i++) {
    var button = $('<button class="downloadButton">Download</button>');
    button.insertBefore(objects[i+7].parentNode.childNodes[14]);
  }
}
<|endoftext|>


<commit_before>let leafCount = 0
let itemCount = 0

const isInBounds = (item, bounds) => {
	if (item.x >= bounds.left &&
		item.x < bounds.right &&
		item.y >= bounds.top &&
		item.y < bounds.bottom) {
		return true
	}

	return false
}

const shuffle = (items, leaves) => {
	items.forEach(subitem => {
		leaves.forEach(subleaf => {
			if (isInBounds(subitem, subleaf.bounds)) {
				subleaf.addItem(subitem)

				return false
			}
		})
	})
}

const place = (item, leaves) => {
	let subleaf

	for (let i = 0; i < 4; i += 1) {
		subleaf = leaves[i]

		if (isInBounds(item, subleaf.bounds)) {
			return subleaf.addItem(item)
		}
	}
}

const validLeaf = props => {
	if (props === undefined) {
		return false
	}

	if (!props.bounds) {
		return false
	}

	if (Reflect.has(props, 'bounds')) {
		if (!Reflect.has(props.bounds, 'top') ||
			!Reflect.has(props.bounds, 'left') ||
			!Reflect.has(props.bounds, 'bottom') ||
			!Reflect.has(props.bounds, 'right')) {
			return false
		}
	}

	return true
}

const validItem = item => {
	if (item === undefined) {
		return false
	}

	if ((typeof item).toString() === 'object') {
		if (!Reflect.has(item, 'x') ||
			!Reflect.has(item, 'y') ||
			!Reflect.has(item, 'val')) {
			return false
		}
	}

	return true
}

const constructItem = (item, leaf) => {
	itemCount += 1

	item.uid = itemCount
	item.leaf = leaf
	item.index = leaf.items.push(item) - 1

	item.remove = () => {
		leaf.items.splice(leaf.index, 1)
		leaf.parent.collapse()
	}

	return item
}

const constructLeaf = props => {
	if (!validLeaf(props)) {
		return undefined
	}

	leafCount += 1

	const leaf = {
		bounds: props.bounds,
		depth: props.depth + 1 || 0,
		uid: leafCount,
		leaves: [],
		items: [],
		parent: props.parent || {root: true}
	}

	leaf.getItems = () => {
		if (leaf.items.length > 0) {
			return leaf.items
		}

		if (leaf.leaves.length > 0) {
			return leaf.leaves[0].getItems()
				.concat(
					leaf.leaves[1].getItems(),
					leaf.leaves[2].getItems(),
					leaf.leaves[3].getItems())
		}

		return []
	}

	leaf.getUnEmptyLeaves = () => {
		if (leaf.items.length > 0) {
			return [leaf]
		}

		if (leaf.leaves.length > 0) {
			return leaf.leaves[0].getUnEmptyLeaves()
				.concat(
					leaf.leaves[1].getUnEmptyLeaves(),
					leaf.leaves[2].getUnEmptyLeaves(),
					leaf.leaves[3].getUnEmptyLeaves())
		}

		return []
	}

	leaf.addItem = item => {
		if (!validItem(item)) {
			return undefined
		}

		if (leaf.leaves.length > 0) {
			return place(item, leaf.leaves)
		}

		if (leaf.items.length < 4) {
			return constructItem(item, leaf)
		}

		if (leaf.items.length === 4) {
			split(leaf)
			shuffle(leaf.items, leaf.leaves)

			leaf.items = []
			return place(item, leaf.leaves)
		}
	}

	leaf.collapse = () => {
		const subitems = leaf.getItems()
		leaf.leaves = []

		for (let i = 0, l = subitems.length; i < l; i++) {
			leaf.addItem(subitems[i])
		}
	}

	return leaf
}

const split = leaf => {
	const midX = leaf.bounds.left +
		((leaf.bounds.right - leaf.bounds.left) / 2)
	const midY = leaf.bounds.top +
		((leaf.bounds.bottom - leaf.bounds.top) / 2)

	const subBoundsTopLeft = {
		top: leaf.bounds.top,
		left: leaf.bounds.left,
		right: midX,
		bottom: midY
	}

	const subBoundsTopRight = {
		top: leaf.bounds.top,
		left: midX,
		right: leaf.bounds.right,
		bottom: midY
	}

	const subBoundsBottomRight = {
		top: midY,
		left: midX,
		right: leaf.bounds.right,
		bottom: leaf.bounds.bottom
	}

	const subBoundsBottomLeft = {
		top: midY,
		left: leaf.bounds.left,
		right: midX,
		bottom: leaf.bounds.bottom
	}

	leaf.leaves.push(
		constructLeaf({
			bounds: subBoundsTopLeft,
			depth: leaf.depth, parent: leaf
		}),

		constructLeaf({
			bounds: subBoundsTopRight,
			depth: leaf.depth, parent: leaf
		}),

		constructLeaf({
			bounds: subBoundsBottomRight,
			depth: leaf.depth, parent: leaf
		}),

		constructLeaf({
			bounds: subBoundsBottomLeft,
			depth: leaf.depth, parent: leaf
		})
	)
}

// Constructor export depends on environment
if (typeof module === 'undefined') {
	window.Leaf = constructLeaf
} else {
	module.exports = constructLeaf
}
<commit_msg>Comments and spacing.<commit_after>/**
 * Carlton Quadtree
 *  - A Quad Tree implementation in ESNext
 *  - Alistair MacDonald (f1lt3r)
 *  - MIT License
 */

let leafCount = 0
let itemCount = 0

const isInBounds = (item, bounds) => {
	if (item.x >= bounds.left &&
		item.x < bounds.right &&
		item.y >= bounds.top &&
		item.y < bounds.bottom) {
		return true
	}

	return false
}

const shuffle = (items, leaves) => {
	items.forEach(subitem => {
		leaves.forEach(subleaf => {
			if (isInBounds(subitem, subleaf.bounds)) {
				subleaf.addItem(subitem)

				return false
			}
		})
	})
}

const place = (item, leaves) => {
	let subleaf

	for (let i = 0; i < 4; i += 1) {
		subleaf = leaves[i]

		if (isInBounds(item, subleaf.bounds)) {
			return subleaf.addItem(item)
		}
	}
}

const validLeaf = props => {
	if (props === undefined) {
		return false
	}

	if (!props.bounds) {
		return false
	}

	if (Reflect.has(props, 'bounds')) {
		if (!Reflect.has(props.bounds, 'top') ||
			!Reflect.has(props.bounds, 'left') ||
			!Reflect.has(props.bounds, 'bottom') ||
			!Reflect.has(props.bounds, 'right')) {
			return false
		}
	}

	return true
}

const validItem = item => {
	if (item === undefined) {
		return false
	}

	if ((typeof item).toString() === 'object') {
		if (!Reflect.has(item, 'x') ||
			!Reflect.has(item, 'y') ||
			!Reflect.has(item, 'val')) {
			return false
		}
	}

	return true
}

const constructItem = (item, leaf) => {
	itemCount += 1

	item.uid = itemCount
	item.leaf = leaf
	item.index = leaf.items.push(item) - 1

	item.remove = () => {
		leaf.items.splice(leaf.index, 1)
		leaf.parent.collapse()
	}

	return item
}

const constructLeaf = props => {
	if (!validLeaf(props)) {
		return undefined
	}

	leafCount += 1

	const leaf = {
		bounds: props.bounds,
		depth: props.depth + 1 || 0,
		uid: leafCount,
		leaves: [],
		items: [],
		parent: props.parent || {root: true}
	}

	leaf.getItems = () => {
		if (leaf.items.length > 0) {
			return leaf.items
		}

		if (leaf.leaves.length > 0) {
			return leaf.leaves[0].getItems()
				.concat(
					leaf.leaves[1].getItems(),
					leaf.leaves[2].getItems(),
					leaf.leaves[3].getItems())
		}

		return []
	}

	leaf.getUnEmptyLeaves = () => {
		if (leaf.items.length > 0) {
			return [leaf]
		}

		if (leaf.leaves.length > 0) {
			return leaf.leaves[0].getUnEmptyLeaves()
				.concat(
					leaf.leaves[1].getUnEmptyLeaves(),
					leaf.leaves[2].getUnEmptyLeaves(),
					leaf.leaves[3].getUnEmptyLeaves())
		}

		return []
	}

	leaf.addItem = item => {
		if (!validItem(item)) {
			return undefined
		}

		if (leaf.leaves.length > 0) {
			return place(item, leaf.leaves)
		}

		if (leaf.items.length < 4) {
			return constructItem(item, leaf)
		}

		if (leaf.items.length === 4) {
			split(leaf)
			shuffle(leaf.items, leaf.leaves)

			leaf.items = []
			return place(item, leaf.leaves)
		}
	}

	leaf.collapse = () => {
		const subitems = leaf.getItems()
		leaf.leaves = []

		for (let i = 0, l = subitems.length; i < l; i++) {
			leaf.addItem(subitems[i])
		}
	}

	return leaf
}

const split = leaf => {
	const midX = leaf.bounds.left +
		((leaf.bounds.right - leaf.bounds.left) / 2)

	const midY = leaf.bounds.top +
		((leaf.bounds.bottom - leaf.bounds.top) / 2)

	const subBoundsTopLeft = {
		top: leaf.bounds.top,
		left: leaf.bounds.left,
		right: midX,
		bottom: midY
	}

	const subBoundsTopRight = {
		top: leaf.bounds.top,
		left: midX,
		right: leaf.bounds.right,
		bottom: midY
	}

	const subBoundsBottomRight = {
		top: midY,
		left: midX,
		right: leaf.bounds.right,
		bottom: leaf.bounds.bottom
	}

	const subBoundsBottomLeft = {
		top: midY,
		left: leaf.bounds.left,
		right: midX,
		bottom: leaf.bounds.bottom
	}

	leaf.leaves.push(
		constructLeaf({
			bounds: subBoundsTopLeft,
			depth: leaf.depth, parent: leaf
		}),

		constructLeaf({
			bounds: subBoundsTopRight,
			depth: leaf.depth, parent: leaf
		}),

		constructLeaf({
			bounds: subBoundsBottomRight,
			depth: leaf.depth, parent: leaf
		}),

		constructLeaf({
			bounds: subBoundsBottomLeft,
			depth: leaf.depth, parent: leaf
		})
	)
}

// Constructor export depends on environment
if (typeof module === 'undefined') {
	window.Leaf = constructLeaf
} else {
	module.exports = constructLeaf
}
<|endoftext|>


<commit_before>var gulp = require('gulp'),
	plugins = require('gulp-load-plugins')(),
	webpack = require('webpack'),
	notifier = require('node-notifier'),
	path = require('path');

var webpackConfig = {
	entry: ['./application/main.js'],
	output: {
		path: __dirname,
		pathinfo: true,
		filename: '../temp/application.js'
	},
	resolve: {
		modulesDirectories: [
			'libs',
			'application',
			'node_modules',
			'temp'
		],
		alias: {
			config: path.resolve('./config.js')
		}
	},
	module: {
		loaders: [
			{ test: /\.tpl$/, loader: require.resolve('./template-loader.js') }
		]
	}
};

var webpackCallback = function(err, stats) {
	if(err) {
		throw new plugins.util.PluginError('[webpack]', err);
	} else {
		if(stats.hasErrors() || stats.hasWarnings()) {
			try {
				var errors;

				if(stats.compilation.errors[0]) {
					errors = stats.compilation.errors[0];
				}

				if(stats.compilation.warnings[0]) {
					errors = stats.compilation.warnings[0];
				}

				var message = [
					errors.module.rawRequest,
					errors.error.toString()
				].join('\n');

				notifier.notify({
					title: 'Boiler error',
					message: message
				});
			} catch(err) {}
		}
		plugins.util.log('[webpack]', stats.toString({colors: true, chunks: false}));
	}
};

gulp.task('webpack', function() {
	webpackConfig.devtool = 'eval';
	webpackConfig.watch = true;

	webpack(webpackConfig, webpackCallback);
});

gulp.task('webpack-build', function() {
	webpackConfig.devtool = 'source-map';

	webpack(webpackConfig, webpackCallback);
});
<commit_msg>Update webpack.js<commit_after>var gulp = require('gulp'),
	plugins = require('gulp-load-plugins')(),
	webpack = require('webpack'),
	notifier = require('node-notifier'),
	path = require('path');

var webpackConfig = {
	entry: ['./application/main.js'],
	output: {
		path: __dirname,
		pathinfo: true,
		filename: '../temp/application.js'
	},
	resolve: {
		modulesDirectories: [
			'libs',
			'application',
			'node_modules',
			'temp'
		],
		alias: {
			config: path.resolve('./config.js')
		}
	},
	module: {
		loaders: [
			{ test: /\.tpl$/, loader: require.resolve('./template-loader.js') }
		]
	}
};

var webpackCallback = function(err, stats) {
	if(err) {
		throw new plugins.util.PluginError('[webpack]', err);
	} else {
		if(stats.hasErrors() || stats.hasWarnings()) {
			try {
				var errors;

				if(stats.compilation.errors[0]) {
					errors = stats.compilation.errors[0];
				}

				if(stats.compilation.warnings[0]) {
					errors = stats.compilation.warnings[0];
				}

				var message = [
					errors.module.rawRequest,
					errors.error.toString()
				].join('\n');

				notifier.notify({
					title: 'Boiler error',
					message: message
				});
			} catch(err) {}
		}
		plugins.util.log('[webpack]', stats.toString({colors: true, chunks: false}));
	}
};

gulp.task('webpack', function() {
	webpackConfig.devtool = 'eval';
	webpackConfig.watch = true;

	webpack(webpackConfig, webpackCallback);
});

gulp.task('webpack-build', function() {
	webpackConfig.devtool = 'eval-source-map';

	webpack(webpackConfig, webpackCallback);
});
<|endoftext|>


<commit_before>// Wraps any value with promise.

'use strict';

var curry       = require('es5-ext/lib/Function/curry').call
  , isFunction  = require('es5-ext/lib/Function/is-function')
  , bindMethods = require('es5-ext/lib/Object/plain/bind-methods').call
  , merge       = require('es5-ext/lib/Object/plain/merge').call
  , nextTick    = require('clock/lib/next-tick')

  , isPromise   = require('./is-promise')

  , deferred, throwError, port;

throwError = function () {
	throw this;
};

port = {
	then: (function (callback) {
		return function (win, fail) {
			var d = deferred();
			nextTick(callback.bind(d, this.failed ? fail : win, this.value));
			return d.promise;
		};
	}(function (callback, value) {
		if (callback) {
			try {
				value = callback(value);
			} catch (e) {
				value = e;
			}
		}
		this.resolve(value);
	})),
	cb: function (cb) {
		nextTick(this.failed ?
			curry(cb, this.value, null) :
			curry(cb, null, this.value));
	},
	end: function (handler) {
		if (this.failed) {
			nextTick(
				isFunction(handler) ? curry(handler, this.value) :
					throwError.bind(this.value));
		}
		return this.then;
	}
};

module.exports = function (value) {
	var p, r;
	if (isPromise(value)) {
		return value;
	}
	p = bindMethods({}, null, port);
	r = merge(p.then, p);
	p.value = value;
	p.failed = value instanceof Error;
	return r;
};

deferred = require('./deferred');
<commit_msg>Use 'throw' function from es5-ext<commit_after>// Wraps any value with promise.

'use strict';

var curry       = require('es5-ext/lib/Function/curry').call
  , isFunction  = require('es5-ext/lib/Function/is-function')
  , bindMethods = require('es5-ext/lib/Object/plain/bind-methods').call
  , merge       = require('es5-ext/lib/Object/plain/merge').call
  , throwError  = require('es5-ext/lib/Error/throw')
  , nextTick    = require('clock/lib/next-tick')

  , isPromise   = require('./is-promise')

  , deferred, port;

port = {
	then: (function (callback) {
		return function (win, fail) {
			var d = deferred();
			nextTick(callback.bind(d, this.failed ? fail : win, this.value));
			return d.promise;
		};
	}(function (callback, value) {
		if (callback) {
			try {
				value = callback(value);
			} catch (e) {
				value = e;
			}
		}
		this.resolve(value);
	})),
	cb: function (cb) {
		nextTick(this.failed ?
			curry(cb, this.value, null) :
			curry(cb, null, this.value));
	},
	end: function (handler) {
		if (this.failed) {
			nextTick(
				isFunction(handler) ? curry(handler, this.value) :
					throwError.bind(this.value));
		}
		return this.then;
	}
};

module.exports = function (value) {
	var p, r;
	if (isPromise(value)) {
		return value;
	}
	p = bindMethods({}, null, port);
	r = merge(p.then, p);
	p.value = value;
	p.failed = value instanceof Error;
	return r;
};

deferred = require('./deferred');
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var log = require(path.resolve(__dirname, '../../api/lib/log.js'));

var dumpFolder = '../web/data/';

var typeMatching = {
	'day': 'days',
	'session': 'sessions',
	'speaker': 'speakers',
	'location': 'locations'
};

exports.dump = function (data) {
	var events = {};
	data.forEach(function (entry) {
		if (events[entry.event] === undefined) events[entry.event] = {};
		var event = events[entry.event];

		var key = typeMatching[entry.type];
		if (key === undefined) console.error('Unknown type "'+entry.type+'"')

		if (event[key] === undefined) event[key] = [];
		var list = event[key];

		list.push(entry);
	})

	Object.keys(events).forEach(function (event_key) {
		var event = events[event_key];

		if (!fs.existsSync(dumpFolder+event_key)) fs.mkdirSync(dumpFolder+event_key);

		exportJSON(event_key, 'data',     event         );

		exportJSON(event_key, 'sessions', event.sessions);
		exportJSON(event_key, 'speakers', event.speakers);

		exportTSV(event_key, 'sessions', event.sessions.map(function (entry) {
			return {
				id:            entry.id,
				event:         entry.event,
				type:          entry.type,
				status:        entry.status,
				title:         entry.title,
				photo:         entry.photo,
				begin:         entry.begin,
				end:           entry.end,
				duration:      entry.duration,
				day:           entry.day,
				area:          entry.area,
				track:         entry.track.slug,
				format:        entry.format.slug,
				level:         entry.level.slug,
				lang:          entry.lang.slug,
				speakers:      entry.speakers.join(', '),
				last_modified: entry.last_modified
			}
		}));

		exportTSV(event_key, 'speakers', event.speakers.map(function (entry) {
			return {
				id:           entry.id,
				event:        entry.event,
				type:         entry.type,
				name:         entry.name,
				nickname:     entry.nickname,
				photo:        entry.photo,
				organization: entry.organization,
				position:     entry.position,
				biography:    entry.biography,
				sessions:     entry.sessions.join(', ')
			}
		}));
	});
}

function exportJSON(event_key, suffix, data) {
	var filename = dumpFolder+event_key+'/'+suffix+'.json';
	log.info('Dumping JSON "'+filename+'"');

	fs.writeFileSync(filename, JSON.stringify(data, null, '\t'), 'utf8');
}

function exportTSV(event_key, suffix, data) {

	var header = Object.keys(data[0]);
	var tsv = [header.join('\t')];

	data.forEach(function (entry) {
		var row = header.map(function (key) { return (''+entry[key]).replace(/\n/g, '\\n').replace(/\t/g, '\\t') });
		tsv.push(row.join('\t'));
	})

	tsv = tsv.join('\n');

	var filename = dumpFolder+event_key+'/'+suffix+'.tsv';
	log.info('Dumping TSV "'+filename+'"');

	fs.writeFileSync(filename, tsv, 'utf8');
}

<commit_msg>better TSV export<commit_after>var fs = require('fs');
var path = require('path');
var log = require(path.resolve(__dirname, '../../api/lib/log.js'));

var dumpFolder = '../web/data/';

var typeMatching = {
	'day': 'days',
	'session': 'sessions',
	'speaker': 'speakers',
	'location': 'locations'
};

exports.dump = function (data) {
	var events = {};
	data.forEach(function (entry) {
		if (events[entry.event] === undefined) events[entry.event] = {};
		var event = events[entry.event];

		var key = typeMatching[entry.type];
		if (key === undefined) console.error('Unknown type "'+entry.type+'"')

		if (event[key] === undefined) event[key] = [];
		var list = event[key];

		list.push(entry);
	})

	Object.keys(events).forEach(function (event_key) {
		var event = events[event_key];

		if (!fs.existsSync(dumpFolder+event_key)) fs.mkdirSync(dumpFolder+event_key);

		exportJSON(event_key, 'data',     event         );

		exportJSON(event_key, 'sessions', event.sessions);
		exportJSON(event_key, 'speakers', event.speakers);

		exportTSV(event_key, 'sessions', event.sessions.map(function (entry) {
			return {
				id:            entry.id,
				event:         entry.event,
				title:         entry.title,
				url:           entry.url,
				begin:         entry.begin,
				end:           entry.end,
				duration:      entry.duration,
				day:           entry.day.date,
				location:      entry.location.label_en,
				track:         entry.track.id,
				format:        entry.format.id,
				level:         entry.level.id,
				lang:          entry.lang.id,
				speakers:      entry.speakers.map(function (speaker) { return speaker.name }).join(', '),
				last_modified: entry.last_modified
			}
		}));

		exportTSV(event_key, 'speakers', event.speakers.map(function (entry) {
			return {
				id:           entry.id,
				event:        entry.event,
				name:         entry.name,
				photo:        entry.photo,
				organization: entry.organization,
				position:     entry.position,
				sessions:     entry.sessions.map(function (session) { return session.title }).join(', ')
			}
		}));
	});
}

function exportJSON(event_key, suffix, data) {
	var filename = dumpFolder+event_key+'/'+suffix+'.json';
	log.info('Dumping JSON "'+filename+'"');

	fs.writeFileSync(filename, JSON.stringify(data, null, '\t'), 'utf8');
}

function exportTSV(event_key, suffix, data) {

	var header = Object.keys(data[0]);
	var tsv = [header.join('\t')];

	data.forEach(function (entry) {
		var row = header.map(function (key) { return (''+entry[key]).replace(/\n/g, '\\n').replace(/\t/g, '\\t') });
		tsv.push(row.join('\t'));
	})

	tsv = tsv.join('\n');

	var filename = dumpFolder+event_key+'/'+suffix+'.tsv';
	log.info('Dumping TSV "'+filename+'"');

	fs.writeFileSync(filename, tsv, 'utf8');
}

<|endoftext|>


<commit_before>$(document).ready(function(){
    $('#search').click(function(){
        $('#searchbox').show();
    });

    $('#submit').click(function(){
        var loc = $('#selloc').val();
        var checkin = $('#checkin').val();
        var checkout = $('#checkout').val();

    });

})<commit_msg>renamed<commit_after><|endoftext|>


<commit_before>var clone = require('clone')
var nlobjField = require('../modules/nlobjField.js')
var nlobjSubRecord = require('../modules/nlobjSubRecord.js')

var nlobjRecord = function (recordtype, internalid) {

  if(!internalid) {
    internalid = parseInt((Math.random() * 100), 10)
  }

  var id = internalid;
  var type = recordtype;
  var lineItems = [];
  var addressBookLines = [];
  var fields = [];
  var fieldValues = {};
  var currentLineItems =  {
    'item':[],
    'addressbook':[]
  };

  var setFieldValue = function(name, value) {
    fieldValues[name] = value
  }

  setFieldValue('internalid', id)

  var getFieldValue = function(name) {
    if(fieldValues[name]) {
      return fieldValues[name]
    }
  }

  var getLineItemCount = function(group) {
    if(group == 'item') {
      return lineItems.length
    } else {
      return 0
    }
  }

  var setLineItemValue = function(group,name,linenum,value) {
    if(group == 'item') {
      lineItems[linenum-1][name] = value
    } else {
      throw new Error('NETSIM ERROR: Line item group: '+group+' is unsupported.');
    }
  }

  var getLineItemValue = function(group,name,line) {
    if(group == 'item') {
      return lineItems[line-1][name]
    } else {
      throw new Error('NETSIM ERROR: Line item group: '+group+' is unsupported.');
    }
  }

  var selectNewLineItem = function(group) {
    if(group == 'item') {
      currentLineItems[group] = {}
      currentLineItems[group]['id'] = id+'_'+lineItems.length;
      currentLineItems[group]['line'] = lineItems.length;
    } else if(group == 'addressbook') {
      currentLineItems[group] = {}
      currentLineItems[group]['id'] = id+'_'+lineItems.length;
      currentLineItems[group]['line'] = lineItems.length;
      currentLineItems[group]['addressbookaddress'] = [];
    } else {
      throw new Error('NETSIM ERROR: Line item group: '+group+' is unsupported.');
    }
  }

  var createCurrentLineItemSubrecord = function(sublist,fldname) {
    var subRecord = new nlobjSubRecord(fldname);
    currentLineItems[sublist][fldname].push(subRecord);
    return subRecord;
  }

  var setCurrentLineItemValue = function(group,name,value) {
    currentLineItems[group][name] = value
  }

  var commitLineItem = function(group,ignoreRecalc) {
    if(group == 'item') {
      lineItems.push(currentLineItems[group])
    } if(group == 'addressbook') {
      addressBookLines.push(currentLineItems[group])
    } else {
      throw new Error('NETSIM ERROR: Line item group: '+group+' is unsupported.');
    }
  }

  var getRecordType = function() {
    return type
  }

  var getId = function() {
    return id
  }

  var getField = function(fldnam) {
    for(var i = 0; i < fields.length; i++) {
      var field = fields[i]
      if(field.getName() == fldnam) {
        return field;
      }
    }
  }

  var addField = function(name,type,label,source,group) {
    console.log("Warning: addField is not part of the netsuite api, use this for mocking data only");
    fields.push(new nlobjField(name));
  }

  //This funtion is for netsim use only, do not use as part of a suitescript
  //as it is not part of the netsuite api.
  var transform = function(transformType, newRecordId) {
    var clonedLineItems = clone(lineItems)
    var clonedRecord = nlobjRecord(transformType,newRecordId)

    for(var i = 0; i < clonedLineItems.length; i++) {
      clonedRecord.selectNewLineItem('item')

      var lineItem = clonedLineItems[i];
      var lineItemKeys = Object.keys(lineItem)

      for(var x = 0; x < lineItemKeys.length; x++) {
        var name = lineItemKeys[x]
        var value = lineItem[name]

        clonedRecord.setCurrentLineItemValue('item', name, value)
      }

      clonedRecord.commitLineItem('item')

    }

    return clonedRecord
  }

  return {
    setFieldValue : setFieldValue,
    getFieldValue : getFieldValue,
    getLineItemCount : getLineItemCount,
    setLineItemValue : setLineItemValue,
    getLineItemValue : getLineItemValue,
    selectNewLineItem : selectNewLineItem,
    setCurrentLineItemValue : setCurrentLineItemValue,
    commitLineItem : commitLineItem,
    getRecordType : getRecordType,
    getId : getId,
    transform : transform,
    createCurrentLineItemSubrecord:createCurrentLineItemSubrecord,
    getField:getField,
    addField:addField
  }

}

module.exports = nlobjRecord
<commit_msg>Adding code to hand address sublists<commit_after>var clone = require('clone')
var nlobjField = require('../modules/nlobjField.js')
var nlobjSubRecord = require('../modules/nlobjSubRecord.js')

var nlobjRecord = function (recordtype, internalid) {

  if(!internalid) {
    internalid = parseInt((Math.random() * 100), 10)
  }

  var id = internalid;
  var type = recordtype;
  var lineItems = [];
  var addressBookLines = [];
  var fields = [];
  var fieldValues = {};
  var currentLineItems =  {
    'item':[],
    'addressbook':[]
  };

  var setFieldValue = function(name, value) {
    fieldValues[name] = value
  }

  setFieldValue('internalid', id)

  var getFieldValue = function(name) {
    if(fieldValues[name]) {
      return fieldValues[name]
    }
  }

  var getLineItemCount = function(group) {
    if(group == 'item') {
      return lineItems.length
    } else {
      return 0
    }
  }

  var setLineItemValue = function(group,name,linenum,value) {
    if(group == 'item') {
      lineItems[linenum-1][name] = value
    } else {
      throw new Error('NETSIM ERROR: Line item group: '+group+' is unsupported.');
    }
  }

  var getLineItemValue = function(group,name,line) {
    if(group == 'item') {
      return lineItems[line-1][name]
    } else {
      throw new Error('NETSIM ERROR: Line item group: '+group+' is unsupported.');
    }
  }

  var selectNewLineItem = function(group) {
    if(group == 'item') {
      currentLineItems[group] = {}
      currentLineItems[group]['id'] = id+'_'+lineItems.length;
      currentLineItems[group]['line'] = lineItems.length;
    } else if(group == 'addressbook') {
      currentLineItems[group] = {}
      currentLineItems[group]['id'] = id+'_'+lineItems.length;
      currentLineItems[group]['line'] = lineItems.length;
      currentLineItems[group]['addressbookaddress'] = [];
    } else {
      throw new Error('NETSIM ERROR: Line item group: '+group+' is unsupported.');
    }
  }

  var createCurrentLineItemSubrecord = function(sublist,fldname) {
    var subRecord = new nlobjSubRecord(fldname);
    currentLineItems[sublist][fldname].push(subRecord);
    return subRecord;
  }

  var setCurrentLineItemValue = function(group,name,value) {
    currentLineItems[group][name] = value
  }

  var commitLineItem = function(group,ignoreRecalc) {
    if(group == 'item') {
      lineItems.push(currentLineItems[group])
    } else if(group == 'addressbook') {
      addressBookLines.push(currentLineItems[group])
    } else {
      throw new Error('NETSIM ERROR: Line item group: '+group+' is unsupported.');
    }
  }

  var getRecordType = function() {
    return type
  }

  var getId = function() {
    return id
  }

  var getField = function(fldnam) {
    for(var i = 0; i < fields.length; i++) {
      var field = fields[i]
      if(field.getName() == fldnam) {
        return field;
      }
    }
  }

  var addField = function(name,type,label,source,group) {
    console.log("Warning: addField is not part of the netsuite api, use this for mocking data only");
    fields.push(new nlobjField(name));
  }

  //This funtion is for netsim use only, do not use as part of a suitescript
  //as it is not part of the netsuite api.
  var transform = function(transformType, newRecordId) {
    var clonedLineItems = clone(lineItems)
    var clonedRecord = nlobjRecord(transformType,newRecordId)

    for(var i = 0; i < clonedLineItems.length; i++) {
      clonedRecord.selectNewLineItem('item')

      var lineItem = clonedLineItems[i];
      var lineItemKeys = Object.keys(lineItem)

      for(var x = 0; x < lineItemKeys.length; x++) {
        var name = lineItemKeys[x]
        var value = lineItem[name]

        clonedRecord.setCurrentLineItemValue('item', name, value)
      }

      clonedRecord.commitLineItem('item')

    }

    return clonedRecord
  }

  return {
    setFieldValue : setFieldValue,
    getFieldValue : getFieldValue,
    getLineItemCount : getLineItemCount,
    setLineItemValue : setLineItemValue,
    getLineItemValue : getLineItemValue,
    selectNewLineItem : selectNewLineItem,
    setCurrentLineItemValue : setCurrentLineItemValue,
    commitLineItem : commitLineItem,
    getRecordType : getRecordType,
    getId : getId,
    transform : transform,
    createCurrentLineItemSubrecord:createCurrentLineItemSubrecord,
    getField:getField,
    addField:addField
  }

}

module.exports = nlobjRecord
<|endoftext|>