<commit_before>'use strict';

var copy         = require('es5-ext/lib/Array/prototype/copy')
  , last         = require('es5-ext/lib/Array/prototype/last')
  , isString     = require('es5-ext/lib/String/is-string')
  , trimCommon   = require('es5-ext/lib/String/prototype/trim-common-left')
  , sLast        = require('es5-ext/lib/String/prototype/last')
  , d            = require('es5-ext/lib/Object/descriptor')
  , indent       = require('es5-ext/lib/String/prototype/indent')
  , endsWith     = require('es5-ext/lib/String/prototype/ends-with')
  , deferred     = require('deferred')
  , path         = require('path')
  , stat         = require('fs').stat
  , nmSource     = process.binding('natives')
  , getRequire   = require('next/lib/module/get-require')
  , findRoot     = require('next/lib/module/find-package-root')
  , readFile     = require('fs2/lib/read-file').readFile
  , statP        = require('fs2/lib/stat').stat
  , findRequires = require('find-requires')

  , create = Object.create, keys = Object.keys, stringify = JSON.stringify
  , dirname = path.dirname, join = path.join, resolve = path.resolve
  , sep = path.sep
  , readFileOpts = { encoding: 'utf8' }
  , dotMatch = /^\.+$/
  , readCachedFile, fileExists, dirExists, parseDependencies, parser
  , modulesToString;

if (sep == null) {
	throw new Error("Unsupported Node version. Please upgrade," +
		" Webmake needs at least Node v0.8");
}

fileExists = function (path) {
	var def = deferred();
	stat(path, function (err, stats) {
		def.resolve(err ? false : stats.isFile());
	});
	return def.promise;
};

dirExists = function (path) {
	var def = deferred();
	stat(path, function (err, stats) {
		def.resolve(err ? false : stats.isDirectory());
	});
	return def.promise;
};

parseDependencies = function (text, filename) {
	return findRequires(text, { raw: true }).map(function (node) {
		var path = node.value;
		if (!path) {
			throw new TypeError("Not supported require call: " + node.raw +
				" at " + filename + ":" + node.line);
		}
		return (path.slice(-3) === '.js') ? path.slice(0, -3) : path;
	});
};

readCachedFile = (function () {
	var cache = {};
	return function (path) {
		var data;
		if (!cache.hasOwnProperty(path)) data = cache[path] = {};
		else data = cache[path];
		return statP(path)(function (stats) {
			stats = stats.size + '.' + stats.mtime.valueOf();
			if (data.stats === stats) return data.file;
			data.stats = stats;
			return (data.file = readFile(path, readFileOpts));
		});
	};
}());

modulesToString = function self(nest, parser) {
	return keys(this).sort().map(function (name) {
		var current = this[name]
		  , text = indent.call(stringify(name), '\t', nest + 1) + ': ';
		if (name === ':mainpath:') {
			text += stringify(current);
		} else if (!isString(current)) {
			text += '{\n' + self.call(current, nest + 1, parser) + '\n' +
				indent.call('}', '\t', nest + 1);
		} else {
			text += 'function (exports, module, require) {\n' +
				indent.call(parser.sourceMaps ?
						'eval(' + stringify(current + '\n//@ sourceURL=' +
							current.filename) +
						')' : current, '\t', nest + 2) +
				indent.call('}', '\t', nest + 1);
		}
		return text;
	}, this).join(',\n');
};

parser = {
	readInput: function (input) {
		var scope, path, tree = [];
		input = resolve(String(input));
		return findRoot(input)(function (root) {
			var name, dirs;
			name = root ? last.call(root.split(sep)) : '/';
			if (!this.modules[name]) {
				this.packages[name] = root || sep;
				this.modules[name] = {};
			}
			scope = this.modules[name];
			path = name + (root ? sep : '') +
				(dirs = trimCommon.call(input, root || '').slice(1));
			dirs = dirs.split(sep);
			name = dirs.pop();
			dirs.forEach(function (dir) {
				tree.push(scope);
				scope = scope[dir] || (scope[dir] = {});
			});
			return this.readFile(input, name, scope, tree);
		}.bind(this))(function () {
			return path.slice(0, -3).replace(/\\/g, '/');
		});
	},
	readFile: function (filename, name, scope, tree) {
		// console.log("PC", filename);
		return readCachedFile(filename)(function (content) {
			this.modulesFiles.push(filename);
			if (content.charCodeAt(0) === 0xFEFF) {
				// Remove BOM, see:
				// https://github.com/joyent/node/blob/master/lib/module.js#L448
				// (...) This catches EF BB BF (the UTF-8 BOM)
				// because the buffer-to-string conversion in `fs.readFile()`
				// translates it to FEFF, the UTF-16 BOM. (...)
				content = content.slice(1);
			}
			if (sLast.call(content) !== '\n') {
				content += '\n';
			}
			scope[name] = content = new String(content);
			content.filename = filename.split(sep)
				.slice(-2 - tree.length).join('/');
			return deferred.map(parseDependencies(content, filename),
				this.resolve.bind(this, filename, dirname(filename), scope, tree));
		}.bind(this));
	},
	resolve: function (fromfile, dirname, scope, tree, filename) {
		// console.log("R", filename);
		tree = copy.call(tree);
		if (filename[0] === '.') {
			return this.resolveLocal(fromfile, dirname, scope, tree, filename);
		} else {
			return this.resolveExternal(fromfile, dirname, filename);
		}
	},
	resolveLocal: function (fromfile, dirname, scope, tree, filename) {
		// console.log("RL", filename, dirname, fromfile, tree.length);
		var path, dir, name, isDir;
		filename = join(filename);
		path = filename.split(sep);
		if (dotMatch.test(last.call(path))) {
			// If path ends with '/..' or '/.' we end it with sep: '/../'
			// (it will indicate that we're after directory's index.js)
			path.push('');
			filename += sep;
		}
		isDir = (sLast.call(filename) === sep);
		filename = resolve(dirname + sep + filename);
		return (isDir ? dirExists(filename)(function (exists) {
			if (exists) {
				path.pop();
				name = 'index.js';
				filename += sep + 'index.js';
			} else {
				throw new Error("Module '" + filename +
					"' not found, as required in '" + fromfile + "'");
			}
		}) : fileExists(filename + '.js')(function (exists) {
			if (exists) {
				filename += '.js';
				name = path.pop() + '.js';
			} else {
				return dirExists(filename)(function (exists) {
					if (exists) {
						name = 'index.js';
						filename += sep + 'index.js';
					} else {
						throw new Error("Module '" + filename +
							"' not found, as required in '" + fromfile + "'");
					}
				});
			}
		}))(function () {
			while ((dir = path.shift())) {
				if (dir === '..') {
					if (!tree.length) {
						throw new Error("Require out of package root scope");
					}
					scope = tree.pop();
				} else if (dir !== '.') {
					tree.push(scope);
					scope = scope[dir] || (scope[dir] = {});
				}
			}
			if (scope[name]) {
				return null;
			} else {
				return (scope[name] = this.readFile(filename, name, scope, tree));
			}
		}.bind(this));
	},
	resolveExternal: function (fromfile, dirname, filename) {
		// console.log("RE", filename);
		var org = filename, name, tree, require, main, path, cache;
		filename = join(filename);
		name = filename.split(sep, 1)[0];
		return deferred.promisifySync(function () {
			if (this.modules[name]) {
				return this.modules[name];
			} else {
				require = getRequire(fromfile);
				try {
					path = main = require.resolve(name);
				} catch (e) {
					try {
						path = require.resolve(org);
					} catch (e2) {
						throw new Error("Module '" + filename +
							"' not found, as required in '" + fromfile + "'");
					}
				}
				if (main === name) {
					cache = nmSource[name];
					delete nmSource[name];
					try {
						path = main = require.resolve(name);
					} catch (e3) {}
					nmSource[name] = cache;
					if (main === name) {
						throw new Error("Cannot require " + stringify(name) +
							" (as in '" + fromfile + "').\n       Native node.js modules" +
							" are not ported to client-side. You can however provide" +
							" alternative version of this module in your node_modules" +
							" path, it will be picked up by Webmake.\n");
					}
				}
				if (!main && endsWith.call(path, filename + '.js')) {
					this.packages[name] = path.slice(0, -(filename.length + 3) +
						name.length);
					return (this.modules[name] = {});
				}
				var promise = this.modules[name] = findRoot(path)(function (root) {
					var module = {};
					this.packages[name] = root;
					if (main) {
						module[':mainpath:'] = trimCommon.call(main, root).slice(1, -3);
					}
					return module;
				}.bind(this));
				promise.aside(function (module) {
					this.modules[name] = module;
				}.bind(this));
				return promise;
			}
		}.bind(this))()(function (scope) {
			// console.log('RFT', name, this.modules[name] === scope);
			tree = [];
			if (name === filename) {
				filename = scope[':mainpath:'];
			} else {
				filename = filename.slice(name.length + 1);
			}
			// console.log("RE", name, scope, filename);
			return this.resolveLocal(fromfile, this.packages[name], scope, tree,
				filename);
		}.bind(this));
	},
	toString: function () {
		return '{\n' + modulesToString.call(this.modules, 0, this) + '\n}';
	}
};

module.exports = exports = function (/* options */) {
	var options = Object(arguments[0]);
	return create(parser, {
		modules: d({}),
		packages: d({}),
		modulesFiles: d([]),
		sourceMaps: d(Boolean(options.sourceMap))
	});
};
exports.modulesToString = modulesToString;
<commit_msg>-- whitespace<commit_after>'use strict';

var copy         = require('es5-ext/lib/Array/prototype/copy')
  , last         = require('es5-ext/lib/Array/prototype/last')
  , isString     = require('es5-ext/lib/String/is-string')
  , trimCommon   = require('es5-ext/lib/String/prototype/trim-common-left')
  , sLast        = require('es5-ext/lib/String/prototype/last')
  , d            = require('es5-ext/lib/Object/descriptor')
  , indent       = require('es5-ext/lib/String/prototype/indent')
  , endsWith     = require('es5-ext/lib/String/prototype/ends-with')
  , deferred     = require('deferred')
  , path         = require('path')
  , stat         = require('fs').stat
  , nmSource     = process.binding('natives')
  , getRequire   = require('next/lib/module/get-require')
  , findRoot     = require('next/lib/module/find-package-root')
  , readFile     = require('fs2/lib/read-file').readFile
  , statP        = require('fs2/lib/stat').stat
  , findRequires = require('find-requires')

  , create = Object.create, keys = Object.keys, stringify = JSON.stringify
  , dirname = path.dirname, join = path.join, resolve = path.resolve
  , sep = path.sep
  , readFileOpts = { encoding: 'utf8' }
  , dotMatch = /^\.+$/
  , readCachedFile, fileExists, dirExists, parseDependencies, parser
  , modulesToString;

if (sep == null) {
	throw new Error("Unsupported Node version. Please upgrade," +
		" Webmake needs at least Node v0.8");
}

fileExists = function (path) {
	var def = deferred();
	stat(path, function (err, stats) {
		def.resolve(err ? false : stats.isFile());
	});
	return def.promise;
};

dirExists = function (path) {
	var def = deferred();
	stat(path, function (err, stats) {
		def.resolve(err ? false : stats.isDirectory());
	});
	return def.promise;
};

parseDependencies = function (text, filename) {
	return findRequires(text, { raw: true }).map(function (node) {
		var path = node.value;
		if (!path) {
			throw new TypeError("Not supported require call: " + node.raw +
				" at " + filename + ":" + node.line);
		}
		return (path.slice(-3) === '.js') ? path.slice(0, -3) : path;
	});
};

readCachedFile = (function () {
	var cache = {};
	return function (path) {
		var data;
		if (!cache.hasOwnProperty(path)) data = cache[path] = {};
		else data = cache[path];
		return statP(path)(function (stats) {
			stats = stats.size + '.' + stats.mtime.valueOf();
			if (data.stats === stats) return data.file;
			data.stats = stats;
			return (data.file = readFile(path, readFileOpts));
		});
	};
}());

modulesToString = function self(nest, parser) {
	return keys(this).sort().map(function (name) {
		var current = this[name]
		  , text = indent.call(stringify(name), '\t', nest + 1) + ': ';
		if (name === ':mainpath:') {
			text += stringify(current);
		} else if (!isString(current)) {
			text += '{\n' + self.call(current, nest + 1, parser) + '\n' +
				indent.call('}', '\t', nest + 1);
		} else {
			text += 'function (exports, module, require) {\n' +
				indent.call(parser.sourceMaps ?
						'eval(' + stringify(current + '\n//@ sourceURL=' +
							current.filename) +
						')' : current, '\t', nest + 2) +
				indent.call('}', '\t', nest + 1);
		}
		return text;
	}, this).join(',\n');
};

parser = {
	readInput: function (input) {
		var scope, path, tree = [];
		input = resolve(String(input));
		return findRoot(input)(function (root) {
			var name, dirs;
			name = root ? last.call(root.split(sep)) : '/';
			if (!this.modules[name]) {
				this.packages[name] = root || sep;
				this.modules[name] = {};
			}
			scope = this.modules[name];
			path = name + (root ? sep : '') +
				(dirs = trimCommon.call(input, root || '').slice(1));
			dirs = dirs.split(sep);
			name = dirs.pop();
			dirs.forEach(function (dir) {
				tree.push(scope);
				scope = scope[dir] || (scope[dir] = {});
			});
			return this.readFile(input, name, scope, tree);
		}.bind(this))(function () {
			return path.slice(0, -3).replace(/\\/g, '/');
		});
	},
	readFile: function (filename, name, scope, tree) {
		// console.log("PC", filename);
		return readCachedFile(filename)(function (content) {
			this.modulesFiles.push(filename);
			if (content.charCodeAt(0) === 0xFEFF) {
				// Remove BOM, see:
				// https://github.com/joyent/node/blob/master/lib/module.js#L448
				// (...) This catches EF BB BF (the UTF-8 BOM)
				// because the buffer-to-string conversion in `fs.readFile()`
				// translates it to FEFF, the UTF-16 BOM. (...)
				content = content.slice(1);
			}
			if (sLast.call(content) !== '\n') content += '\n';
			scope[name] = content = new String(content);
			content.filename = filename.split(sep).slice(-2 - tree.length).join('/');
			return deferred.map(parseDependencies(content, filename),
				this.resolve.bind(this, filename, dirname(filename), scope, tree));
		}.bind(this));
	},
	resolve: function (fromfile, dirname, scope, tree, filename) {
		// console.log("R", filename);
		tree = copy.call(tree);
		if (filename[0] === '.') {
			return this.resolveLocal(fromfile, dirname, scope, tree, filename);
		} else {
			return this.resolveExternal(fromfile, dirname, filename);
		}
	},
	resolveLocal: function (fromfile, dirname, scope, tree, filename) {
		// console.log("RL", filename, dirname, fromfile, tree.length);
		var path, dir, name, isDir;
		filename = join(filename);
		path = filename.split(sep);
		if (dotMatch.test(last.call(path))) {
			// If path ends with '/..' or '/.' we end it with sep: '/../'
			// (it will indicate that we're after directory's index.js)
			path.push('');
			filename += sep;
		}
		isDir = (sLast.call(filename) === sep);
		filename = resolve(dirname + sep + filename);
		return (isDir ? dirExists(filename)(function (exists) {
			if (exists) {
				path.pop();
				name = 'index.js';
				filename += sep + 'index.js';
			} else {
				throw new Error("Module '" + filename +
					"' not found, as required in '" + fromfile + "'");
			}
		}) : fileExists(filename + '.js')(function (exists) {
			if (exists) {
				filename += '.js';
				name = path.pop() + '.js';
			} else {
				return dirExists(filename)(function (exists) {
					if (exists) {
						name = 'index.js';
						filename += sep + 'index.js';
					} else {
						throw new Error("Module '" + filename +
							"' not found, as required in '" + fromfile + "'");
					}
				});
			}
		}))(function () {
			while ((dir = path.shift())) {
				if (dir === '..') {
					if (!tree.length) {
						throw new Error("Require out of package root scope");
					}
					scope = tree.pop();
				} else if (dir !== '.') {
					tree.push(scope);
					scope = scope[dir] || (scope[dir] = {});
				}
			}
			if (scope[name]) return null;
			else return (scope[name] = this.readFile(filename, name, scope, tree));
		}.bind(this));
	},
	resolveExternal: function (fromfile, dirname, filename) {
		// console.log("RE", filename);
		var org = filename, name, tree, require, main, path, cache;
		filename = join(filename);
		name = filename.split(sep, 1)[0];
		return deferred.promisifySync(function () {
			if (this.modules[name]) {
				return this.modules[name];
			} else {
				require = getRequire(fromfile);
				try {
					path = main = require.resolve(name);
				} catch (e) {
					try {
						path = require.resolve(org);
					} catch (e2) {
						throw new Error("Module '" + filename +
							"' not found, as required in '" + fromfile + "'");
					}
				}
				if (main === name) {
					cache = nmSource[name];
					delete nmSource[name];
					try {
						path = main = require.resolve(name);
					} catch (e3) {}
					nmSource[name] = cache;
					if (main === name) {
						throw new Error("Cannot require " + stringify(name) +
							" (as in '" + fromfile + "').\n       Native node.js modules" +
							" are not ported to client-side. You can however provide" +
							" alternative version of this module in your node_modules" +
							" path, it will be picked up by Webmake.\n");
					}
				}
				if (!main && endsWith.call(path, filename + '.js')) {
					this.packages[name] = path.slice(0, -(filename.length + 3) +
						name.length);
					return (this.modules[name] = {});
				}
				var promise = this.modules[name] = findRoot(path)(function (root) {
					var module = {};
					this.packages[name] = root;
					if (main) {
						module[':mainpath:'] = trimCommon.call(main, root).slice(1, -3);
					}
					return module;
				}.bind(this));
				promise.aside(function (module) {
					this.modules[name] = module;
				}.bind(this));
				return promise;
			}
		}.bind(this))()(function (scope) {
			// console.log('RFT', name, this.modules[name] === scope);
			tree = [];
			if (name === filename) filename = scope[':mainpath:'];
			else filename = filename.slice(name.length + 1);
			// console.log("RE", name, scope, filename);
			return this.resolveLocal(fromfile, this.packages[name], scope, tree,
				filename);
		}.bind(this));
	},
	toString: function () {
		return '{\n' + modulesToString.call(this.modules, 0, this) + '\n}';
	}
};

module.exports = exports = function (/* options */) {
	var options = Object(arguments[0]);
	return create(parser, {
		modules: d({}),
		packages: d({}),
		modulesFiles: d([]),
		sourceMaps: d(Boolean(options.sourceMap))
	});
};
exports.modulesToString = modulesToString;
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var express = require('express');
var routes = require('./routes');
var http = require('http');
var path = require('path');

var app = express();

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

routes(app);

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});
<commit_msg>修改端口号<commit_after>
/**
 * Module dependencies.
 */

var express = require('express');
var routes = require('./routes');
var http = require('http');
var path = require('path');

var app = express();

// all environments
app.set('port', process.env.PORT || 80);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

routes(app);

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});
<|endoftext|>


<commit_before>const Cast = require('../util/cast');

class Scratch3DataBlocks {
    constructor (runtime) {
        /**
         * The runtime instantiating this block package.
         * @type {Runtime}
         */
        this.runtime = runtime;
    }

    /**
     * Retrieve the block primitives implemented by this package.
     * @return {object.<string, Function>} Mapping of opcode to Function.
     */
    getPrimitives () {
        return {
            data_variable: this.getVariable,
            data_setvariableto: this.setVariableTo,
            data_changevariableby: this.changeVariableBy,
            data_hidevariable: this.hideVariable,
            data_showvariable: this.showVariable,
            data_listcontents: this.getListContents,
            data_addtolist: this.addToList,
            data_deleteoflist: this.deleteOfList,
            data_deletealloflist: this.deleteAllOfList,
            data_insertatlist: this.insertAtList,
            data_replaceitemoflist: this.replaceItemOfList,
            data_itemoflist: this.getItemOfList,
            data_itemnumoflist: this.getItemNumOfList,
            data_lengthoflist: this.lengthOfList,
            data_listcontainsitem: this.listContainsItem,
            data_hidelist: this.hideList,
            data_showlist: this.showList
        };
    }

    getVariable (args, util) {
        const variable = util.target.lookupOrCreateVariable(
            args.VARIABLE.id, args.VARIABLE.name);
        return variable.value;
    }

    setVariableTo (args, util) {
        const variable = util.target.lookupOrCreateVariable(
            args.VARIABLE.id, args.VARIABLE.name);
        variable.value = args.VALUE;

        if (variable.isCloud) {
            util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, args.VALUE]);
        }
    }

    changeVariableBy (args, util) {
        const variable = util.target.lookupOrCreateVariable(
            args.VARIABLE.id, args.VARIABLE.name);
        const castedValue = Cast.toNumber(variable.value);
        const dValue = Cast.toNumber(args.VALUE);
        const newValue = castedValue + dValue;
        variable.value = newValue;

        if (variable.isCloud) {
            util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, newValue]);
        }
    }

    changeMonitorVisibility (id, visible) {
        // Send the monitor blocks an event like the flyout checkbox event.
        // This both updates the monitor state and changes the isMonitored block flag.
        this.runtime.monitorBlocks.changeBlock({
            id: id, // Monitor blocks for variables are the variable ID.
            element: 'checkbox', // Mimic checkbox event from flyout.
            value: visible
        }, this.runtime);
    }

    showVariable (args) {
        this.changeMonitorVisibility(args.VARIABLE.id, true);
    }

    hideVariable (args) {
        this.changeMonitorVisibility(args.VARIABLE.id, false);
    }

    showList (args) {
        this.changeMonitorVisibility(args.LIST.id, true);
    }

    hideList (args) {
        this.changeMonitorVisibility(args.LIST.id, false);
    }

    getListContents (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);

        // If block is running for monitors, return copy of list as an array if changed.
        if (util.thread.updateMonitor) {
            // Return original list value if up-to-date, which doesn't trigger monitor update.
            if (list._monitorUpToDate) return list.value;
            // If value changed, reset the flag and return a copy to trigger monitor update.
            // Because monitors use Immutable data structures, only new objects trigger updates.
            list._monitorUpToDate = true;
            return list.value.slice();
        }

        // Determine if the list is all single letters.
        // If it is, report contents joined together with no separator.
        // If it's not, report contents joined together with a space.
        let allSingleLetters = true;
        for (let i = 0; i < list.value.length; i++) {
            const listItem = list.value[i];
            if (!((typeof listItem === 'string') &&
                  (listItem.length === 1))) {
                allSingleLetters = false;
                break;
            }
        }
        if (allSingleLetters) {
            return list.value.join('');
        }
        return list.value.join(' ');

    }

    addToList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        if (list.value.length < Scratch3DataBlocks.LIST_ITEM_LIMIT) {
            list.value.push(args.ITEM);
            list._monitorUpToDate = false;
        }
    }

    deleteOfList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        const index = Cast.toListIndex(args.INDEX, list.value.length);
        if (index === Cast.LIST_INVALID) {
            return;
        } else if (index === Cast.LIST_ALL) {
            list.value = [];
            return;
        }
        list.value.splice(index - 1, 1);
        list._monitorUpToDate = false;
    }

    deleteAllOfList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        list.value = [];
        return;
    }

    insertAtList (args, util) {
        const item = args.ITEM;
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        const index = Cast.toListIndex(args.INDEX, list.value.length + 1);
        if (index === Cast.LIST_INVALID) {
            return;
        }
        const listLimit = Scratch3DataBlocks.LIST_ITEM_LIMIT;
        if (index > listLimit) return;
        list.value.splice(index - 1, 0, item);
        if (list.value.length > listLimit) {
            // If inserting caused the list to grow larger than the limit,
            // remove the last element in the list
            list.value.pop();
        }
        list._monitorUpToDate = false;
    }

    replaceItemOfList (args, util) {
        const item = args.ITEM;
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        const index = Cast.toListIndex(args.INDEX, list.value.length);
        if (index === Cast.LIST_INVALID) {
            return;
        }
        list.value.splice(index - 1, 1, item);
        list._monitorUpToDate = false;
    }

    getItemOfList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        const index = Cast.toListIndex(args.INDEX, list.value.length);
        if (index === Cast.LIST_INVALID) {
            return '';
        }
        return list.value[index - 1];
    }

    getItemNumOfList (args, util) {
        const item = args.ITEM;
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);

        // Go through the list items one-by-one using Cast.compare. This is for
        // cases like checking if 123 is contained in a list [4, 7, '123'] --
        // Scratch considers 123 and '123' to be equal.
        for (let i = 0; i < list.value.length; i++) {
            if (Cast.compare(list.value[i], item) === 0) {
                return i + 1;
            }
        }

        // We don't bother using .indexOf() at all, because it would end up with
        // edge cases such as the index of '123' in [4, 7, 123, '123', 9].
        // If we use indexOf(), this block would return 4 instead of 3, because
        // indexOf() sees the first occurence of the string 123 as the fourth
        // item in the list. With Scratch, this would be confusing -- after all,
        // '123' and 123 look the same, so one would expect the block to say
        // that the first occurrence of '123' (or 123) to be the third item.

        // Default to 0 if there's no match. Since Scratch lists are 1-indexed,
        // we don't have to worry about this conflicting with the "this item is
        // the first value" number (in JS that is 0, but in Scratch it's 1).
        return 0;
    }

    lengthOfList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        return list.value.length;
    }

    listContainsItem (args, util) {
        const item = args.ITEM;
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        if (list.value.indexOf(item) >= 0) {
            return true;
        }
        // Try using Scratch comparison operator on each item.
        // (Scratch considers the string '123' equal to the number 123).
        for (let i = 0; i < list.value.length; i++) {
            if (Cast.compare(list.value[i], item) === 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Type representation for list variables.
     * @const {string}
     */
    static get LIST_ITEM_LIMIT () {
        return 200000;
    }
}

module.exports = Scratch3DataBlocks;
<commit_msg>replaceItemOfList by assigning array index<commit_after>const Cast = require('../util/cast');

class Scratch3DataBlocks {
    constructor (runtime) {
        /**
         * The runtime instantiating this block package.
         * @type {Runtime}
         */
        this.runtime = runtime;
    }

    /**
     * Retrieve the block primitives implemented by this package.
     * @return {object.<string, Function>} Mapping of opcode to Function.
     */
    getPrimitives () {
        return {
            data_variable: this.getVariable,
            data_setvariableto: this.setVariableTo,
            data_changevariableby: this.changeVariableBy,
            data_hidevariable: this.hideVariable,
            data_showvariable: this.showVariable,
            data_listcontents: this.getListContents,
            data_addtolist: this.addToList,
            data_deleteoflist: this.deleteOfList,
            data_deletealloflist: this.deleteAllOfList,
            data_insertatlist: this.insertAtList,
            data_replaceitemoflist: this.replaceItemOfList,
            data_itemoflist: this.getItemOfList,
            data_itemnumoflist: this.getItemNumOfList,
            data_lengthoflist: this.lengthOfList,
            data_listcontainsitem: this.listContainsItem,
            data_hidelist: this.hideList,
            data_showlist: this.showList
        };
    }

    getVariable (args, util) {
        const variable = util.target.lookupOrCreateVariable(
            args.VARIABLE.id, args.VARIABLE.name);
        return variable.value;
    }

    setVariableTo (args, util) {
        const variable = util.target.lookupOrCreateVariable(
            args.VARIABLE.id, args.VARIABLE.name);
        variable.value = args.VALUE;

        if (variable.isCloud) {
            util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, args.VALUE]);
        }
    }

    changeVariableBy (args, util) {
        const variable = util.target.lookupOrCreateVariable(
            args.VARIABLE.id, args.VARIABLE.name);
        const castedValue = Cast.toNumber(variable.value);
        const dValue = Cast.toNumber(args.VALUE);
        const newValue = castedValue + dValue;
        variable.value = newValue;

        if (variable.isCloud) {
            util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, newValue]);
        }
    }

    changeMonitorVisibility (id, visible) {
        // Send the monitor blocks an event like the flyout checkbox event.
        // This both updates the monitor state and changes the isMonitored block flag.
        this.runtime.monitorBlocks.changeBlock({
            id: id, // Monitor blocks for variables are the variable ID.
            element: 'checkbox', // Mimic checkbox event from flyout.
            value: visible
        }, this.runtime);
    }

    showVariable (args) {
        this.changeMonitorVisibility(args.VARIABLE.id, true);
    }

    hideVariable (args) {
        this.changeMonitorVisibility(args.VARIABLE.id, false);
    }

    showList (args) {
        this.changeMonitorVisibility(args.LIST.id, true);
    }

    hideList (args) {
        this.changeMonitorVisibility(args.LIST.id, false);
    }

    getListContents (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);

        // If block is running for monitors, return copy of list as an array if changed.
        if (util.thread.updateMonitor) {
            // Return original list value if up-to-date, which doesn't trigger monitor update.
            if (list._monitorUpToDate) return list.value;
            // If value changed, reset the flag and return a copy to trigger monitor update.
            // Because monitors use Immutable data structures, only new objects trigger updates.
            list._monitorUpToDate = true;
            return list.value.slice();
        }

        // Determine if the list is all single letters.
        // If it is, report contents joined together with no separator.
        // If it's not, report contents joined together with a space.
        let allSingleLetters = true;
        for (let i = 0; i < list.value.length; i++) {
            const listItem = list.value[i];
            if (!((typeof listItem === 'string') &&
                  (listItem.length === 1))) {
                allSingleLetters = false;
                break;
            }
        }
        if (allSingleLetters) {
            return list.value.join('');
        }
        return list.value.join(' ');

    }

    addToList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        if (list.value.length < Scratch3DataBlocks.LIST_ITEM_LIMIT) {
            list.value.push(args.ITEM);
            list._monitorUpToDate = false;
        }
    }

    deleteOfList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        const index = Cast.toListIndex(args.INDEX, list.value.length);
        if (index === Cast.LIST_INVALID) {
            return;
        } else if (index === Cast.LIST_ALL) {
            list.value = [];
            return;
        }
        list.value.splice(index - 1, 1);
        list._monitorUpToDate = false;
    }

    deleteAllOfList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        list.value = [];
        return;
    }

    insertAtList (args, util) {
        const item = args.ITEM;
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        const index = Cast.toListIndex(args.INDEX, list.value.length + 1);
        if (index === Cast.LIST_INVALID) {
            return;
        }
        const listLimit = Scratch3DataBlocks.LIST_ITEM_LIMIT;
        if (index > listLimit) return;
        list.value.splice(index - 1, 0, item);
        if (list.value.length > listLimit) {
            // If inserting caused the list to grow larger than the limit,
            // remove the last element in the list
            list.value.pop();
        }
        list._monitorUpToDate = false;
    }

    replaceItemOfList (args, util) {
        const item = args.ITEM;
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        const index = Cast.toListIndex(args.INDEX, list.value.length);
        if (index === Cast.LIST_INVALID) {
            return;
        }
        list.value[index - 1] = item;
        list._monitorUpToDate = false;
    }

    getItemOfList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        const index = Cast.toListIndex(args.INDEX, list.value.length);
        if (index === Cast.LIST_INVALID) {
            return '';
        }
        return list.value[index - 1];
    }

    getItemNumOfList (args, util) {
        const item = args.ITEM;
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);

        // Go through the list items one-by-one using Cast.compare. This is for
        // cases like checking if 123 is contained in a list [4, 7, '123'] --
        // Scratch considers 123 and '123' to be equal.
        for (let i = 0; i < list.value.length; i++) {
            if (Cast.compare(list.value[i], item) === 0) {
                return i + 1;
            }
        }

        // We don't bother using .indexOf() at all, because it would end up with
        // edge cases such as the index of '123' in [4, 7, 123, '123', 9].
        // If we use indexOf(), this block would return 4 instead of 3, because
        // indexOf() sees the first occurence of the string 123 as the fourth
        // item in the list. With Scratch, this would be confusing -- after all,
        // '123' and 123 look the same, so one would expect the block to say
        // that the first occurrence of '123' (or 123) to be the third item.

        // Default to 0 if there's no match. Since Scratch lists are 1-indexed,
        // we don't have to worry about this conflicting with the "this item is
        // the first value" number (in JS that is 0, but in Scratch it's 1).
        return 0;
    }

    lengthOfList (args, util) {
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        return list.value.length;
    }

    listContainsItem (args, util) {
        const item = args.ITEM;
        const list = util.target.lookupOrCreateList(
            args.LIST.id, args.LIST.name);
        if (list.value.indexOf(item) >= 0) {
            return true;
        }
        // Try using Scratch comparison operator on each item.
        // (Scratch considers the string '123' equal to the number 123).
        for (let i = 0; i < list.value.length; i++) {
            if (Cast.compare(list.value[i], item) === 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Type representation for list variables.
     * @const {string}
     */
    static get LIST_ITEM_LIMIT () {
        return 200000;
    }
}

module.exports = Scratch3DataBlocks;
<|endoftext|>


<commit_before>function mmg() {
    var l = {},
        // a list of our markers
        markers = null,
        // the absolute position of the parent element
        position = null,
        factory = null,
        // the last coordinate we saw on the map
        lastCoord = null;

    var parent = document.createElement('div');
    parent.style.cssText = 'position: absolute; top: 0px;' +
      'left: 0px; width: 100%; height: 100%; margin: 0; padding: 0; z-index: 0';

    markers = [];
    resetPosition();

    function defaultFactory(feature) {
        var d = document.createElement('div');
        d.className = 'mmg-default';
        return d;
    }

    // when panned, offset the position by the provided screen coordinate x
    // and y values
    function onPanned (dx, dy) {
        position.x += dx;
        position.y += dy;
        parent.style.left = ~~(position.x + 0.5) + 'px';
        parent.style.top = ~~(position.y + 0.5) + 'px';
    }

    // when zoomed, reset the position and reposition all markers
    function onZoomed() {
        resetPosition();
        repositionAllMarkers();
    }

    function fLocation (feature) {
        // GeoJSON
        var geom = feature.geometry;
        // coerce the lat and lon values, just in case
        var lon = Number(geom.coordinates[0]),
            lat = Number(geom.coordinates[1]);
        return new MM.Location(lat, lon);
    }

    // Reposition al markers
    function repositionAllMarkers() {
        var len = markers.length;
        for (var i = 0; i < len; i++) {
            repositionMarker(markers[i]);
        }
    }

    // reposition a single marker element
    function repositionMarker(marker) {
        // remember the tile coordinate so we don't have to reproject every time
        if (!marker.coord) marker.coord = l.map.locationCoordinate(marker.location);
        var pos = l.map.coordinatePoint(marker.coord);
        // offset by the layer parent position if x or y is non-zero
        if (position.x || position.y) {
            pos.x -= position.x;
            pox.y -= position.y;
        }
        marker.style.left = ~~(pos.x + 0.5) + "px";
        marker.style.top = ~~(pos.y + 0.5) + "px";
    }

    /**
     * Add an HTML element as a marker, located at the position of the
     * provided GeoJSON feature, Location instance (or {lat,lon} object
     * literal), or "lat,lon" string.
     */
    l.addMarker = function(marker, feature) {
        if (!marker || !feature) {
            return null;
        }
        // convert the feature to a Location instance
        marker.location = fLocation(feature);
        // position: absolute
        marker.style.position = 'absolute';
        // update the marker's position
        if (l.map) repositionMarker(marker);
        // append it to the DOM
        parent.appendChild(marker);
        // add it to the list
        markers.push(marker);
        return marker;
    };

    l.geojson = function(x) {
        if (!x) return markers;

        for (var i = 0; i < x.features.length; i++) {
            l.addMarker(factory(x.features[i]), x.features[i]);
        }
        return this;
    };

    l.draw = function() {
        // these are our previous and next map center coordinates
        var prev = lastCoord,
            next = l.map.pointCoordinate({x: 0, y: 0});
        // if we've recorded the map's previous center...
        if (prev) {
            // if the zoom hasn't changed, find the delta in screen
            // coordinates and pan the parent element
            if (prev.zoom == next.zoom) {
                var p1 = l.map.coordinatePoint(prev),
                    p2 = l.map.coordinatePoint(next),
                    dx = p1.x - p2.x,
                    dy = p1.y - p2.y;
                onPanned(dx, dy);
            // otherwise, reposition all the markers
            } else {
                onZoomed();
            }
        // otherwise, reposition all the markers
        } else {
            onZoomed();
        }
        // remember the previous center
        _lastCoord = next.copy();
    };

    // reset the absolute position of the layer's parent element
    function resetPosition() {
        position = new MM.Point(0, 0);
        parent.style.left = parent.style.top = "0px";
    }

    /**
     * Remove the element marker from the layer and the DOM.
     */
    l.removeMarker = function(marker) {
        var index = markers.indexOf(marker);
        if (index > -1) {
            markers.splice(index, 1);
        }
        if (marker.parentNode == parent) {
            parent.removeChild(marker);
        }
        return marker;
    };

    // remove all markers
    l.removeAllMarkers = function() {
        while (markers.length > 0) {
            removeMarker(markers[0]);
        }
    };

    l.factory = function(x) {
      if (!x) return factory;
      factory = x;
      return this;
    };

    l.parent = parent;

    l.factory(defaultFactory);

    return l;
}
<commit_msg>Porting improvements back from mapbox-light. Other-world problem fixed, optimizations moved around.<commit_after>function mmg() {
    var l = {},
        // a list of our markers
        markers = null,
        // the absolute position of the parent element
        position = null,
        factory = null,
        // map bounds
        left = null,
        right = null;

    var parent = document.createElement('div');
    parent.style.cssText = 'position: absolute; top: 0px;' +
      'left: 0px; width: 100%; height: 100%; margin: 0; padding: 0; z-index: 0';

    markers = [];

    function defaultFactory(feature) {
        var d = document.createElement('div');
        d.className = 'mmg-default';
        return d;
    }

    function fLocation (feature) {
        // GeoJSON
        var geom = feature.geometry;
        // coerce the lat and lon values, just in case
        var lon = Number(geom.coordinates[0]),
            lat = Number(geom.coordinates[1]);
        return new MM.Location(lat, lon);
    }

    // Reposition al markers
    function repositionAllMarkers() {
        left = l.map.pointLocation(new MM.Point(0, 0));
        right = l.map.pointLocation(new MM.Point(l.map.dimensions.x, 0));
        var len = markers.length;
        for (var i = 0; i < len; i++) {
            repositionMarker(markers[i]);
        }
    }

    // reposition a single marker element
    function repositionMarker(marker) {
        // remember the tile coordinate so we don't have to reproject every time
        if (!marker.coord) marker.coord = l.map.locationCoordinate(marker.location);
        var pos = l.map.coordinatePoint(marker.coord);
        var pos_loc;
        if (pos.x < 0) {
            pos_loc = marker.location.copy();
            pos_loc.lon += Math.ceil((left.lon - marker.location.lon) / 360) * 360;
            pos = l.map.locationPoint(pos_loc);
        } else if (pos.x > l.map.dimensions.x) {
            pos_loc = marker.location.copy();
            pos_loc.lon -= Math.ceil((marker.location.lon - right.lon) / 360) * 360;
            pos = l.map.locationPoint(pos_loc);
        }
        if (pos_loc) {
            marker.coord = l.map.locationCoordinate(pos_loc);
        }
        pos.scale = 1;
        pos.width = pos.height = 0;
        MM.moveElement(marker, pos);
    }

    /**
     * Add an HTML element as a marker, located at the position of the
     * provided GeoJSON feature, Location instance (or {lat,lon} object
     * literal), or "lat,lon" string.
     */
    l.addMarker = function(marker, feature) {
        if (!marker || !feature) {
            return null;
        }
        // convert the feature to a Location instance
        marker.location = fLocation(feature);
        // position: absolute
        marker.style.position = 'absolute';
        // update the marker's position
        if (l.map) repositionMarker(marker);
        // append it to the DOM
        parent.appendChild(marker);

        // add it to the list
        markers.push(marker);
        return marker;
    };

    l.geojson = function(x) {
        if (!x) return markers;

        for (var i = 0; i < x.features.length; i++) {
            l.addMarker(factory(x.features[i]), x.features[i]);
        }
        return this;
    };

    l.draw = function() {
        repositionAllMarkers();
    };

    /**
     * Remove the element marker from the layer and the DOM.
     */
    l.removeMarker = function(marker) {
        var index = markers.indexOf(marker);
        if (index > -1) {
            markers.splice(index, 1);
        }
        if (marker.parentNode == parent) {
            parent.removeChild(marker);
        }
        return marker;
    };

    // remove all markers
    l.removeAllMarkers = function() {
        while (markers.length > 0) {
            removeMarker(markers[0]);
        }
    };

    l.factory = function(x) {
      if (!x) return factory;
      factory = x;
      return this;
    };

    l.parent = parent;

    l.factory(defaultFactory);

    return l;
}
<|endoftext|>


<commit_before>var Lexer = require('./lexer')
  , Doctypes = require('./doctypes')
  , fs = require('fs')
  , path = require('path');

var Parser = module.exports = function Parser(str, options) {
  this.options = options;
  this.lexer = new Lexer(str);
  this.tokens = this.lexer.exec();
  this.buf = [];
}

Parser.prototype = {

  peek: function () {
    return this.tokens[0];
  },

  advance: function () {
    return this.current = this.tokens.shift();
  },

  buffer: function (str) {
    this.buf.push(str);
  },

  add: function (parser) {
    this.buf = this.buf.concat(parser.parse().buf);
  },

  doctype: function () {
    var doctype = this.advance().value
      .trim()
      .toLowerCase();
    if (doctype in Doctypes) {
      this.buffer(Doctypes[doctype]);
    } else {
      throw new Error("Doctype not found");
    }
  },

  tag: function () {
    var tag = this.advance().value;
    this.buffer('<' + tag + this.attrs() + '>');
    switch (this.peek().type) {
    case 'text':
      this.text();
      break;
    case 'variable':
      this.variable();
      break;
    case 'local':
      this.local();
      break;
    case 'include':
      this.include();
      break;
    case 'dot':
      this.dot();
      break;
    case 'indent':
      this.nested();
      break;
    }
    this.buffer('</' + tag + '>');
  },

  text: function () {
    var text = escape(this.advance().value)
      .trim()
      .replace(/#\{(.*)!\}/, locals('$1'))
      .replace(/#\{(.*)\}/, '{$1}');
    this.buffer(text);
  },

  local: function () {
    var lo = this.advance().value;
    this.buffer(locals(lo));
  },

  dot: function () {
    this.advance();
    if (this.peek().type == 'indent') {
      this.advance();
      while (this.peek().type !== 'outdent'
      && this.peek().type !== 'eos') {
        switch (this.peek().type) {
        case "newline":
          this.buffer("\n");
          break;
        default:
          this.buffer(escape(this.peek().match));
        }
        this.advance();
      }
      this.advance();
    } else {
      throw new Error("Indent must follow dot");
    }
  },

  attrs: function () {
    var attrs = []
      , classes = []
      , buffer = []
      , output = [];
    while (this.peek().type == 'attrs'
    || this.peek().type == 'id'
    || this.peek().type == 'class') {
      switch (this.peek().type) {
      case 'attrs':
        attrs.push(new Function("", "return {"+ this.peek().value +"};")());
        break;
      case 'id':
        attrs.push({"id": this.peek().value});
        break;
      case "class":
        attrs.push({class: this.peek().value});
        break;
      }
      this.advance();
    }
    for (var a in attrs) {
      for (var b in attrs[a]) {
        if (b == 'class') {
          classes.push(attrs[a][b]);
        } else {
          buffer[b] = attrs[a][b];
        }
      }
    }
    if (classes.length) {
      buffer.class = classes.join(" ");
    }
    for (var x in buffer) {
      output.push(x + '="' + buffer[x] + '"');
    }
    if (output.length) {
      return " "
        + output.join(" ");
    } else {
      return "";
    }
  },

  identifier: function () {
    this.tokens.unshift(this.lexer.token('tag', null, null, 'div'));
  },

  block: function () {
    var block = this.advance();
    this.buffer('{block:' + block.value + '}');

    switch (this.peek().type) {
    case 'indent':
      this.nested();
      break;
    default:
      throw new Error('A block must be followed by an indent');
    }

    this.buffer('{/block:' + block.value + '}');
  },

  include: function () {
    var include = this.advance();
    var filepath = path.resolve(this.options.basepath, include.value)
      , str = fs.readFileSync(filepath, this.options.encoding)
      , parser = new Parser(str, path.dirname(filepath));

    this.add(parser);
  },

  variable: function () {
    var variable = this.advance();
    this.buffer('{' + variable.value + '}');
  },

  nested: function () {
    this.advance();
    while (this.peek().type !== 'outdent' && this.peek().type !== 'eos') {
      this.expression();
    }
    if (this.peek().type == 'outdent') {
      return this.advance();
    }
  },

  expression: function () {
    switch (this.peek().type) {
    case 'indent':
    case 'outdent':
    case 'newline':
      this.advance();
      break;
    case 'doctype':
      this.doctype();
      break;
    case 'block':
      this.block();
      break;
    case 'include':
      this.include();
      break;
    case 'variable':
      this.variable();
      break;
    case 'local':
      this.local();
      break;
    case 'tag':
      this.tag();
      break;
    case 'id':
    case 'class':
      this.identifier();
      break;
    case 'text':
      this.text();
      break;
    default:
      throw new Error("Unexpected " + this.peek().type)
    }
  },

  js: function () {
    var js = [];
    js.push("with (locals || {}) {")
    js.push("  var buffer = [");
    js.push('    "' + this.buf.join('","') + '"');
    js.push("  ];");
    js.push("  return buffer.join('');");
    js.push("}")
    return js.join("\n");
  },

  parse: function () {
    while (this.peek().type !== 'eos') {
      this.expression();
    }
    return this;
  }

}

function escape(str) {
  return String(str)
    .replace(/&/g, '&')
    .replace(/>/g, '>')
    .replace(/</g, '<')
    .replace(/"/g, '"');
}

function locals(lo) {
  return '" + locals.' + lo + ' + "';
}
<commit_msg>local must exist or throw error<commit_after>var Lexer = require('./lexer')
  , Doctypes = require('./doctypes')
  , fs = require('fs')
  , path = require('path');

var Parser = module.exports = function Parser(str, options) {
  this.options = options;
  this.lexer = new Lexer(str);
  this.tokens = this.lexer.exec();
  this.buf = [];
}

Parser.prototype = {

  peek: function () {
    return this.tokens[0];
  },

  advance: function () {
    return this.current = this.tokens.shift();
  },

  buffer: function (str) {
    this.buf.push(str);
  },

  add: function (parser) {
    this.buf = this.buf.concat(parser.parse().buf);
  },

  doctype: function () {
    var doctype = this.advance().value
      .trim()
      .toLowerCase();
    if (doctype in Doctypes) {
      this.buffer(Doctypes[doctype]);
    } else {
      throw new Error("Doctype not found");
    }
  },

  tag: function () {
    var tag = this.advance().value;
    this.buffer('<' + tag + this.attrs() + '>');
    switch (this.peek().type) {
    case 'text':
      this.text();
      break;
    case 'variable':
      this.variable();
      break;
    case 'local':
      this.local();
      break;
    case 'include':
      this.include();
      break;
    case 'dot':
      this.dot();
      break;
    case 'indent':
      this.nested();
      break;
    }
    this.buffer('</' + tag + '>');
  },

  text: function () {
    var text = escape(this.advance().value)
      .trim()
      .replace(/#\{(.*)!\}/, locals('$1'))
      .replace(/#\{(.*)\}/, '{$1}');
    this.buffer(text);
  },

  local: function () {
    var lo = this.advance().value;
    this.buffer(locals(lo));
  },

  dot: function () {
    this.advance();
    if (this.peek().type == 'indent') {
      this.advance();
      while (this.peek().type !== 'outdent'
      && this.peek().type !== 'eos') {
        switch (this.peek().type) {
        case "newline":
          this.buffer("\n");
          break;
        default:
          this.buffer(escape(this.peek().match));
        }
        this.advance();
      }
      this.advance();
    } else {
      throw new Error("Indent must follow dot");
    }
  },

  attrs: function () {
    var attrs = []
      , classes = []
      , buffer = []
      , output = [];
    while (this.peek().type == 'attrs'
    || this.peek().type == 'id'
    || this.peek().type == 'class') {
      switch (this.peek().type) {
      case 'attrs':
        attrs.push(new Function("", "return {"+ this.peek().value +"};")());
        break;
      case 'id':
        attrs.push({"id": this.peek().value});
        break;
      case "class":
        attrs.push({class: this.peek().value});
        break;
      }
      this.advance();
    }
    for (var a in attrs) {
      for (var b in attrs[a]) {
        if (b == 'class') {
          classes.push(attrs[a][b]);
        } else {
          buffer[b] = attrs[a][b];
        }
      }
    }
    if (classes.length) {
      buffer.class = classes.join(" ");
    }
    for (var x in buffer) {
      output.push(x + '="' + buffer[x] + '"');
    }
    if (output.length) {
      return " "
        + output.join(" ");
    } else {
      return "";
    }
  },

  identifier: function () {
    this.tokens.unshift(this.lexer.token('tag', null, null, 'div'));
  },

  block: function () {
    var block = this.advance();
    this.buffer('{block:' + block.value + '}');

    switch (this.peek().type) {
    case 'indent':
      this.nested();
      break;
    default:
      throw new Error('A block must be followed by an indent');
    }

    this.buffer('{/block:' + block.value + '}');
  },

  include: function () {
    var include = this.advance();
    var filepath = path.resolve(this.options.basepath, include.value)
      , str = fs.readFileSync(filepath, this.options.encoding)
      , parser = new Parser(str, path.dirname(filepath));

    this.add(parser);
  },

  variable: function () {
    var variable = this.advance();
    this.buffer('{' + variable.value + '}');
  },

  nested: function () {
    this.advance();
    while (this.peek().type !== 'outdent' && this.peek().type !== 'eos') {
      this.expression();
    }
    if (this.peek().type == 'outdent') {
      return this.advance();
    }
  },

  expression: function () {
    switch (this.peek().type) {
    case 'indent':
    case 'outdent':
    case 'newline':
      this.advance();
      break;
    case 'doctype':
      this.doctype();
      break;
    case 'block':
      this.block();
      break;
    case 'include':
      this.include();
      break;
    case 'variable':
      this.variable();
      break;
    case 'local':
      this.local();
      break;
    case 'tag':
      this.tag();
      break;
    case 'id':
    case 'class':
      this.identifier();
      break;
    case 'text':
      this.text();
      break;
    default:
      throw new Error("Unexpected " + this.peek().type)
    }
  },

  js: function () {
    var js = [];
    js.push("with (locals || {}) {")
    js.push("  property = function(key) {");
    js.push("    if (locals[key] !== undefined) {");
    js.push("      return locals[key]");
    js.push("    } else {");
    js.push("      throw new ReferenceError(key + 'is not defined')");
    js.push("    }");
    js.push("  }");
    js.push("  var buffer = [");
    js.push('    "' + this.buf.join('","') + '"');
    js.push("  ];");
    js.push("  return buffer.join('');");
    js.push("}")
    return js.join("\n");
  },

  parse: function () {
    while (this.peek().type !== 'eos') {
      this.expression();
    }
    return this;
  }

}

function escape(str) {
  return String(str)
    .replace(/&/g, '&')
    .replace(/>/g, '>')
    .replace(/</g, '<')
    .replace(/"/g, '"');
}

function locals(lo) {
  return '" + property(' + lo + ') + "';
}
<|endoftext|>


<commit_before>'use strict'

// Random customer function
var times = ['6am', '7am', '8am', '9am', '10am', '11am', '12pm', '1pm', '2pm', '3pm', '4pm', '5pm', '6pm', '7pm'];
var salesTable = document.getElementById('cookieShops');

function Store(location, minCustomers, maxCustomers, avgCookiesPerSale){
  this.location = location;
  this.minCustomers = minCustomers;
  this.maxCustomers = maxCustomers;
  this.avgCookiesPerSale = avgCookiesPerSale;
  this.customersPerHour = [];
  this.calcCustomersPerHour = function(){
    for(var i = 0; i < times.length; i++){
      this.customersPerHour.push(Math.floor(Math.random() * (this.maxCustomers - this.minCustomers + 1)) + this.minCustomers)
    }
  };
  this.cookiesSoldEachHour = [];
  this.calcCookiesSoldEachHour = function(){
  this.calcCustomersPerHour();
    for(var i = 0; i < times.length; i++){
      this.cookiesSoldEachHour.push(Math.ceil(this.customersPerHour[i] * this.avgCookiesPerSale));
      this.totalCookiesPerDay += this.cookiesSoldEachHour[i];
    }
  };
  this.totalCookiesPerDay = 0;
  myStores.push(this);

    this.render = function(){
    var trEl = document.createElement('tr');

    var tdEl = document.createElement('td');
    tdEl.textContent = this.location;
    trEl.appendChild(tdEl);

    for(var i = 0; i < times.length; i++){
      var tdEl = document.createElement('td');
      tdEl.textContent = this.cookiesSoldEachHour[i];
      trEl.appendChild(tdEl);
    }

    var tdEl = document.createElement('td');
    tdEl.textContent = this.totalCookiesPerDay;
    trEl.appendChild(tdEl);

    salesTable.appendChild(trEl);
    }
    // this.render(myStores);
    this.calcCookiesSoldEachHour();
  };

var myStores = [];

new Store('1st and Pike', 23, 65, 6.3);
new Store('SeaTac', 3, 24, 1.2);
new Store('Seattle Center', 11, 38, 2.3);
new Store('Capitol Hill', 20, 38, 2.3);
new Store('Alki', 2, 16, 4.6);

function storesInTheDOM(){
  var storeList = document.getElementById('stores');
  for(var i = 0; i < myStores.length; i++){
    var liEl = document.createElement('li');
    liEl.textContent = myStores[i].location;
    storeList.appendChild(liEl);
  }
}
// storesInTheDOM();

function makeHeaderRow(){
  var trEl = document.createElement('tr');

  var thEl = document.createElement('th');
  thEl.textContent = 'location';
  trEl.appendChild(thEl);

  for(var i = 0; i < times.length; i++){
    var thEl = document.createElement('th');
    thEl.textContent = times[i];
    trEl.appendChild(thEl);
  };

  var thEl = document.createElement('th');
  thEl.textContent = 'Daily Location Totals';
  trEl.appendChild(thEl);

  salesTable.appendChild(trEl);
}

function allCookies(){
  for(var i = 0; i < myStores.length; i++){
    myStores[i].render();
  }
}
makeHeaderRow();
allCookies();
<commit_msg>got rid of unused code and comments<commit_after>'use strict'

var times = ['6am', '7am', '8am', '9am', '10am', '11am', '12pm', '1pm', '2pm', '3pm', '4pm', '5pm', '6pm', '7pm'];
var salesTable = document.getElementById('cookieShops');

function Store(location, minCustomers, maxCustomers, avgCookiesPerSale){
  this.location = location;
  this.minCustomers = minCustomers;
  this.maxCustomers = maxCustomers;
  this.avgCookiesPerSale = avgCookiesPerSale;
  this.customersPerHour = [];
  this.calcCustomersPerHour = function(){
    for(var i = 0; i < times.length; i++){
      this.customersPerHour.push(Math.floor(Math.random() * (this.maxCustomers - this.minCustomers + 1)) + this.minCustomers)
    }
  };
  this.cookiesSoldEachHour = [];
  this.calcCookiesSoldEachHour = function(){
  this.calcCustomersPerHour();
    for(var i = 0; i < times.length; i++){
      this.cookiesSoldEachHour.push(Math.ceil(this.customersPerHour[i] * this.avgCookiesPerSale));
      this.totalCookiesPerDay += this.cookiesSoldEachHour[i];
    }
  };
  this.totalCookiesPerDay = 0;
  myStores.push(this);

    this.render = function(){
    var trEl = document.createElement('tr');

    var tdEl = document.createElement('td');
    tdEl.textContent = this.location;
    trEl.appendChild(tdEl);

    for(var i = 0; i < times.length; i++){
      var tdEl = document.createElement('td');
      tdEl.textContent = this.cookiesSoldEachHour[i];
      trEl.appendChild(tdEl);
    }

    var tdEl = document.createElement('td');
    tdEl.textContent = this.totalCookiesPerDay;
    trEl.appendChild(tdEl);

    salesTable.appendChild(trEl);
    }
    this.calcCookiesSoldEachHour();
  };

var myStores = [];

new Store('1st and Pike', 23, 65, 6.3);
new Store('SeaTac', 3, 24, 1.2);
new Store('Seattle Center', 11, 38, 2.3);
new Store('Capitol Hill', 20, 38, 2.3);
new Store('Alki', 2, 16, 4.6);

function makeHeaderRow(){
  var trEl = document.createElement('tr');

  var thEl = document.createElement('th');
  thEl.textContent = 'location';
  trEl.appendChild(thEl);

  for(var i = 0; i < times.length; i++){
    var thEl = document.createElement('th');
    thEl.textContent = times[i];
    trEl.appendChild(thEl);
  };

  var thEl = document.createElement('th');
  thEl.textContent = 'Daily Location Totals';
  trEl.appendChild(thEl);

  salesTable.appendChild(trEl);
}

function allCookies(){
  for(var i = 0; i < myStores.length; i++){
    myStores[i].render();
  }
}
makeHeaderRow();
allCookies();
<|endoftext|>


<commit_before>// @flow

import Telegraf from 'telegraf';
// $FlowFixMe
import appConfig from '../../../app-config';
import { type MessageEvent, emitMessage } from '../../bus';
import botNetwork from './network';

import type { Config } from './config';

import type {
  Telegram$User,
  Telegram$Chat,
  Telegram$Message
} from './telegram.h';

type Context = {
  telegram: Telegraf$Telegram,
  updateType: string,
  updateSubTypes?: string,
  me?: string,
  message?: Telegram$Message,
  editedMessage?: Telegram$Message,
  // inlineQuery?: ,
  // chosenInlineResult?: ,
  // callbackQuery?: ,
  // shippingQuery?: ,
  // preCheckoutQuery?: ,
  // channelPost?: ,
  // editedChannelPost?: ,
  chat?: Telegram$Chat,
  from?: Telegram$User,
  match?: Array<?string>
};

const config: Config = appConfig.telegram;

const chat = config[process.env.NODE_ENV === 'prod' ? 'prod' : 'dev'];

const { BOT_TOKEN } = config;

const client = new Telegraf(BOT_TOKEN);

class Name {
  static from(user: Telegram$User): string {
    if (user.username) {
      return user.username;
    }

    if (user.last_name) {
      return `${user.first_name} ${user.last_name}`;
    }

    return user.first_name;
  }
}

class Message {
  msg: Telegram$Message;

  constructor(msg: Telegram$Message) {
    this.msg = msg;
  }

  toString(): string {
    return this.msg.text || '';
  }
}

class StickerMessage extends Message {
  static test(msg: Telegram$Message): boolean {
    return Boolean(msg.sticker);
  }

  toString(): string {
    const { msg } = this;

    let message: string = msg.text || '';

    const { sticker } = msg;

    if (!sticker) {
      return message;
    }

    const { emoji } = sticker;

    message += emoji ? `[Sticker ${emoji}]` : '';

    return message;
  }
}

class PhotoMessage extends Message {
  static test(msg: Telegram$Message): boolean {
    return Boolean(msg.photo);
  }

  toString(): string {
    const caption = this.msg.caption || '';
    const message = `[Photo] ${caption}`;

    return message;
  }
}

class DocumentMessage extends Message {
  static test(msg: Telegram$Message): boolean {
    return Boolean(msg.document);
  }

  toString(): string {
    const caption = this.msg.caption || '';
    const message = `[Document] ${caption}`;

    return message;
  }
}

class ReplyToMessage extends Message {
  static test(msg: Telegram$Message): boolean {
    return Boolean(msg.reply_to_message);
  }

  getDetails(): [string, string] {
    const { msg } = this;
    const replyToMessage: ?Telegram$Message = msg.reply_to_message;

    if (!replyToMessage) {
      return ['', ''];
    }

    const nick = Name.from(replyToMessage.from);
    const message: string = prepareMessage(replyToMessage);

    return [nick, message];
  }

  toString(): string {
    const { msg } = this;
    let [nick, message] = this.getDetails();

    // adds quoting brackets
    message = message.replace(/\n/g, '\n>> ');

    return `>> <${nick}> ${message}\n${msg.text || ''}`;
  }
}

class BotMessage extends ReplyToMessage {
  static test(msg: Telegram$Message): boolean {
    const replyToMessage: ?Telegram$Message = msg.reply_to_message;

    if (!replyToMessage) {
      return false;
    }

    const isReplyWithText = Boolean(replyToMessage.text);

    if (!isReplyWithText) {
      return false;
    }

    const isReplyFromOurBot = BOT_TOKEN.indexOf(
      replyToMessage.from.id.toString()
    ) > -1;

    return isReplyFromOurBot;
  }

  getDetails(): [string, string] {
    const { msg } = this;

    const replyToMessage: ?Telegram$Message = msg.reply_to_message;
    const text: string = replyToMessage ? (replyToMessage.text || '') : '';
    const lineEndPos: number = text.indexOf('\n');
    const _nick: string = text.slice(0, lineEndPos);
    const _message: string = text.slice(lineEndPos + 1);

    return [_nick, _message];
  }
}

function messageFactory(msg: Telegram$Message): Message {
  if (StickerMessage.test(msg)) {
    return new StickerMessage(msg);
  }

  if (PhotoMessage.test(msg)) {
    return new PhotoMessage(msg);
  }

  if (DocumentMessage.test(msg)) {
    return new DocumentMessage(msg);
  }

  if (!ReplyToMessage.test(msg)) {
    return new Message(msg);
  }

  if (BotMessage.test(msg)) {
    return new BotMessage(msg);
  }

  return new ReplyToMessage(msg);
}

function prepareMessage(msg: Telegram$Message): string {
  const message: Message = messageFactory(msg);
  let stringMessage = message.toString();

  if (msg.forward_from) {
    const nick = Name.from(msg.forward_from);

    // adds quoting brackets
    stringMessage = stringMessage.replace(/\n/g, '\n>> ');

    return `>> <${nick}> ${stringMessage}`;
  }

  return stringMessage;
}

function prepareEmittingMessageDetails(
  message: Telegram$Message
): ?MessageEvent {
  const room: string = message.chat.title || '';

  if (room !== chat.title) {
    return null;
  }

  const name = Name.from(message.from);
  const msg: string = prepareMessage(message);

  return { network: botNetwork, room, name, message: msg };
}

function onMessage(ctx: Context, next: (*) => Promise<*>): void {
  if (!ctx.message) {
    return;
  }

  const e: ?MessageEvent = prepareEmittingMessageDetails(ctx.message);

  if (e) {
    emitMessage(e);
    next();
  }
}

client
  .on('text', onMessage)
  .on('sticker', onMessage)
  .on('photo', onMessage)
  .on('document', onMessage)
  .startPolling();

export default client;
<commit_msg>telegram/client.js: refactor messageFactory<commit_after>// @flow

import Telegraf from 'telegraf';
// $FlowFixMe
import appConfig from '../../../app-config';
import { type MessageEvent, emitMessage } from '../../bus';
import botNetwork from './network';

import type { Config } from './config';

import type {
  Telegram$User,
  Telegram$Chat,
  Telegram$Message
} from './telegram.h';

type Context = {
  telegram: Telegraf$Telegram,
  updateType: string,
  updateSubTypes?: string,
  me?: string,
  message?: Telegram$Message,
  editedMessage?: Telegram$Message,
  // inlineQuery?: ,
  // chosenInlineResult?: ,
  // callbackQuery?: ,
  // shippingQuery?: ,
  // preCheckoutQuery?: ,
  // channelPost?: ,
  // editedChannelPost?: ,
  chat?: Telegram$Chat,
  from?: Telegram$User,
  match?: Array<?string>
};

const config: Config = appConfig.telegram;

const chat = config[process.env.NODE_ENV === 'prod' ? 'prod' : 'dev'];

const { BOT_TOKEN } = config;

const client = new Telegraf(BOT_TOKEN);

class Name {
  static from(user: Telegram$User): string {
    if (user.username) {
      return user.username;
    }

    if (user.last_name) {
      return `${user.first_name} ${user.last_name}`;
    }

    return user.first_name;
  }
}

class Message {
  msg: Telegram$Message;

  constructor(msg: Telegram$Message) {
    this.msg = msg;
  }

  toString(): string {
    return this.msg.text || '';
  }
}

class StickerMessage extends Message {
  static test(msg: Telegram$Message): boolean {
    return Boolean(msg.sticker);
  }

  toString(): string {
    const { msg } = this;

    let message: string = msg.text || '';

    const { sticker } = msg;

    if (!sticker) {
      return message;
    }

    const { emoji } = sticker;

    message += emoji ? `[Sticker ${emoji}]` : '';

    return message;
  }
}

class PhotoMessage extends Message {
  static test(msg: Telegram$Message): boolean {
    return Boolean(msg.photo);
  }

  toString(): string {
    const caption = this.msg.caption || '';
    const message = `[Photo] ${caption}`;

    return message;
  }
}

class DocumentMessage extends Message {
  static test(msg: Telegram$Message): boolean {
    return Boolean(msg.document);
  }

  toString(): string {
    const caption = this.msg.caption || '';
    const message = `[Document] ${caption}`;

    return message;
  }
}

class ReplyToMessage extends Message {
  static test(msg: Telegram$Message): boolean {
    return Boolean(msg.reply_to_message);
  }

  getDetails(): [string, string] {
    const { msg } = this;
    const replyToMessage: ?Telegram$Message = msg.reply_to_message;

    if (!replyToMessage) {
      return ['', ''];
    }

    const nick = Name.from(replyToMessage.from);
    const message: string = prepareMessage(replyToMessage);

    return [nick, message];
  }

  toString(): string {
    const { msg } = this;
    let [nick, message] = this.getDetails();

    // adds quoting brackets
    message = message.replace(/\n/g, '\n>> ');

    return `>> <${nick}> ${message}\n${msg.text || ''}`;
  }
}

class BotMessage extends ReplyToMessage {
  static test(msg: Telegram$Message): boolean {
    const replyToMessage: ?Telegram$Message = msg.reply_to_message;

    if (!replyToMessage) {
      return false;
    }

    const isReplyWithText = Boolean(replyToMessage.text);

    if (!isReplyWithText) {
      return false;
    }

    const isReplyFromOurBot = BOT_TOKEN.indexOf(
      replyToMessage.from.id.toString()
    ) > -1;

    return isReplyFromOurBot;
  }

  getDetails(): [string, string] {
    const { msg } = this;

    const replyToMessage: ?Telegram$Message = msg.reply_to_message;
    const text: string = replyToMessage ? (replyToMessage.text || '') : '';
    const lineEndPos: number = text.indexOf('\n');
    const _nick: string = text.slice(0, lineEndPos);
    const _message: string = text.slice(lineEndPos + 1);

    return [_nick, _message];
  }
}

function messageFactory(msg: Telegram$Message): Message {
  if (StickerMessage.test(msg)) {
    return new StickerMessage(msg);
  }

  if (PhotoMessage.test(msg)) {
    return new PhotoMessage(msg);
  }

  if (DocumentMessage.test(msg)) {
    return new DocumentMessage(msg);
  }

  if (BotMessage.test(msg)) {
    return new BotMessage(msg);
  }

  if (ReplyToMessage.test(msg)) {
    return new ReplyToMessage(msg);
  }

  return new Message(msg);
}

function prepareMessage(msg: Telegram$Message): string {
  const message: Message = messageFactory(msg);
  let stringMessage = message.toString();

  if (msg.forward_from) {
    const nick = Name.from(msg.forward_from);

    // adds quoting brackets
    stringMessage = stringMessage.replace(/\n/g, '\n>> ');

    return `>> <${nick}> ${stringMessage}`;
  }

  return stringMessage;
}

function prepareEmittingMessageDetails(
  message: Telegram$Message
): ?MessageEvent {
  const room: string = message.chat.title || '';

  if (room !== chat.title) {
    return null;
  }

  const name = Name.from(message.from);
  const msg: string = prepareMessage(message);

  return { network: botNetwork, room, name, message: msg };
}

function onMessage(ctx: Context, next: (*) => Promise<*>): void {
  if (!ctx.message) {
    return;
  }

  const e: ?MessageEvent = prepareEmittingMessageDetails(ctx.message);

  if (e) {
    emitMessage(e);
    next();
  }
}

client
  .on('text', onMessage)
  .on('sticker', onMessage)
  .on('photo', onMessage)
  .on('document', onMessage)
  .startPolling();

export default client;
<|endoftext|>


<commit_before>(function(ext) {
    // Cleanup function when the extension is unloaded
    ext._shutdown = function() {};

    // Status reporting code
    // Use this to report missing hardware, plugin or unsupported browser
    ext._getStatus = function() {
        return {status: 2, msg: 'Ready'};
    };

    ext.comment = function() {};

    // Block and block menu descriptions
    var descriptor = {
        blocks: [
            [' ', '/', 'comment'],
        ]
    };

    // Register the extension
    ScratchExtensions.register('Scratch-Mod', descriptor, ext);
})({});
<commit_msg>Update mod.js<commit_after>(function(ext) {
    // Cleanup function when the extension is unloaded
    ext._shutdown = function() {};

    // Status reporting code
    // Use this to report missing hardware, plugin or unsupported browser
    ext._getStatus = function() {
        return {status: 2, msg: 'Ready'};
    };

    ext.comment = function(string) {};

    // Block and block menu descriptions
    var descriptor = {
        blocks: [
            [' ', '/ %s', 'comment', 'comment'],
        ]
    };

    // Register the extension
    ScratchExtensions.register('Scratch-Mod', descriptor, ext);
})({});
<|endoftext|>


<commit_before>'use strict';

angular.module('angularOauth', []).

  provider('Token', function() {

    /**
     * Given an flat object, returns a query string for use in URLs.  Note
     * that for a given object, the return value may be.
     *
     * @example
     *

         // returns 'color=red&size=large'
         objectToQueryString({color: 'red', size: 'large'})
     *

     *
     * @param {Object} obj A flat object containing keys for such a string.
     * @returns {string} A string suitable as a query string.
     */
    var objectToQueryString = function(obj) {
      var str = [];
      angular.forEach(obj, function(value, key) {
        str.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
      });
      return str.join("&");
    };

    // This response_type MUST be passed to the authorization endpoint using
    // the implicit grant flow (4.2.1 of RFC 6749).
    var RESPONSE_TYPE = 'token';

    // Create a special object for config fields that are required and missing.
    // If any config items still contain it when Token is used, raise an error.
    var REQUIRED_AND_MISSING = {};

    var config = {
      clientId: REQUIRED_AND_MISSING,
      redirectUri: REQUIRED_AND_MISSING,
      authorizationEndpoint: REQUIRED_AND_MISSING,
      localStorageName: 'accessToken',
      verifyFunc: REQUIRED_AND_MISSING,
      scopes: []
    };

    this.extendConfig = function(configExtension) {
      config = angular.extend(config, configExtension);
    };

    this.$get = function($q, $http, $window, $rootScope) {
      var requiredAndMissing = [];
      angular.forEach(config, function(value, key) {
        if (value === REQUIRED_AND_MISSING) {
          requiredAndMissing.push(key);
        }
      });

      if (requiredAndMissing.length) {
        throw new Error("TokenProvider is insufficiently configured.  Please " +
          "configure the following options using " +
          "TokenProvider.extendConfig: " + requiredAndMissing.join(", "))
      }

      if (!config.clientId) {
        throw new Error("clientId needs to be configured using TokenProvider.");
      }

      var getParams = function() {
        // TODO: Facebook uses comma-delimited scopes. This is not compliant with section 3.3 but perhaps support later.

        return {
          response_type: RESPONSE_TYPE,
          client_id: config.clientId,
          redirect_uri: config.redirectUri,
          scope: config.scopes.join(" ")
        }
      };

      return {
        // TODO: get/set might want to support expiration to reauthenticate
        // TODO: check for localStorage support and otherwise perhaps use other methods of storing data (e.g. cookie)

        /**
         * Returns the stored access token.
         *
         * @returns {string} The access token.
         */
        get: function() {
          return localStorage[config.localStorageName];
        },

        /**
         * Persist the access token so that it can be retrieved later by.
         *
         * @param accessToken
         */
        set: function(accessToken) {
          localStorage[config.localStorageName] = accessToken;
        },

        /**
         * Verifies that the access token is was issued for the use of the current client.
         *
         * @param accessToken An access token received from the authorization server.
         * @returns {Promise} Promise that will be resolved when the authorization server has verified that the
         *  token is valid, and we've verified that the token is passed back has audience that matches our client
         *  ID (to prevent the Confused Deputy Problem).
         *
         *  If there's an error verifying the token, the promise is rejected with an object identifying the `name` error
         *  in the name member.  The `name` can be either:
         *
         *    - `invalid_audience`: The audience didn't match our client ID.
         *    - `error_response`: The server responded with an error, typically because the token was invalid.  In this
         *      case, the callback parameters to `error` callback on `$http` are available in the object (`data`,
         *      `status`, `headers`, `config`).
         */
        verifyAsync: function(accessToken) {
          return config.verifyFunc(config, accessToken);
        },

        /**
         * Verifies an access token asynchronously.
         *
         * @param extraParams An access token received from the authorization server.
         * @param popupOptions Settings for the display of the popup.
         * @returns {Promise} Promise that will be resolved when the authorization server has verified that the
         *  token is valid, and we've verified that the token is passed back has audience that matches our client
         *  ID (to prevent the Confused Deputy Problem).
         *
         *  If there's an error verifying the token, the promise is rejected with an object identifying the `name` error
         *  in the name member.  The `name` can be either:
         *
         *    - `invalid_audience`: The audience didn't match our client ID.
         *    - `error_response`: The server responded with an error, typically because the token was invalid.  In this
         *      case, the callback parameters to `error` callback on `$http` are available in the object (`data`,
         *      `status`, `headers`, `config`).
         */
        getTokenByPopup: function(extraParams, popupOptions) {
          popupOptions = angular.extend({
            name: 'AuthPopup',
            openParams: {
              width: 650,
              height: 300,
              resizable: true,
              scrollbars: true,
              status: true
            }
          }, popupOptions);

          var deferred = $q.defer(),
            params = angular.extend(getParams(), extraParams),
            url = config.authorizationEndpoint + '?' + objectToQueryString(params),
            resolved = false;

          var formatPopupOptions = function(options) {
            var pairs = [];
            angular.forEach(options, function(value, key) {
              if (value || value === 0) {
                value = value === true ? 'yes' : value;
                pairs.push(key + '=' + value);
              }
            });
            return pairs.join(',');
          };

          var popup = window.open(url, popupOptions.name, formatPopupOptions(popupOptions.openParams));

          // TODO: binding occurs for each reauthentication, leading to leaks for long-running apps.

          angular.element($window).bind('message', function(event) {
            if (event.source == popup && event.origin == window.location.origin) {
              $rootScope.$apply(function() {
                if (event.data.access_token) {
                  deferred.resolve(event.data)
                } else {
                  deferred.reject(event.data)
                }
              })
            }
          });

          // TODO: reject deferred if the popup was closed without a message being delivered + maybe offer a timeout

          return deferred.promise;
        }
      }
    }
  }).

  /**
   * A controller for the redirect endpoint that inspects the URL redirected to by the authorization server and sends
   * it back to other windows using.
   */
  controller('CallbackCtrl', function($scope, $location) {

    /**
     * Parses an escaped url query string into key-value pairs.
     *
     * (Copied from Angular.js in the AngularJS project.)
     *
     * @returns Object.<(string|boolean)>
     */
    function parseKeyValue(/**string*/keyValue) {
      var obj = {}, key_value, key;
      angular.forEach((keyValue || "").split('&'), function(keyValue){
        if (keyValue) {
          key_value = keyValue.split('=');
          key = decodeURIComponent(key_value[0]);
          obj[key] = angular.isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true;
        }
      });
      return obj;
    }

    var queryString = $location.path().substring(1);  // preceding slash omitted
    var params = parseKeyValue(queryString);

    // TODO: The target origin should be set to an explicit origin.  Otherwise, a malicious site that can receive
    //       the token if it manages to change the location of the parent. (See:
    //       https://developer.mozilla.org/en/docs/DOM/window.postMessage#Security_concerns)

    window.opener.postMessage(params, "*");
    window.close();
  });
<commit_msg>Use JQuery originalEvent if present<commit_after>'use strict';

angular.module('angularOauth', []).

  provider('Token', function() {

    /**
     * Given an flat object, returns a query string for use in URLs.  Note
     * that for a given object, the return value may be.
     *
     * @example
     *

         // returns 'color=red&size=large'
         objectToQueryString({color: 'red', size: 'large'})
     *

     *
     * @param {Object} obj A flat object containing keys for such a string.
     * @returns {string} A string suitable as a query string.
     */
    var objectToQueryString = function(obj) {
      var str = [];
      angular.forEach(obj, function(value, key) {
        str.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
      });
      return str.join("&");
    };

    // This response_type MUST be passed to the authorization endpoint using
    // the implicit grant flow (4.2.1 of RFC 6749).
    var RESPONSE_TYPE = 'token';

    // Create a special object for config fields that are required and missing.
    // If any config items still contain it when Token is used, raise an error.
    var REQUIRED_AND_MISSING = {};

    var config = {
      clientId: REQUIRED_AND_MISSING,
      redirectUri: REQUIRED_AND_MISSING,
      authorizationEndpoint: REQUIRED_AND_MISSING,
      localStorageName: 'accessToken',
      verifyFunc: REQUIRED_AND_MISSING,
      scopes: []
    };

    this.extendConfig = function(configExtension) {
      config = angular.extend(config, configExtension);
    };

    this.$get = function($q, $http, $window, $rootScope) {
      var requiredAndMissing = [];
      angular.forEach(config, function(value, key) {
        if (value === REQUIRED_AND_MISSING) {
          requiredAndMissing.push(key);
        }
      });

      if (requiredAndMissing.length) {
        throw new Error("TokenProvider is insufficiently configured.  Please " +
          "configure the following options using " +
          "TokenProvider.extendConfig: " + requiredAndMissing.join(", "))
      }

      if (!config.clientId) {
        throw new Error("clientId needs to be configured using TokenProvider.");
      }

      var getParams = function() {
        // TODO: Facebook uses comma-delimited scopes. This is not compliant with section 3.3 but perhaps support later.

        return {
          response_type: RESPONSE_TYPE,
          client_id: config.clientId,
          redirect_uri: config.redirectUri,
          scope: config.scopes.join(" ")
        }
      };

      return {
        // TODO: get/set might want to support expiration to reauthenticate
        // TODO: check for localStorage support and otherwise perhaps use other methods of storing data (e.g. cookie)

        /**
         * Returns the stored access token.
         *
         * @returns {string} The access token.
         */
        get: function() {
          return localStorage[config.localStorageName];
        },

        /**
         * Persist the access token so that it can be retrieved later by.
         *
         * @param accessToken
         */
        set: function(accessToken) {
          localStorage[config.localStorageName] = accessToken;
        },

        /**
         * Verifies that the access token is was issued for the use of the current client.
         *
         * @param accessToken An access token received from the authorization server.
         * @returns {Promise} Promise that will be resolved when the authorization server has verified that the
         *  token is valid, and we've verified that the token is passed back has audience that matches our client
         *  ID (to prevent the Confused Deputy Problem).
         *
         *  If there's an error verifying the token, the promise is rejected with an object identifying the `name` error
         *  in the name member.  The `name` can be either:
         *
         *    - `invalid_audience`: The audience didn't match our client ID.
         *    - `error_response`: The server responded with an error, typically because the token was invalid.  In this
         *      case, the callback parameters to `error` callback on `$http` are available in the object (`data`,
         *      `status`, `headers`, `config`).
         */
        verifyAsync: function(accessToken) {
          return config.verifyFunc(config, accessToken);
        },

        /**
         * Verifies an access token asynchronously.
         *
         * @param extraParams An access token received from the authorization server.
         * @param popupOptions Settings for the display of the popup.
         * @returns {Promise} Promise that will be resolved when the authorization server has verified that the
         *  token is valid, and we've verified that the token is passed back has audience that matches our client
         *  ID (to prevent the Confused Deputy Problem).
         *
         *  If there's an error verifying the token, the promise is rejected with an object identifying the `name` error
         *  in the name member.  The `name` can be either:
         *
         *    - `invalid_audience`: The audience didn't match our client ID.
         *    - `error_response`: The server responded with an error, typically because the token was invalid.  In this
         *      case, the callback parameters to `error` callback on `$http` are available in the object (`data`,
         *      `status`, `headers`, `config`).
         */
        getTokenByPopup: function(extraParams, popupOptions) {
          popupOptions = angular.extend({
            name: 'AuthPopup',
            openParams: {
              width: 650,
              height: 300,
              resizable: true,
              scrollbars: true,
              status: true
            }
          }, popupOptions);

          var deferred = $q.defer(),
            params = angular.extend(getParams(), extraParams),
            url = config.authorizationEndpoint + '?' + objectToQueryString(params),
            resolved = false;

          var formatPopupOptions = function(options) {
            var pairs = [];
            angular.forEach(options, function(value, key) {
              if (value || value === 0) {
                value = value === true ? 'yes' : value;
                pairs.push(key + '=' + value);
              }
            });
            return pairs.join(',');
          };

          var popup = window.open(url, popupOptions.name, formatPopupOptions(popupOptions.openParams));

          // TODO: binding occurs for each reauthentication, leading to leaks for long-running apps.

          angular.element($window).bind('message', function(event) {
            // Use JQuery originalEvent if present
            event = event.originalEvent || event;
            if (event.source == popup && event.origin == window.location.origin) {
              $rootScope.$apply(function() {
                if (event.data.access_token) {
                  deferred.resolve(event.data)
                } else {
                  deferred.reject(event.data)
                }
              })
            }
          });

          // TODO: reject deferred if the popup was closed without a message being delivered + maybe offer a timeout

          return deferred.promise;
        }
      }
    }
  }).

  /**
   * A controller for the redirect endpoint that inspects the URL redirected to by the authorization server and sends
   * it back to other windows using.
   */
  controller('CallbackCtrl', function($scope, $location) {

    /**
     * Parses an escaped url query string into key-value pairs.
     *
     * (Copied from Angular.js in the AngularJS project.)
     *
     * @returns Object.<(string|boolean)>
     */
    function parseKeyValue(/**string*/keyValue) {
      var obj = {}, key_value, key;
      angular.forEach((keyValue || "").split('&'), function(keyValue){
        if (keyValue) {
          key_value = keyValue.split('=');
          key = decodeURIComponent(key_value[0]);
          obj[key] = angular.isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true;
        }
      });
      return obj;
    }

    var queryString = $location.path().substring(1);  // preceding slash omitted
    var params = parseKeyValue(queryString);

    // TODO: The target origin should be set to an explicit origin.  Otherwise, a malicious site that can receive
    //       the token if it manages to change the location of the parent. (See:
    //       https://developer.mozilla.org/en/docs/DOM/window.postMessage#Security_concerns)

    window.opener.postMessage(params, "*");
    window.close();
  });
<|endoftext|>