<commit_before>dojo.provide("dijit.tree.TreeStoreModel");

dojo.declare(
		"dijit.tree.TreeStoreModel",
		null,
	{
		// summary:
		//		Implements dijit.Tree.model connecting to a store with a single
		//		root item.  Any methods passed into the constructor will override
		//		the ones defined here.

		// store: dojo.data.Store
		//		Underlying store
		store: null,

		// childrenAttrs: String[]
		//		One or more attribute names (attributes in the dojo.data item) that specify that item's children
		childrenAttrs: ["children"],

		// newItemIdAttr: String
		//		Name of attribute in the Object passed to newItem() that specifies the id.
		//
		//		If newItemIdAttr is set then it's used when newItem() is called to see if an
		//		item with the same id already exists, and if so just links to the old item
		//		(so that the old item ends up with two parents).
		//
		//		Setting this to null or "" will make every drop create a new item.
		newItemIdAttr: "id",

		// labelAttr: String
		//		If specified, get label for tree node from this attribute, rather
		//		than by calling store.getLabel()
		labelAttr: "",

	 	// root: [readonly] dojo.data.Item
		//		Pointer to the root item (read only, not a parameter)
		root: null,

		// query: anything
		//		Specifies datastore query to return the root item for the tree.
		//		Must only return a single item.   Alternately can just pass in pointer
		//		to root item.
		// example:
		//	|	{id:'ROOT'}
		query: null,

		// deferItemLoadingUntilExpand: Boolean
		//		Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
		// 		until they are expanded. This allows for lazying loading where only one
		//		loadItem (and generally one network call, consequently) per expansion
		// 		(rather than one for each child).
		// 		This relies on partial loading of the children items; each children item of a
		// 		fully loaded item should contain the label and info about having children.
		deferItemLoadingUntilExpand: false,

		constructor: function(/* Object */ args){
			// summary:
			//		Passed the arguments listed above (store, etc)
			// tags:
			//		private

			dojo.mixin(this, args);

			this.connects = [];

			var store = this.store;
			if(!store.getFeatures()['dojo.data.api.Identity']){
				throw new Error("dijit.Tree: store must support dojo.data.Identity");
			}

			// if the store supports Notification, subscribe to the notification events
			if(store.getFeatures()['dojo.data.api.Notification']){
				this.connects = this.connects.concat([
					dojo.connect(store, "onNew", this, "onNewItem"),
					dojo.connect(store, "onDelete", this, "onDeleteItem"),
					dojo.connect(store, "onSet", this, "onSetItem")
				]);
			}
		},

		destroy: function(){
			dojo.forEach(this.connects, dojo.disconnect);
			// TODO: should cancel any in-progress processing of getRoot(), getChildren()
		},

		// =======================================================================
		// Methods for traversing hierarchy

		getRoot: function(onItem, onError){
			// summary:
			//		Calls onItem with the root item for the tree, possibly a fabricated item.
			//		Calls onError on error.
			if(this.root){
				onItem(this.root);
			}else{
				this.store.fetch({
					query: this.query,
					onComplete: dojo.hitch(this, function(items){
						if(items.length != 1){
							throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
							 	" items, but must return exactly one item");
						}
						this.root = items[0];
						onItem(this.root);
					}),
					onError: onError
				});
			}
		},

		mayHaveChildren: function(/*dojo.data.Item*/ item){
			// summary:
			//		Tells if an item has or may have children.  Implementing logic here
			//		avoids showing +/- expando icon for nodes that we know don't have children.
			//		(For efficiency reasons we may not want to check if an element actually
			//		has children until user clicks the expando node)
			return dojo.some(this.childrenAttrs, function(attr){
				return this.store.hasAttribute(item, attr);
			}, this);
		},

		getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
			// summary:
			// 		Calls onComplete() with array of child items of given parent item, all loaded.

			var store = this.store;
			if(!store.isItemLoaded(parentItem)){
				// The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
				// mode, so we will load it and just return the children (without loading each
				// child item)
				var getChildren = dojo.hitch(this, arguments.callee);
				store.loadItem({
					item: parentItem,
					onItem: function(parentItem){
						getChildren(parentItem, onComplete, onError);
					},
					onError: onError
				});
				return;
			}
			// get children of specified item
			var childItems = [];
			for(var i=0; i<this.childrenAttrs.length; i++){
				var vals = store.getValues(parentItem, this.childrenAttrs[i]);
				childItems = childItems.concat(vals);
			}

			// count how many items need to be loaded
			var _waitCount = 0;
			if(!this.deferItemLoadingUntilExpand){
				dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
			}

			if(_waitCount == 0){
				// all items are already loaded (or we aren't loading them).  proceed...
				onComplete(childItems);
			}else{
				// still waiting for some or all of the items to load
				var onItem = function onItem(item){
					if(--_waitCount == 0){
						// all nodes have been loaded, send them to the tree
						onComplete(childItems);
					}
				}
				dojo.forEach(childItems, function(item){
					if(!store.isItemLoaded(item)){
						store.loadItem({
							item: item,
							onItem: onItem,
							onError: onError
						});
					}
				});
			}
		},

		// =======================================================================
		// Inspecting items

		isItem: function(/* anything */ something){
			return this.store.isItem(something);	// Boolean
		},

		fetchItemByIdentity: function(/* object */ keywordArgs){
			this.store.fetchItemByIdentity(keywordArgs);
		},

		getIdentity: function(/* item */ item){
			return this.store.getIdentity(item);	// Object
		},

		getLabel: function(/*dojo.data.Item*/ item){
			// summary:
			//		Get the label for an item
			if(this.labelAttr){
				return this.store.getValue(item,this.labelAttr);	// String
			}else{
				return this.store.getLabel(item);	// String
			}
		},

		// =======================================================================
		// Write interface

		newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
			// summary:
			//		Creates a new item.   See `dojo.data.api.Write` for details on args.
			//		Used in drag & drop when item from external source dropped onto tree.
			// description:
			//		Developers will need to override this method if new items get added
			//		to parents with multiple children attributes, in order to define which
			//		children attribute points to the new item.

			var pInfo = {parent: parent, attribute: this.childrenAttrs[0], insertIndex: insertIndex};

			if(this.newItemIdAttr && args[this.newItemIdAttr]){
				// Maybe there's already a corresponding item in the store; if so, reuse it.
				this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
					if(item){
						// There's already a matching item in store, use it
						this.pasteItem(item, null, parent, true, insertIndex);
					}else{
						// Create new item in the tree, based on the drag source.
						this.store.newItem(args, pInfo);
					}
				}});
			}else{
				// [as far as we know] there is no id so we must assume this is a new item
				this.store.newItem(args, pInfo);
			}
		},

		pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
			// summary:
			//		Move or copy an item from one parent item to another.
			//		Used in drag & drop
			var store = this.store,
				parentAttr = this.childrenAttrs[0];	// name of "children" attr in parent item

			// remove child from source item, and record the attribute that child occurred in
			if(oldParentItem){
				dojo.forEach(this.childrenAttrs, function(attr){
					if(store.containsValue(oldParentItem, attr, childItem)){
						if(!bCopy){
							var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){
								return x != childItem;
							});
							store.setValues(oldParentItem, attr, values);
						}
						parentAttr = attr;
					}
				});
			}

			// modify target item's children attribute to include this item
			if(newParentItem){
				if(typeof insertIndex == "number"){
					var childItems = store.getValues(newParentItem, parentAttr);
					childItems.splice(insertIndex, 0, childItem);
					store.setValues(newParentItem, parentAttr, childItems);
				}else{
				store.setValues(newParentItem, parentAttr,
					store.getValues(newParentItem, parentAttr).concat(childItem));
			}
			}
		},

		// =======================================================================
		// Callbacks

		onChange: function(/*dojo.data.Item*/ item){
			// summary:
			//		Callback whenever an item has changed, so that Tree
			//		can update the label, icon, etc.   Note that changes
			//		to an item's children or parent(s) will trigger an
			//		onChildrenChange() so you can ignore those changes here.
			// tags:
			//		callback
		},

		onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
			// summary:
			//		Callback to do notifications about new, updated, or deleted items.
			// tags:
			//		callback
		},

		onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
			// summary:
			//		Callback when an item has been deleted.
			// description:
			//		Note that there will also be an onChildrenChange() callback for the parent
			//		of this item.
			// tags:
			//		callback
		},

		// =======================================================================
		// Events from data store

		onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
			// summary:
			//		Handler for when new items appear in the store, either from a drop operation
			//		or some other way.   Updates the tree view (if necessary).
			// description:
			//		If the new item is a child of an existing item,
			//		calls onChildrenChange() with the new list of children
			//		for that existing item.
			//
			// tags:
			//		extension

			// We only care about the new item if it has a parent that corresponds to a TreeNode
			// we are currently displaying
			if(!parentInfo){
				return;
			}

			// Call onChildrenChange() on parent (ie, existing) item with new list of children
			// In the common case, the new list of children is simply parentInfo.newValue or
			// [ parentInfo.newValue ], although if items in the store has multiple
			// child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
			// so call getChildren() to be sure to get right answer.
			this.getChildren(parentInfo.item, dojo.hitch(this, function(children){
				this.onChildrenChange(parentInfo.item, children);
			}));
		},

		onDeleteItem: function(/*Object*/ item){
			// summary:
			//		Handler for delete notifications from underlying store
			this.onDelete(item);
		},

		onSetItem: function(/* item */ item,
						/* attribute-name-string */ attribute,
						/* object | array */ oldValue,
						/* object | array */ newValue){
			// summary:
			//		Updates the tree view according to changes in the data store.
			// description:
			//		Handles updates to an item's children by calling onChildrenChange(), and
			//		other updates to an item by calling onChange().
			//
			//		See `onNewItem` for more details on handling updates to an item's children.
			// tags:
			//		extension

			if(dojo.indexOf(this.childrenAttrs, attribute) != -1){
				// item's children list changed
				this.getChildren(item, dojo.hitch(this, function(children){
					// See comments in onNewItem() about calling getChildren()
					this.onChildrenChange(item, children);
				}));
			}else{
				// item's label/icon/etc. changed.
				this.onChange(item);
			}
		}
	});

<commit_msg>Usually loadItem(item) just fills in attribute inside the specified Object, but theoretically onItem(item) could return a completely new object.  Refs #10569 !strict.<commit_after>dojo.provide("dijit.tree.TreeStoreModel");

dojo.declare(
		"dijit.tree.TreeStoreModel",
		null,
	{
		// summary:
		//		Implements dijit.Tree.model connecting to a store with a single
		//		root item.  Any methods passed into the constructor will override
		//		the ones defined here.

		// store: dojo.data.Store
		//		Underlying store
		store: null,

		// childrenAttrs: String[]
		//		One or more attribute names (attributes in the dojo.data item) that specify that item's children
		childrenAttrs: ["children"],

		// newItemIdAttr: String
		//		Name of attribute in the Object passed to newItem() that specifies the id.
		//
		//		If newItemIdAttr is set then it's used when newItem() is called to see if an
		//		item with the same id already exists, and if so just links to the old item
		//		(so that the old item ends up with two parents).
		//
		//		Setting this to null or "" will make every drop create a new item.
		newItemIdAttr: "id",

		// labelAttr: String
		//		If specified, get label for tree node from this attribute, rather
		//		than by calling store.getLabel()
		labelAttr: "",

	 	// root: [readonly] dojo.data.Item
		//		Pointer to the root item (read only, not a parameter)
		root: null,

		// query: anything
		//		Specifies datastore query to return the root item for the tree.
		//		Must only return a single item.   Alternately can just pass in pointer
		//		to root item.
		// example:
		//	|	{id:'ROOT'}
		query: null,

		// deferItemLoadingUntilExpand: Boolean
		//		Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
		// 		until they are expanded. This allows for lazying loading where only one
		//		loadItem (and generally one network call, consequently) per expansion
		// 		(rather than one for each child).
		// 		This relies on partial loading of the children items; each children item of a
		// 		fully loaded item should contain the label and info about having children.
		deferItemLoadingUntilExpand: false,

		constructor: function(/* Object */ args){
			// summary:
			//		Passed the arguments listed above (store, etc)
			// tags:
			//		private

			dojo.mixin(this, args);

			this.connects = [];

			var store = this.store;
			if(!store.getFeatures()['dojo.data.api.Identity']){
				throw new Error("dijit.Tree: store must support dojo.data.Identity");
			}

			// if the store supports Notification, subscribe to the notification events
			if(store.getFeatures()['dojo.data.api.Notification']){
				this.connects = this.connects.concat([
					dojo.connect(store, "onNew", this, "onNewItem"),
					dojo.connect(store, "onDelete", this, "onDeleteItem"),
					dojo.connect(store, "onSet", this, "onSetItem")
				]);
			}
		},

		destroy: function(){
			dojo.forEach(this.connects, dojo.disconnect);
			// TODO: should cancel any in-progress processing of getRoot(), getChildren()
		},

		// =======================================================================
		// Methods for traversing hierarchy

		getRoot: function(onItem, onError){
			// summary:
			//		Calls onItem with the root item for the tree, possibly a fabricated item.
			//		Calls onError on error.
			if(this.root){
				onItem(this.root);
			}else{
				this.store.fetch({
					query: this.query,
					onComplete: dojo.hitch(this, function(items){
						if(items.length != 1){
							throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
							 	" items, but must return exactly one item");
						}
						this.root = items[0];
						onItem(this.root);
					}),
					onError: onError
				});
			}
		},

		mayHaveChildren: function(/*dojo.data.Item*/ item){
			// summary:
			//		Tells if an item has or may have children.  Implementing logic here
			//		avoids showing +/- expando icon for nodes that we know don't have children.
			//		(For efficiency reasons we may not want to check if an element actually
			//		has children until user clicks the expando node)
			return dojo.some(this.childrenAttrs, function(attr){
				return this.store.hasAttribute(item, attr);
			}, this);
		},

		getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
			// summary:
			// 		Calls onComplete() with array of child items of given parent item, all loaded.

			var store = this.store;
			if(!store.isItemLoaded(parentItem)){
				// The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
				// mode, so we will load it and just return the children (without loading each
				// child item)
				var getChildren = dojo.hitch(this, arguments.callee);
				store.loadItem({
					item: parentItem,
					onItem: function(parentItem){
						getChildren(parentItem, onComplete, onError);
					},
					onError: onError
				});
				return;
			}
			// get children of specified item
			var childItems = [];
			for(var i=0; i<this.childrenAttrs.length; i++){
				var vals = store.getValues(parentItem, this.childrenAttrs[i]);
				childItems = childItems.concat(vals);
			}

			// count how many items need to be loaded
			var _waitCount = 0;
			if(!this.deferItemLoadingUntilExpand){
				dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
			}

			if(_waitCount == 0){
				// all items are already loaded (or we aren't loading them).  proceed...
				onComplete(childItems);
			}else{
				// still waiting for some or all of the items to load
				dojo.forEach(childItems, function(item, idx){
					if(!store.isItemLoaded(item)){
						store.loadItem({
							item: function onItem(item){
								childItems[idx] = item;
								if(--_waitCount == 0){
									// all nodes have been loaded, send them to the tree
									onComplete(childItems);
								}
							},
							onItem: onItem,
							onError: onError
						});
					}
				});
			}
		},

		// =======================================================================
		// Inspecting items

		isItem: function(/* anything */ something){
			return this.store.isItem(something);	// Boolean
		},

		fetchItemByIdentity: function(/* object */ keywordArgs){
			this.store.fetchItemByIdentity(keywordArgs);
		},

		getIdentity: function(/* item */ item){
			return this.store.getIdentity(item);	// Object
		},

		getLabel: function(/*dojo.data.Item*/ item){
			// summary:
			//		Get the label for an item
			if(this.labelAttr){
				return this.store.getValue(item,this.labelAttr);	// String
			}else{
				return this.store.getLabel(item);	// String
			}
		},

		// =======================================================================
		// Write interface

		newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
			// summary:
			//		Creates a new item.   See `dojo.data.api.Write` for details on args.
			//		Used in drag & drop when item from external source dropped onto tree.
			// description:
			//		Developers will need to override this method if new items get added
			//		to parents with multiple children attributes, in order to define which
			//		children attribute points to the new item.

			var pInfo = {parent: parent, attribute: this.childrenAttrs[0], insertIndex: insertIndex};

			if(this.newItemIdAttr && args[this.newItemIdAttr]){
				// Maybe there's already a corresponding item in the store; if so, reuse it.
				this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
					if(item){
						// There's already a matching item in store, use it
						this.pasteItem(item, null, parent, true, insertIndex);
					}else{
						// Create new item in the tree, based on the drag source.
						this.store.newItem(args, pInfo);
					}
				}});
			}else{
				// [as far as we know] there is no id so we must assume this is a new item
				this.store.newItem(args, pInfo);
			}
		},

		pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
			// summary:
			//		Move or copy an item from one parent item to another.
			//		Used in drag & drop
			var store = this.store,
				parentAttr = this.childrenAttrs[0];	// name of "children" attr in parent item

			// remove child from source item, and record the attribute that child occurred in
			if(oldParentItem){
				dojo.forEach(this.childrenAttrs, function(attr){
					if(store.containsValue(oldParentItem, attr, childItem)){
						if(!bCopy){
							var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){
								return x != childItem;
							});
							store.setValues(oldParentItem, attr, values);
						}
						parentAttr = attr;
					}
				});
			}

			// modify target item's children attribute to include this item
			if(newParentItem){
				if(typeof insertIndex == "number"){
					var childItems = store.getValues(newParentItem, parentAttr);
					childItems.splice(insertIndex, 0, childItem);
					store.setValues(newParentItem, parentAttr, childItems);
				}else{
				store.setValues(newParentItem, parentAttr,
					store.getValues(newParentItem, parentAttr).concat(childItem));
			}
			}
		},

		// =======================================================================
		// Callbacks

		onChange: function(/*dojo.data.Item*/ item){
			// summary:
			//		Callback whenever an item has changed, so that Tree
			//		can update the label, icon, etc.   Note that changes
			//		to an item's children or parent(s) will trigger an
			//		onChildrenChange() so you can ignore those changes here.
			// tags:
			//		callback
		},

		onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
			// summary:
			//		Callback to do notifications about new, updated, or deleted items.
			// tags:
			//		callback
		},

		onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
			// summary:
			//		Callback when an item has been deleted.
			// description:
			//		Note that there will also be an onChildrenChange() callback for the parent
			//		of this item.
			// tags:
			//		callback
		},

		// =======================================================================
		// Events from data store

		onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
			// summary:
			//		Handler for when new items appear in the store, either from a drop operation
			//		or some other way.   Updates the tree view (if necessary).
			// description:
			//		If the new item is a child of an existing item,
			//		calls onChildrenChange() with the new list of children
			//		for that existing item.
			//
			// tags:
			//		extension

			// We only care about the new item if it has a parent that corresponds to a TreeNode
			// we are currently displaying
			if(!parentInfo){
				return;
			}

			// Call onChildrenChange() on parent (ie, existing) item with new list of children
			// In the common case, the new list of children is simply parentInfo.newValue or
			// [ parentInfo.newValue ], although if items in the store has multiple
			// child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
			// so call getChildren() to be sure to get right answer.
			this.getChildren(parentInfo.item, dojo.hitch(this, function(children){
				this.onChildrenChange(parentInfo.item, children);
			}));
		},

		onDeleteItem: function(/*Object*/ item){
			// summary:
			//		Handler for delete notifications from underlying store
			this.onDelete(item);
		},

		onSetItem: function(/* item */ item,
						/* attribute-name-string */ attribute,
						/* object | array */ oldValue,
						/* object | array */ newValue){
			// summary:
			//		Updates the tree view according to changes in the data store.
			// description:
			//		Handles updates to an item's children by calling onChildrenChange(), and
			//		other updates to an item by calling onChange().
			//
			//		See `onNewItem` for more details on handling updates to an item's children.
			// tags:
			//		extension

			if(dojo.indexOf(this.childrenAttrs, attribute) != -1){
				// item's children list changed
				this.getChildren(item, dojo.hitch(this, function(children){
					// See comments in onNewItem() about calling getChildren()
					this.onChildrenChange(item, children);
				}));
			}else{
				// item's label/icon/etc. changed.
				this.onChange(item);
			}
		}
	});

<|endoftext|>


<commit_before>import * as Util from './util';
import { Errors } from './const';
import * as BaseCommands from './commands';
import * as BashParser from './parser';

export default class Bash {

    constructor(extensions = {}) {
        this.commands = Object.assign(extensions, BaseCommands);
        this.prevCommands = [];
        this.prevCommandsIndex = 0;
    }

    /*
     * This adds the given <input> into the terminal history
     *
     * @param {string} input - the user input
     * @param {Object} state - the current terminal state
     * @returns {Object} the new terminal state
     */
    pushInput(input, currentState) {
        this.prevCommands.push(input);
        this.prevCommandsIndex = this.prevCommands.length;

        // Append input to history
        return Object.assign({}, currentState, {
            history: currentState.history.concat({
                cwd: currentState.cwd,
                value: input,
            }),
        });
    }

    /*
     * This parses and executes the given <input> and returns an updated
     * state object.
     *
     * @param {string} input - the user input
     * @param {Object} state - the current terminal state
     * @returns {Object} a promise that resolves to the new terminal state
     */
    execute(input, currentState, progressObserver = () => {}) {
        const commandList = BashParser.parse(input);
        return this.runCommands(commandList, currentState, progressObserver);
    }

    /*
     * This function executes a list of command lists. The outer list
     * is a dependency list parsed from the `&&` operator. The inner lists
     * are groups of commands parsed from the `;` operator. If any given
     * command fails, the outer list will stop executing.
     *
     * @param {Array} commands - the commands to run
     * @param {Object} state - the terminal state
     * @returns {Object} a promise that resolves to the new terminal state
     */
    runCommands(commands, state, progressObserver = () => {}) {
        let errorOccurred = false;
        let newState = Object.assign({}, state);

        const emitNextState = (p, command) => {
            return p
                .catch((error) => {
                    errorOccurred = true;
                    const message = (error && error.message)
                        ? error.message
                        : `command ${command.name} failed`;
                    return Util.appendError(newState, '$1', message);
                })
                .then((nextState) => {
                    errorOccurred = errorOccurred || (nextState && nextState.error);
                    newState = nextState;
                    return nextState;
                });
        };

        const self = this;
        const stream = (function* () {
            for (const dependentCommands of commands) {
                for (const command of dependentCommands) {
                    if (errorOccurred) {
                        break;
                    }

                    if (command.name === '') {
                        yield Promise.resolve(newState);
                        continue;
                    }

                    if (!self.commands[command.name]) {
                        errorOccurred = true;
                        newState = Util.appendError(
                            newState, Errors.COMMAND_NOT_FOUND, command.name);
                        yield Promise.resolve(newState);
                        continue;
                    }

                    const result = self.commands[command.name].exec(newState, command);
                    if (result && (typeof result[Symbol.iterator] === 'function')) {
                        // If the result of the command is an iterable, yield
                        // its successive procuded states.
                        for (const partial of result) {
                            yield emitNextState(Promise.resolve(partial), command);
                        }
                    } else {
                        yield emitNextState(Promise.resolve(result), command);
                    }
                }
            }
        }());

        const consumeStream = () => {
            const { value, done } = stream.next();
            if (!done) {
                return value
                    .then((nextState) => progressObserver(nextState))
                    .then(() => consumeStream(stream));
            }
            return Promise.resolve(newState);
        };

        return consumeStream();
    }

    /*
     * This is a very naive autocomplete method that works for both
     * commands and directories. If the input contains only one token it
     * should only suggest commands.
     *
     * @param {string} input - the user input
     * @param {Object} state - the terminal state
     * @param {Object} state.structure - the file structure
     * @param {string} state.cwd - the current working directory
     * @returns {?string} a suggested autocomplete for the <input>
     */
    autocomplete(input, { structure, cwd }) {
        const tokens = input.split(/ +/);
        let token = tokens.pop();
        const filter = item => item.indexOf(token) === 0;
        const result = str => tokens.concat(str).join(' ');

        if (tokens.length === 0) {
            const suggestions = Object.keys(this.commands).filter(filter);
            return suggestions.length === 1 ? result(suggestions[0]) : null;
        } else {
            const pathList = token.split('/');
            token = pathList.pop();
            const partialPath = pathList.join('/');
            const path = Util.extractPath(partialPath, cwd);
            const { err, dir } = Util.getDirectoryByPath(structure, path);
            if (err) return null;
            const suggestions = Object.keys(dir).filter(filter);
            const prefix = partialPath ? `${partialPath}/` : '';
            return suggestions.length === 1 ? result(`${prefix}${suggestions[0]}`) : null;
        }
    }

    getPrevCommand() {
        return this.prevCommands[--this.prevCommandsIndex];
    }

    getNextCommand() {
        return this.prevCommands[++this.prevCommandsIndex];
    }

    hasPrevCommand() {
        return this.prevCommandsIndex !== 0;
    }

    hasNextCommand() {
        return this.prevCommandsIndex !== this.prevCommands.length - 1;
    }

}
<commit_msg>comments<commit_after>import * as Util from './util';
import { Errors } from './const';
import * as BaseCommands from './commands';
import * as BashParser from './parser';

export default class Bash {

    constructor(extensions = {}) {
        this.commands = Object.assign(extensions, BaseCommands);
        this.prevCommands = [];
        this.prevCommandsIndex = 0;
    }

    /*
     * This adds the given <input> into the terminal history
     *
     * @param {string} input - the user input
     * @param {Object} state - the current terminal state
     * @returns {Object} the new terminal state
     */
    pushInput(input, currentState) {
        this.prevCommands.push(input);
        this.prevCommandsIndex = this.prevCommands.length;

        // Append input to history
        return Object.assign({}, currentState, {
            history: currentState.history.concat({
                cwd: currentState.cwd,
                value: input,
            }),
        });
    }

    /*
     * This parses and executes the given <input> and returns an updated
     * state object.
     *
     * @param {string} input - the user input
     * @param {Object} state - the current terminal state
     * @param {function} state - a state progress observer callback
     * @returns {Object} a promise that resolves to the new terminal state
     */
    execute(input, currentState, progressObserver = () => {}) {
        const commandList = BashParser.parse(input);
        return this.runCommands(commandList, currentState, progressObserver);
    }

    /*
     * This function executes a list of command lists. The outer list
     * is a dependency list parsed from the `&&` operator. The inner lists
     * are groups of commands parsed from the `;` operator. If any given
     * command fails, the outer list will stop executing.
     *
     * @param {Array} commands - the commands to run
     * @param {Object} state - the terminal state
     * @param {function} state - a state progress observer callback
     * @returns {Object} a promise that resolves to the new terminal state
     */
    runCommands(commands, state, progressObserver = () => {}) {
        let errorOccurred = false;
        let newState = Object.assign({}, state);

        const emitNextState = (p, command) => {
            return p
                .catch((error) => {
                    errorOccurred = true;
                    const message = (error && error.message)
                        ? error.message
                        : `command ${command.name} failed`;
                    return Util.appendError(newState, '$1', message);
                })
                .then((nextState) => {
                    errorOccurred = errorOccurred || (nextState && nextState.error);
                    newState = nextState;
                    return nextState;
                });
        };

        const self = this;
        const stream = (function* () {
            for (const dependentCommands of commands) {
                for (const command of dependentCommands) {
                    if (errorOccurred) {
                        break;
                    }

                    if (command.name === '') {
                        yield Promise.resolve(newState);
                        continue;
                    }

                    if (!self.commands[command.name]) {
                        errorOccurred = true;
                        newState = Util.appendError(
                            newState, Errors.COMMAND_NOT_FOUND, command.name);
                        yield Promise.resolve(newState);
                        continue;
                    }

                    const result = self.commands[command.name].exec(newState, command);
                    if (result && (typeof result[Symbol.iterator] === 'function')) {
                        // If the result of the command is an iterable, yield
                        // its successive procuded states.
                        for (const partial of result) {
                            yield emitNextState(Promise.resolve(partial), command);
                        }
                    } else {
                        yield emitNextState(Promise.resolve(result), command);
                    }
                }
            }
        }());

        const consumeStream = () => {
            const { value, done } = stream.next();
            if (!done) {
                return value
                    .then((nextState) => progressObserver(nextState))
                    .then(() => consumeStream(stream));
            }
            return Promise.resolve(newState);
        };

        return consumeStream();
    }

    /*
     * This is a very naive autocomplete method that works for both
     * commands and directories. If the input contains only one token it
     * should only suggest commands.
     *
     * @param {string} input - the user input
     * @param {Object} state - the terminal state
     * @param {Object} state.structure - the file structure
     * @param {string} state.cwd - the current working directory
     * @returns {?string} a suggested autocomplete for the <input>
     */
    autocomplete(input, { structure, cwd }) {
        const tokens = input.split(/ +/);
        let token = tokens.pop();
        const filter = item => item.indexOf(token) === 0;
        const result = str => tokens.concat(str).join(' ');

        if (tokens.length === 0) {
            const suggestions = Object.keys(this.commands).filter(filter);
            return suggestions.length === 1 ? result(suggestions[0]) : null;
        } else {
            const pathList = token.split('/');
            token = pathList.pop();
            const partialPath = pathList.join('/');
            const path = Util.extractPath(partialPath, cwd);
            const { err, dir } = Util.getDirectoryByPath(structure, path);
            if (err) return null;
            const suggestions = Object.keys(dir).filter(filter);
            const prefix = partialPath ? `${partialPath}/` : '';
            return suggestions.length === 1 ? result(`${prefix}${suggestions[0]}`) : null;
        }
    }

    getPrevCommand() {
        return this.prevCommands[--this.prevCommandsIndex];
    }

    getNextCommand() {
        return this.prevCommands[++this.prevCommandsIndex];
    }

    hasPrevCommand() {
        return this.prevCommandsIndex !== 0;
    }

    hasNextCommand() {
        return this.prevCommandsIndex !== this.prevCommands.length - 1;
    }

}
<|endoftext|>


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

var _ = require('lodash');
var util = require('util');
var QS = require('querystring');
var Transform = require('stream').Transform;

/**
 * Originate a new client request instance and lob it at this Sails
 * app at the specified route `address`.
 *
 * Particularly useful for running unit/integration tests without
 * actually having to bind the HTTP and/or WebSocket servers to
 * a TCP port.
 *
 * @param  {String} address
 * @param  {Object} body
 * @param  {Function} cb
 * @return {Stream.Readable}
 *
 * @api public
 */

module.exports = function request( /* address, body, cb */ ) {

  var sails = this;

  //
  // Body params may be passed in to DELETE, HEAD, and GET requests,
  // even though these types of requests don't normally contain a body.
  // (this method just serializes them as if they were sent in the querystring)
  //

  // Normalize usage
  var address = arguments[0];
  var body;
  var cb;

  var method;
  var headers;
  var url;

  // Usage:
  // sails.request(opts, cb)
  // • opts.url
  // • opts.method
  // • opts.params
  // • opts.headers
  //
  // (`opts.url` is required)
  if (_.isObject(arguments[0]) && arguments[0].url) {
    url = arguments[0].url;
    method = arguments[0].method;
    headers = arguments[0].headers || {};
    body = arguments[0].params || arguments[0].data || {};
  }
  // console.log('called sails.request() ');
  // console.log('headers: ',headers);
  // console.log('method: ',method);

  // Usage:
  // sails.request(address, [params], cb)
  if (arguments[2]) {
    cb = arguments[2];
    body = arguments[1];
  }
  if (_.isFunction(arguments[1])) {
    cb = arguments[1];
  } else if (arguments[1]) {
    body = arguments[1];
  }

  // If route has an HTTP verb (e.g. `get /foo/bar`, `put /bar/foo`, etc.) parse it out,
  // (unless method or url was explicitly defined)
  method = method || sails.util.detectVerb(address).verb;
  method = method ? method.toUpperCase() : 'GET';
  url = url || sails.util.detectVerb(address).original;

  // Parse query string (`req.query`)
  var queryStringPos = url.indexOf('?');

  // If this is a GET, HEAD, or DELETE request, treat the "body"
  // as parameters which should be serialized into the querystring.
  if (_.isObject(body) && _.contains(['GET', 'HEAD', 'DELETE'], method)) {

    var stringifiedParams = QS.stringify(body);

    if (queryStringPos === -1) {
      url += '?' + stringifiedParams;
    } else {
      url = url.substring(0, queryStringPos) + '?' + stringifiedParams;
    }
  }

  // Build HTTP Client Response stream
  var clientRes = new MockClientResponse();
  clientRes.on('finish', function() {

    // console.log('clientRes finished. Headers:',clientRes.headers);

    // Only dump the buffer if a callback was supplied
    if (cb) {

      // Attempt to read the response buffer into a string
      try {
        clientRes.body = clientRes.read();
        if (clientRes.body !== null) {
          clientRes.body = clientRes.body.toString();
          clientRes.body = JSON.parse(clientRes.body);
        }
      } catch (e) {}

      // Don't include body if it is empty
      if (!clientRes.body) {delete clientRes.body;}

      // If status code is indicative of an error, send the
      // response body or status code as the first error argument.
      if (clientRes.statusCode < 200 || clientRes.statusCode >= 400) {
        var error = new Error(util.inspect(clientRes.body || clientRes.status));
        if (clientRes.body) {error.body = clientRes.body;}
        error.status = clientRes.statusCode;
        return cb(error);
      }
      else {
        return cb(null, clientRes, clientRes.body);
      }
    }
  });
  clientRes.on('error', function(err) {
    err = err || new Error('Error on response stream');
    if (cb) return cb(err);
    else return clientRes.emit('error', err);
  });

  // To kick things off, pass `opts` (as req) and `res` to the Sails router
  sails.router.route({
    method: method,
    url: url,
    body: body,
    headers: headers || {}
  }, {
    _clientRes: clientRes
  });

  // Return clientRes stream
  return clientRes;

};

function MockClientResponse() {
  Transform.call(this);
}
util.inherits(MockClientResponse, Transform);
MockClientResponse.prototype._transform = function(chunk, encoding, next) {
  this.push(chunk);
  next();
};

<commit_msg>Fix property name for ('status' => 'statusCode') in virtual request helper.<commit_after>/**
 * Module dependencies.
 */

var _ = require('lodash');
var util = require('util');
var QS = require('querystring');
var Transform = require('stream').Transform;

/**
 * Originate a new client request instance and lob it at this Sails
 * app at the specified route `address`.
 *
 * Particularly useful for running unit/integration tests without
 * actually having to bind the HTTP and/or WebSocket servers to
 * a TCP port.
 *
 * @param  {String} address
 * @param  {Object} body
 * @param  {Function} cb
 * @return {Stream.Readable}
 *
 * @api public
 */

module.exports = function request( /* address, body, cb */ ) {

  var sails = this;

  //
  // Body params may be passed in to DELETE, HEAD, and GET requests,
  // even though these types of requests don't normally contain a body.
  // (this method just serializes them as if they were sent in the querystring)
  //

  // Normalize usage
  var address = arguments[0];
  var body;
  var cb;

  var method;
  var headers;
  var url;

  // Usage:
  // sails.request(opts, cb)
  // • opts.url
  // • opts.method
  // • opts.params
  // • opts.headers
  //
  // (`opts.url` is required)
  if (_.isObject(arguments[0]) && arguments[0].url) {
    url = arguments[0].url;
    method = arguments[0].method;
    headers = arguments[0].headers || {};
    body = arguments[0].params || arguments[0].data || {};
  }
  // console.log('called sails.request() ');
  // console.log('headers: ',headers);
  // console.log('method: ',method);

  // Usage:
  // sails.request(address, [params], cb)
  if (arguments[2]) {
    cb = arguments[2];
    body = arguments[1];
  }
  if (_.isFunction(arguments[1])) {
    cb = arguments[1];
  } else if (arguments[1]) {
    body = arguments[1];
  }

  // If route has an HTTP verb (e.g. `get /foo/bar`, `put /bar/foo`, etc.) parse it out,
  // (unless method or url was explicitly defined)
  method = method || sails.util.detectVerb(address).verb;
  method = method ? method.toUpperCase() : 'GET';
  url = url || sails.util.detectVerb(address).original;

  // Parse query string (`req.query`)
  var queryStringPos = url.indexOf('?');

  // If this is a GET, HEAD, or DELETE request, treat the "body"
  // as parameters which should be serialized into the querystring.
  if (_.isObject(body) && _.contains(['GET', 'HEAD', 'DELETE'], method)) {

    var stringifiedParams = QS.stringify(body);

    if (queryStringPos === -1) {
      url += '?' + stringifiedParams;
    } else {
      url = url.substring(0, queryStringPos) + '?' + stringifiedParams;
    }
  }

  // Build HTTP Client Response stream
  var clientRes = new MockClientResponse();
  clientRes.on('finish', function() {

    // console.log('clientRes finished. Headers:',clientRes.headers);

    // Only dump the buffer if a callback was supplied
    if (cb) {

      // Attempt to read the response buffer into a string
      try {
        clientRes.body = clientRes.read();
        if (clientRes.body !== null) {
          clientRes.body = clientRes.body.toString();
          clientRes.body = JSON.parse(clientRes.body);
        }
      } catch (e) {}

      // Don't include body if it is empty
      if (!clientRes.body) {delete clientRes.body;}

      // If status code is indicative of an error, send the
      // response body or status code as the first error argument.
      if (clientRes.statusCode < 200 || clientRes.statusCode >= 400) {
        var error = new Error(util.inspect(clientRes.body || clientRes.statusCode));
        if (clientRes.body) {error.body = clientRes.body;}
        error.status = clientRes.statusCode;
        return cb(error);
      }
      else {
        return cb(null, clientRes, clientRes.body);
      }
    }
  });
  clientRes.on('error', function(err) {
    err = err || new Error('Error on response stream');
    if (cb) return cb(err);
    else return clientRes.emit('error', err);
  });

  // To kick things off, pass `opts` (as req) and `res` to the Sails router
  sails.router.route({
    method: method,
    url: url,
    body: body,
    headers: headers || {}
  }, {
    _clientRes: clientRes
  });

  // Return clientRes stream
  return clientRes;

};

function MockClientResponse() {
  Transform.call(this);
}
util.inherits(MockClientResponse, Transform);
MockClientResponse.prototype._transform = function(chunk, encoding, next) {
  this.push(chunk);
  next();
};

<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/* global google */

if(document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initialize);
} else {
  initialize();
}

let map;
let placesService;
let RADIUS = 1000; // Measured in meters
let interests = ["park"];
let regions = [];

/**
 * Initializes the webpage with a map and a random location.
 * Sets up Directions services and loads recommendations
 * based on user interests.
 */
function initialize() {
  // Modified version of Justine's implementation
  var directionsService = new google.maps.DirectionsService();
  var directionsRenderer = new google.maps.DirectionsRenderer();
  var start = new google.maps.LatLng(37.7699298, -122.4469157);
  var end = new google.maps.LatLng(37.7683909618184, -122.51089453697205);
  var mapOptions = {
    zoom: 14,
    center: start
  }
  map = new google.maps.Map(document.getElementById('map'), mapOptions);
  directionsRenderer.setMap(map);
  document.getElementById("route").addEventListener("click", () => {
    calcRoute(directionsService, directionsRenderer, start, end);
  });
  placesService = new google.maps.places.PlacesService(map);
}

/**
 * Creates a route between two points and loads onto map.
 * Shows markers at the starting point of a step. Loads
 * recommendations centered around these points.
 * @param {DirectionsService directionsService} handles finding
 * the directions between points
 * @param {DirectionsRenderer directionsRenderer} renders the route
 * @param {LatLng start} starting point location
 * @param {LatLng end} ending point location
 */
function calcRoute(directionsService, directionsRenderer, start, end) {
  var request = {
      origin:  start,
      destination: end,
      travelMode: 'DRIVING'
  };
  directionsService.route(request, function(response, status) {
    if (status == 'OK') {
      directionsRenderer.setDirections(response);
      //computeTotalDistance(response);
      showSteps(response);
      loadRecommendations();
    } else {
      window.alert("Could not calculate route due to: " + status);
    }
  });
}

/**
 * Creates a marker at the starting point of a step
 * along the route.
 * @param {DirectionsResult directionResult} contains data
 * on directions from one step to another
 */
function showSteps(directionResult) {
  const myRoute = directionResult.routes[0].legs[0];
  for(let i = 0; i < myRoute.steps.length; i++) {
    const marker = new google.maps.Marker();
    marker.setMap(map);
    marker.setPosition(myRoute.steps[i].start_location);
    regions.push(myRoute.steps[i].start_location);
  }
}

/**
 * Finds the travel distance along the route.
 * @param {DirectionsResult response} path of directions
 */
function computeTotalDistance(response) {
  let totalDist = 0;
  let route = response.routes[0];
  for (i = 0; i < route.legs.length; i++) {
    totalDist += route.legs[i].distance.value; // Measured in meters
  }
  totalDist = totalDist / 1000;
  console.log("total distance is: " + totalDist + " km");
}

/**
 * Go through a user's interests and find places
 * fitting the interests with the textSearch method.
 */
function loadRecommendations() {
  interests.forEach(placeType => {
      console.log(placeType);
      regions.forEach((corner) => {
        let request = {
          location: corner,
          radius: RADIUS,
          query: placeType
        };
        placesService.textSearch(request, addRecommendations);
      });
  });
}

/**
 * Checks the response from PlacesService and creates markers
 * on the locations found from the request. Temporarily limit
 * the amount of markers added.
 * TODO: Load all results, but have a list that limits the results
 * shown rather than limiting the results being loaded.
 * @param {PlaceResults[] results} places found with PlaceResult type.
 * @param {PlacesServiceStatus status} status of PlacesService request.
 */
function addRecommendations(results, status) {
  if (status == google.maps.places.PlacesServiceStatus.OK) {
    let maxRecommendations = 1;
    let numRecommendations = 0;
    for (let i = 0; i < results.length; i++) {
      console.log(results[i].formatted_address);
      placeMarker(results[i]);
      numRecommendations++;
      if(numRecommendations == maxRecommendations) {
        break;
      }
    }
  } else {
    alert("Status: " + status +
          "\nOur services are currently down. Oops!");
  }
}

/**
 * Places a marker onto the map at the specified location.
 * @param {PlaceResult place} Object containing information
 * about a place
 */
function placeMarker(place) {
  new google.maps.Marker({
    position: place.geometry.location,
    map: map,
    title: place.name
  });
}
<commit_msg>Playing with partitions<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/* global google */

if(document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initialize);
} else {
  initialize();
}

let map;
let placesService;
let RADIUS = 1000; // Measured in meters
let interests = ["park"];
let regions = [];

/**
 * Initializes the webpage with a map and a random location.
 * Sets up Directions services and loads recommendations
 * based on user interests.
 */
function initialize() {
  // Modified version of Justine's implementation
  var directionsService = new google.maps.DirectionsService();
  var directionsRenderer = new google.maps.DirectionsRenderer();
  var start = new google.maps.LatLng(33.451645, -112.063580);
  var end = new google.maps.LatLng(34.165704, -118.288232);
  var mapOptions = {
    zoom: 14,
    center: start
  }
  map = new google.maps.Map(document.getElementById('map'), mapOptions);
  directionsRenderer.setMap(map);
  document.getElementById("route").addEventListener("click", () => {
    calcRoute(directionsService, directionsRenderer, start, end);
  });
  placesService = new google.maps.places.PlacesService(map);
}

/**
 * Creates a route between two points and loads onto map.
 * Shows markers at the starting point of a step. Loads
 * recommendations centered around these points.
 * @param {DirectionsService directionsService} handles finding
 * the directions between points
 * @param {DirectionsRenderer directionsRenderer} renders the route
 * @param {LatLng start} starting point location
 * @param {LatLng end} ending point location
 */
function calcRoute(directionsService, directionsRenderer, start, end) {
  var request = {
      origin:  start,
      destination: end,
      travelMode: 'DRIVING'
  };
  directionsService.route(request, function(response, status) {
    if (status == 'OK') {
      directionsRenderer.setDirections(response);
      //computeTotalDistance(response);
      showSteps(response);
      loadRecommendations();
    } else {
      window.alert("Could not calculate route due to: " + status);
    }
  });
}

/**
 * Creates a marker at the starting point of a step
 * along the route.
 * @param {DirectionsResult directionResult} contains data
 * on directions from one step to another
 */
function showSteps(directionResult) {
  const myRoute = directionResult.routes[0].legs[0];
  console.log("showSteps: \n");
  for(let i = 0; i < myRoute.steps.length; i++) {
    const marker = new google.maps.Marker();
    marker.setMap(map);
    marker.setPosition(myRoute.steps[i].start_location);
    console.log("\t\t" + myRoute.steps[i].start_location);
    regions.push(myRoute.steps[i].start_location);
  }
  console.log("size: " + regions.length + "\n\n\n");
}

/**
 * Finds the travel distance along the route.
 * @param {DirectionsResult response} path of directions
 */
function computeTotalDistance(response) {
  let totalDist = 0;
  let route = response.routes[0];
  for (i = 0; i < route.legs.length; i++) {
    totalDist += route.legs[i].distance.value; // Measured in meters
  }
  totalDist = totalDist / 1000;
  console.log("total distance is: " + totalDist + " km");
}

/**
 * Go through a user's interests and find places
 * fitting the interests with the textSearch method.
 */
function loadRecommendations() {
  interests.forEach(placeType => {
      console.log(placeType);
      regions.forEach((corner) => {
        let request = {
          location: corner,
          radius: RADIUS,
          query: placeType
        };
        placesService.textSearch(request, addRecommendations);
      });
  });
}

/**
 * Checks the response from PlacesService and creates markers
 * on the locations found from the request. Temporarily limit
 * the amount of markers added.
 * TODO: Load all results, but have a list that limits the results
 * shown rather than limiting the results being loaded.
 * @param {PlaceResults[] results} places found with PlaceResult type.
 * @param {PlacesServiceStatus status} status of PlacesService request.
 */
function addRecommendations(results, status) {
  if (status == google.maps.places.PlacesServiceStatus.OK) {
    let maxRecommendations = 1;
    let numRecommendations = 0;
    for (let i = 0; i < results.length; i++) {
      console.log(results[i].formatted_address);
      //placeMarker(results[i]);
      numRecommendations++;
      if(numRecommendations == maxRecommendations) {
        break;
      }
    }
  } else {
    alert("Status: " + status +
          "\nOur services are currently down. Oops!");
  }
}

/**
 * Places a marker onto the map at the specified location.
 * @param {PlaceResult place} Object containing information
 * about a place
 */
function placeMarker(place) {
  new google.maps.Marker({
    position: place.geometry.location,
    map: map,
    title: place.name
  });
}
<|endoftext|>


<commit_before>const fs = require('fs');
const deepmerge = require('deepmerge');
const File = require('./file');
const overwriteFieldValue = require('node-object-field-resolver');

module.exports = class Processor {
    constructor(config, cwd) {
        this.config = config;
        this.cwd = cwd;
    }

    process() {
        this.files = [];

        this.config.forEach(config => {
            let file = this.processFile(config);

            this.files.push(file);
        })
    }

    write() {
        this.files.forEach(file => fs.writeFile(file.getOutputPath(), JSON.stringify(file.getContent(), null, 2), 'UTF-8'));
    }

    /**
     * @param {{envMap: {}, output: string, source: string}} config
     *
     * @returns {*}
     */
    processFile(config) {
        const file = new File();

        const pathSource = this.resolvePath(config.source);
        const pathOutput = this.resolvePath(config.output);

        const packageJsonPath = this.resolvePath(pathSource);
        const packageJsonContent = fs.readFileSync(packageJsonPath);

        /** @param {{extra: {}}} content */
        const packageJson = JSON.parse(packageJsonContent);
        const solvedJson = this.resolveOverwritten(config.envMap);
        const completedJson = this.constructor.getMergedData(packageJson, solvedJson);

        file.setSourcePath(pathSource)
            .setOutputPath(pathOutput)
            .setContent(completedJson);

        return file;
    }

    /**
     * @param {string} path
     */
    resolvePath(path) {
        if ('/' === path.charAt(0)) {
            return path;
        }

        return `${this.cwd}/${path}`;
    }

    resolveOverwritten(envMapping) {
        const object = {};

        for (const abstractPath of Object.keys(envMapping)) {
            const envVariable = envMapping[abstractPath];
            const value = this.constructor.getEnvironmentValue(envVariable);

            overwriteFieldValue(abstractPath, value, object);
        }

        return object;
    }

    static getEnvironmentValue(index) {
        return process.env[index] || undefined;
    }

    static getMergedData(data, overwrittenData) {
        return deepmerge(data, overwrittenData);
    }
};
<commit_msg>improve JSDoc<commit_after>const fs = require('fs');
const deepmerge = require('deepmerge');
const File = require('./file');
const overwriteFieldValue = require('node-object-field-resolver');

module.exports = class Processor {
    constructor(config, cwd) {
        this.config = config;
        this.cwd = cwd;
    }

    process() {
        this.files = [];

        this.config.forEach(config => {
            let file = this.processFile(config);

            this.files.push(file);
        })
    }

    write() {
        this.files.forEach(file => fs.writeFile(file.getOutputPath(), JSON.stringify(file.getContent(), null, 2), 'UTF-8'));
    }

    /**
     * @param {{envMap: {}, source: string, output: string}} config
     *
     * @returns {File}
     */
    processFile(config) {
        const file = new File();

        const pathSource = this.resolvePath(config.source);
        const pathOutput = this.resolvePath(config.output);

        const packageJsonPath = this.resolvePath(pathSource);
        const packageJsonContent = fs.readFileSync(packageJsonPath);

        /** @param {{extra: {}}} content */
        const packageJson = JSON.parse(packageJsonContent);
        const solvedJson = this.resolveOverwritten(config.envMap);
        const completedJson = this.constructor.getMergedData(packageJson, solvedJson);

        file.setSourcePath(pathSource)
            .setOutputPath(pathOutput)
            .setContent(completedJson);

        return file;
    }

    /**
     * @param {string} path
     */
    resolvePath(path) {
        if ('/' === path.charAt(0)) {
            return path;
        }

        return `${this.cwd}/${path}`;
    }

    resolveOverwritten(envMapping) {
        const object = {};

        for (const abstractPath of Object.keys(envMapping)) {
            const envVariable = envMapping[abstractPath];
            const value = this.constructor.getEnvironmentValue(envVariable);

            overwriteFieldValue(abstractPath, value, object);
        }

        return object;
    }

    static getEnvironmentValue(index) {
        return process.env[index] || undefined;
    }

    static getMergedData(data, overwrittenData) {
        return deepmerge(data, overwrittenData);
    }
};
<|endoftext|>


<commit_before>var assert = require("assert");
var _ = require("underscore");
var utils = require("./utils");
var Environment = require("./environment");

/**
 * Expose `Application`.
 */
module.exports = Application;

/**
 * `Application` constructor. Takes an `ElasticBeanstalk` wrapper instance.
 */

function Application (eb, appName) {
  this._eb = eb;
  this.appName = appName;

  assert(this.appName);
}

/**
 * Queries the associated object on AWS and returns the ApplicationDescription
 * object for this application.
 * http://docs.aws.amazon.com/elasticbeanstalk/latest/api/API_ApplicationDescription.html
 *
 * @return {Promise->Object}
 */

Application.prototype.info = function info () {
  return this._eb.describeApplications({ ApplicationNames: [this.appName] }).then(function (data) {
    return data.Applications.length ? data.Applications[0]: null;
  });
}

/**
 * Checks to see if Application exists on AWS; creates it if it does not exist, and
 * updates the information accordingly.
 *
 * @param {Object} definition
 * - {String} Description
 * @return {Promise->Application}
 */

Application.prototype.initialize = function initialize (definition) {
  var application = this;
  var params = _.extend({}, definition, { ApplicationName: this.appName });

  return this.info().then(function (appDesc) {
    // If application doesn't exist, create it
    if (!appDesc) {
      return application._eb.createApplication(params);
    }
    // If application description does not match, update it
    else if (appDesc.Description !== params.Description) {
      return application.update(params);
    }
  }).then(function (description) {
    // Application should be good to go and sync'd at this point.
    return application;
  });
};

/**
 * Creates a new Environment object within this application.
 *
 * @param {String} envName
 * @return {Environment}
 */

Application.prototype.createEnvironment = function createEnvironment (envName) {
  var env = new Environment(this._eb, this.appName, envName);
  return env;
};

/**
 * Creates an ApplicationVersion of an app from S3 into this ElasticBeanstalk
 * Application.
 *
 * @param {Object} params
 * - {String} VersionLabel
 * - {String} Description
 * - {Object} SourceBundle
 *   - {String} S3Bucket
 *   - {String} S3Key
 *
 * @return {Promise->ApplicationVersionDescription}
 */

Application.prototype.createVersion = function createVersion (params) {
  var params = _.extend({}, params, {
    ApplicationName: this.appName,
    AutoCreateApplication: false // Application should already be created
  });

  return this._eb.createApplicationVersion(params);
};

/**
 * Updates Application description.
 * @param {Object} params
 * - {String} Description
 *
 * @return {Promise->ApplicationDescription}
 */

Application.prototype.update = function update (params) {
  params = _.extend({}, params, { ApplicationName: this.appName });
  return this._eb.updateApplication(params);
};

/**
 * Returns all environments associated with this application.
 *
 * @return {Promise->[Environment]}
 */

Application.prototype.getEnvironments = function getEnvironments () {
  var app = this;
  return this._eb.describeEnvironments({ ApplicationName: this.appName }).then(function (desc) {
    return when.all(desc.Environments.map(function (env) {
      return new Environment(app._eb, app.appName, env.EnvironmentName);
    }));
  });
};

/**
 * Returns the environment associated with this application by
 * `envName`.
 *
 * @param {String} envName
 * @return {Promise->Environment|null}
 */

Application.prototype.getEnvironment = function getEnvironment (envName) {
  return this.getEnvironments().then(function (envs) {
    for (var i = 0; i < envs.length; i++) {
      if (envs[i].envName === envName) {
        return envs[i];
      }
    }
    return null;
  });
};

/**
 * Deploys `version` on `envName` environment. Environment creates and updates
 * the AWS resources as necessary.
 *
 * @param {String} envName
 * @param {String} version
 *
 * @return {Promise->Environment}
 */

Application.prototype.deploy = function deploy (envName, version) {
  var env = this.getEnvironment(envName);
  if (!env) {
    return when.resolve(null);
  }

  return env.deploy(version);
};

/**
 * Removes this application from AWS.
 *
 * @return {Promise}
 */

Application.prototype.remove = function remove () {
  return this._eb.deleteApplication({ ApplicationName: this.appName, TerminateEnvByForce: true }).then(function () {
    return null;
  });
};
<commit_msg>Automatically initialize environment when creating through application<commit_after>var assert = require("assert");
var _ = require("underscore");
var when = require("when");
var utils = require("./utils");
var Environment = require("./environment");
/**
 * Expose `Application`.
 */
module.exports = Application;

/**
 * `Application` constructor. Takes an `ElasticBeanstalk` wrapper instance.
 */

function Application (eb, appName) {
  this._eb = eb;
  this.appName = appName;

  assert(this.appName);
}

/**
 * Queries the associated object on AWS and returns the ApplicationDescription
 * object for this application.
 * http://docs.aws.amazon.com/elasticbeanstalk/latest/api/API_ApplicationDescription.html
 *
 * @return {Promise->Object}
 */

Application.prototype.info = function info () {
  return this._eb.describeApplications({ ApplicationNames: [this.appName] }).then(function (data) {
    return data.Applications.length ? data.Applications[0]: null;
  });
}

/**
 * Checks to see if Application exists on AWS; creates it if it does not exist, and
 * updates the information accordingly.
 *
 * @param {Object} definition
 * - {String} Description
 * @return {Promise->Application}
 */

Application.prototype.initialize = function initialize (definition) {
  var application = this;
  var params = _.extend({}, definition, { ApplicationName: this.appName });

  return this.info().then(function (appDesc) {
    // If application doesn't exist, create it
    if (!appDesc) {
      return application._eb.createApplication(params);
    }
    // If application description does not match, update it
    else if (appDesc.Description !== params.Description) {
      return application.update(params);
    }
  }).then(function (description) {
    // Application should be good to go and sync'd at this point.
    return application;
  });
};

/**
 * Creates a new Environment object within this application and starts it on AWS.
 *
 * @param {String} envName
 * @param {Object} params
 * @return {Promise->Environment}
 */

Application.prototype.createEnvironment = function createEnvironment (envName, params) {
  var env = new Environment(this._eb, this.appName, envName);
  return env.initialize(params);
};

/**
 * Creates an ApplicationVersion of an app from S3 into this ElasticBeanstalk
 * Application.
 *
 * @param {Object} params
 * - {String} VersionLabel
 * - {String} Description
 * - {Object} SourceBundle
 *   - {String} S3Bucket
 *   - {String} S3Key
 *
 * @return {Promise->ApplicationVersionDescription}
 */

Application.prototype.createVersion = function createVersion (params) {
  var params = _.extend({}, params, {
    ApplicationName: this.appName,
    AutoCreateApplication: false // Application should already be created
  });

  return this._eb.createApplicationVersion(params);
};

/**
 * Updates Application description.
 * @param {Object} params
 * - {String} Description
 *
 * @return {Promise->ApplicationDescription}
 */

Application.prototype.update = function update (params) {
  params = _.extend({}, params, { ApplicationName: this.appName });
  return this._eb.updateApplication(params);
};

/**
 * Returns all environments associated with this application.
 *
 * @return {Promise->[Environment]}
 */

Application.prototype.getEnvironments = function getEnvironments () {
  var app = this;
  return this._eb.describeEnvironments({ ApplicationName: this.appName }).then(function (desc) {
    return desc.Environments.map(function (env) {
      return new Environment(app._eb, app.appName, env.EnvironmentName);
    });
  });
};

/**
 * Returns the environment associated with this application by
 * `envName`.
 *
 * @param {String} envName
 * @return {Promise->Environment|null}
 */

Application.prototype.getEnvironment = function getEnvironment (envName) {
  return this.getEnvironments().then(function (envs) {
    for (var i = 0; i < envs.length; i++) {
      if (envs[i].envName === envName) {
        return envs[i];
      }
    }
    return null;
  });
};

/**
 * Deploys `version` on `envName` environment. Environment creates and updates
 * the AWS resources as necessary.
 *
 * @param {String} envName
 * @param {String} version
 *
 * @return {Promise->Environment}
 */

Application.prototype.deploy = function deploy (envName, version) {
  var env = this.getEnvironment(envName);
  if (!env) {
    return when.resolve(null);
  }

  return env.deploy(version);
};

/**
 * Removes this application from AWS.
 *
 * @return {Promise}
 */

Application.prototype.remove = function remove () {
  return this._eb.deleteApplication({ ApplicationName: this.appName, TerminateEnvByForce: true }).then(function () {
    return null;
  });
};
<|endoftext|>


<commit_before>(function($) {
$.fn.prognroll = function( options ) {

var settings = $.extend({
height:5,        //Progress bar height
color:"#50bcb6", //Progress bar background color
position:"top",  //Progress bar position can be "top" or "bottom"
custom:false     //If you make it true, you can add your custom div and see it's scroll progress on the page.
}, options );

return  this.each( function() {
if ( $( this ).data( 'prognroll' ) ){
return false;
}
$( this ).data( 'prognroll', true );

var $span = $("<span>" , {class: "bar"});
$("body").prepend($span);

$span.css({
position:"fixed",
bottom:null,
left:0,
width:0,
height:settings.height,
backgroundColor:settings.color,
zIndex :9999999
});

if(settings.position === "top") {
$span.css("top",0);
}else {
$span.css("bottom",0);
}

if(settings.custom===false) {

$(window).scroll(function(e) {
e.preventDefault();
var windowScrollTop=$(window).scrollTop();
var windowHeight=$(window).outerHeight();
var bodyHeight=$("body").outerHeight();

var total=(windowScrollTop /(bodyHeight-windowHeight)) * 100;

$(".bar").css("width",total + "%");
});

}else {

$(this).scroll(function(e) {
e.preventDefault();
var customScrollTop = $(this).scrollTop();
var customHeight = $(this).outerHeight();
var customScrollHeight = $(this).prop("scrollHeight");

var total = (customScrollTop / (customScrollHeight - customHeight)) * 100;

$(".bar").css("width", total + "%");
});

}

/* Get scroll position on on page load */
$(window).on('hashchange',function(e) {
e.preventDefault();
console.log($(window).scrollTop());
});
$(window).trigger('hashchange');

var windowScrollTop=$(window).scrollTop();
var windowHeight=$(window).outerHeight();
var bodyHeight=$("body").outerHeight();

var total=(windowScrollTop /(bodyHeight-windowHeight)) * 100;

$(".bar").css("width",total + "%");
/* Get scroll position on on page load */

});
};
})(jQuery);

<commit_msg>Add info<commit_after>/* PrognRoll | https://mburakerman.github.io/prognroll/ | @mburakerman | License: MIT */
(function($) {
$.fn.prognroll = function( options ) {

var settings = $.extend({
height:5,        //Progress bar height
color:"#50bcb6", //Progress bar background color
position:"top",  //Progress bar position can be "top" or "bottom"
custom:false     //If you make it true, you can add your custom div and see it's scroll progress on the page.
}, options );

return  this.each( function() {
if ( $( this ).data( 'prognroll' ) ){
return false;
}
$( this ).data( 'prognroll', true );

var $span = $("<span>" , {class: "bar"});
$("body").prepend($span);

$span.css({
position:"fixed",
bottom:null,
left:0,
width:0,
height:settings.height,
backgroundColor:settings.color,
zIndex :9999999
});

if(settings.position === "top") {
$span.css("top",0);
}else {
$span.css("bottom",0);
}

if(settings.custom===false) {

$(window).scroll(function(e) {
e.preventDefault();
var windowScrollTop=$(window).scrollTop();
var windowHeight=$(window).outerHeight();
var bodyHeight=$("body").outerHeight();

var total=(windowScrollTop /(bodyHeight-windowHeight)) * 100;

$(".bar").css("width",total + "%");
});

}else {

$(this).scroll(function(e) {
e.preventDefault();
var customScrollTop = $(this).scrollTop();
var customHeight = $(this).outerHeight();
var customScrollHeight = $(this).prop("scrollHeight");

var total = (customScrollTop / (customScrollHeight - customHeight)) * 100;

$(".bar").css("width", total + "%");
});

}

/* Get scroll position on on page load */
$(window).on('hashchange',function(e) {
e.preventDefault();
console.log($(window).scrollTop());
});
$(window).trigger('hashchange');

var windowScrollTop=$(window).scrollTop();
var windowHeight=$(window).outerHeight();
var bodyHeight=$("body").outerHeight();

var total=(windowScrollTop /(bodyHeight-windowHeight)) * 100;

$(".bar").css("width",total + "%");
/* Get scroll position on on page load */

});
};
})(jQuery);

<|endoftext|>


<commit_before>const path = require('path');
const cluster = require('cluster');
const helper = require('think-helper');
const thinkLoader = require('./loader.js');
const thinkCluster = require('think-cluster');
const pm2 = require('think-pm2');
const assert = require('assert');
const mockHttp = require('think-mock-http');

/**
 * applition class
 */
module.exports = class Application {
  /**
   * constructor
   */
  constructor(options = {}){
    assert(options.ROOT_PATH, 'options.ROOT_PATH must be set');
    if(!options.APP_PATH){
      let appPath = path.join(options.ROOT_PATH, 'app');
      if(!options.transpiler && !helper.isDirectory(appPath)){
        appPath = path.join(options.ROOT_PATH, 'src');
      }
      options.APP_PATH = appPath;
    }
    this.options = options;
  }
  /**
   * notify error
   */
  notifier(err){
    if(!this.options.notifier) return;
    let notifier = this.options.notifier;
    if(!helper.isArray(notifier)){
      notifier = [notifier];
    }
    notifier[0](Object.assign({
      title: 'ThinkJS Transpile Error',
      message: err.message
    }, notifier[1]));
  }
  /**
   * watcher callback
   */
  _watcherCallBack(fileInfo){
    let transpiler = this.options.transpiler;
    if(transpiler){
      if(!helper.isArray(transpiler)){
        transpiler = [transpiler];
      }
      let ret = transpiler[0]({
        srcPath: fileInfo.path,
        outPath: think.APP_PATH,
        file: fileInfo.file,
        options: transpiler[1]
      });
      if(helper.isError(ret)){
        console.error(ret.stack);
        this.notifier(ret);
        return false;
      }
      think.logger.info(`transpile file ${fileInfo.file} success`);
    }
    //reload all workers
    if(this.masterInstance){
      this.masterInstance.forceReloadWorkers();
    }
  }
  /**
   * start watcher
   */
  startWatcher(){
    if(!this.options.watcher) return;
    const instance = new this.options.watcher({
      srcPath: path.join(think.ROOT_PATH, 'src'),
      diffPath: think.APP_PATH
    }, fileInfo => this._watcherCallBack(fileInfo));
    instance.watch();
  }
  /**
   * start server
   */
  startServer(port, host){
    const callback = () => {
      think.app.emit('appReady');
    }
    const createServer = () => {
      const createServerFn = think.config('createServer');
      if(createServerFn){
        assert(helper.isFunction(createServerFn), 'config.createServer must be a function');
        think.app.server = createServerFn(port, host, callback)
      }else{
        think.app.server = think.app.listen(port, host, callback);
      }
    }
    return think.beforeStartServer().catch(err => {
      think.logger.error(err);
    }).then(() => createServer());
  }
  /**
   * parse argv
   */
  parseArgv(){
    const argv2 = process.argv[2];
    const portRegExp = /^\d{2,5}$/;
    if(argv2){
      if(!portRegExp.test(argv2)){
        return {path: argv2};
      }
      return {port: argv2};
    }
    return {};
  }
  /**
   * run in master
   */
  runInMaster(){
    this.startWatcher();
    const instance = new thinkCluster.Master({
      workers: think.config('workers'),
      reloadSignal: think.config('reloadSignal'),
      enableAgent: think.config('enableAgent')
    });
    instance.forkWorkers().then(() => {
      think.app.emit('appReady');
    });
    this.masterInstance = instance;
  }
  /**
   * run in worker
   * @param {Object} argv
   */
  runInWorker(argv){
    let port = argv.port || think.config('port');
    const host = think.config('host');
    const instance = new thinkCluster.Worker({
      debug: think.env === 'development',
      logger: think.logger.error.bind(think.logger),
      processKillTimeout: think.config('processKillTimeout'),
      onUncaughtException: think.config('onUncaughtException'),
      onUnhandledRejection: think.config('onUnhandledRejection')
    });
    think.app.once('appReady', () => {
      if(thinkCluster.isFirstWorker()){
        think.logger.info(`Server running at http://${host || '127.0.0.1'}:${port}`);
        think.logger.info(`ThinkJS version: ${think.version}`);
        think.logger.info(`Enviroment: ${think.app.env}`);
        think.logger.info(`Workers: ${instance.getWorkers()}`);
        //think.logger.info(`Agent Worker: ${instance.options.enableAgent}`);
      }
      instance.options.server = think.app.server;
      instance.captureEvents();
    });
    this.startServer(port, host);
  }
  /**
   * command line invoke
   */
  runInCli(cliPath){
    mockHttp({
      url: cliPath,
      method: 'CLI'
    }, think.app);
  }
  /**
   * run in agent
   */
  runInAgent(){
    const instance = new thinkCluster.Agent();
    instance.createServer();
  }
  /**
   * run
   */
  run(){
    if(pm2.isClusterMode){
      throw new Error('can not use pm2 cluster mode, please change exec_mode to fork');
    }
    const instance = new thinkLoader(this.options);
    const argv = this.parseArgv();
    try{
      instance.loadAll(!!argv.path);
    }catch(e){
      think.logger.error(e);
    }
    if(argv.path){
      return this.runInCli(argv.path);
    }else if(cluster.isMaster){
      this.runInMaster();
    }else if(thinkCluster.isAgent()){
      this.runInAgent();
    }else{
      this.runInWorker(argv);
    }
  }
}<commit_msg>Change require module order<commit_after>const path = require('path');
const cluster = require('cluster');
const helper = require('think-helper');
const thinkCluster = require('think-cluster');
const pm2 = require('think-pm2');
const assert = require('assert');
const mockHttp = require('think-mock-http');

const thinkLoader = require('./loader.js');

/**
 * applition class
 */
module.exports = class Application {
  /**
   * constructor
   */
  constructor(options = {}){
    assert(options.ROOT_PATH, 'options.ROOT_PATH must be set');
    if(!options.APP_PATH){
      let appPath = path.join(options.ROOT_PATH, 'app');
      if(!options.transpiler && !helper.isDirectory(appPath)){
        appPath = path.join(options.ROOT_PATH, 'src');
      }
      options.APP_PATH = appPath;
    }
    this.options = options;
  }
  /**
   * notify error
   */
  notifier(err){
    if(!this.options.notifier) return;
    let notifier = this.options.notifier;
    if(!helper.isArray(notifier)){
      notifier = [notifier];
    }
    notifier[0](Object.assign({
      title: 'ThinkJS Transpile Error',
      message: err.message
    }, notifier[1]));
  }
  /**
   * watcher callback
   */
  _watcherCallBack(fileInfo){
    let transpiler = this.options.transpiler;
    if(transpiler){
      if(!helper.isArray(transpiler)){
        transpiler = [transpiler];
      }
      let ret = transpiler[0]({
        srcPath: fileInfo.path,
        outPath: think.APP_PATH,
        file: fileInfo.file,
        options: transpiler[1]
      });
      if(helper.isError(ret)){
        console.error(ret.stack);
        this.notifier(ret);
        return false;
      }
      think.logger.info(`transpile file ${fileInfo.file} success`);
    }
    //reload all workers
    if(this.masterInstance){
      this.masterInstance.forceReloadWorkers();
    }
  }
  /**
   * start watcher
   */
  startWatcher(){
    if(!this.options.watcher) return;
    const instance = new this.options.watcher({
      srcPath: path.join(think.ROOT_PATH, 'src'),
      diffPath: think.APP_PATH
    }, fileInfo => this._watcherCallBack(fileInfo));
    instance.watch();
  }
  /**
   * start server
   */
  startServer(port, host){
    const callback = () => {
      think.app.emit('appReady');
    }
    const createServer = () => {
      const createServerFn = think.config('createServer');
      if(createServerFn){
        assert(helper.isFunction(createServerFn), 'config.createServer must be a function');
        think.app.server = createServerFn(port, host, callback)
      }else{
        think.app.server = think.app.listen(port, host, callback);
      }
    }
    return think.beforeStartServer().catch(err => {
      think.logger.error(err);
    }).then(() => createServer());
  }
  /**
   * parse argv
   */
  parseArgv(){
    const argv2 = process.argv[2];
    const portRegExp = /^\d{2,5}$/;
    if(argv2){
      if(!portRegExp.test(argv2)){
        return {path: argv2};
      }
      return {port: argv2};
    }
    return {};
  }
  /**
   * run in master
   */
  runInMaster(){
    this.startWatcher();
    const instance = new thinkCluster.Master({
      workers: think.config('workers'),
      reloadSignal: think.config('reloadSignal'),
      enableAgent: think.config('enableAgent')
    });
    instance.forkWorkers().then(() => {
      think.app.emit('appReady');
    });
    this.masterInstance = instance;
  }
  /**
   * run in worker
   * @param {Object} argv
   */
  runInWorker(argv){
    let port = argv.port || think.config('port');
    const host = think.config('host');
    const instance = new thinkCluster.Worker({
      debug: think.env === 'development',
      logger: think.logger.error.bind(think.logger),
      processKillTimeout: think.config('processKillTimeout'),
      onUncaughtException: think.config('onUncaughtException'),
      onUnhandledRejection: think.config('onUnhandledRejection')
    });
    think.app.once('appReady', () => {
      if(thinkCluster.isFirstWorker()){
        think.logger.info(`Server running at http://${host || '127.0.0.1'}:${port}`);
        think.logger.info(`ThinkJS version: ${think.version}`);
        think.logger.info(`Enviroment: ${think.app.env}`);
        think.logger.info(`Workers: ${instance.getWorkers()}`);
        //think.logger.info(`Agent Worker: ${instance.options.enableAgent}`);
      }
      instance.options.server = think.app.server;
      instance.captureEvents();
    });
    this.startServer(port, host);
  }
  /**
   * command line invoke
   */
  runInCli(cliPath){
    mockHttp({
      url: cliPath,
      method: 'CLI'
    }, think.app);
  }
  /**
   * run in agent
   */
  runInAgent(){
    const instance = new thinkCluster.Agent();
    instance.createServer();
  }
  /**
   * run
   */
  run(){
    if(pm2.isClusterMode){
      throw new Error('can not use pm2 cluster mode, please change exec_mode to fork');
    }
    const instance = new thinkLoader(this.options);
    const argv = this.parseArgv();
    try{
      instance.loadAll(!!argv.path);
    }catch(e){
      think.logger.error(e);
    }
    if(argv.path){
      return this.runInCli(argv.path);
    }else if(cluster.isMaster){
      this.runInMaster();
    }else if(thinkCluster.isAgent()){
      this.runInAgent();
    }else{
      this.runInWorker(argv);
    }
  }
}<|endoftext|>