<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
				dojo.forEach(childItems, function(item, idx){
					if(!store.isItemLoaded(item)){
						store.loadItem({
							item: item,
							onItem: function(item){
								childItems[idx] = item;
								if(--_waitCount == 0){
									// all nodes have been loaded, send them to the tree
									onComplete(childItems);
								}
							},
							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>Need to copy array before splicing it, otherwise the data store thinks the old and new value is the same (because the "old value" has also been modified).   Fixes #11085.<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: item,
							onItem: function(item){
								childItems[idx] = item;
								if(--_waitCount == 0){
									// all nodes have been loaded, send them to the tree
									onComplete(childItems);
								}
							},
							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"){
					// call slice() to avoid modifying the original array, confusing the data store
					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 process, { exit } from 'node:process'
import { log } from '@serverless/utils/log.js'
import {
  commandOptions,
  CUSTOM_OPTION,
  defaultOptions,
  SERVER_SHUTDOWN_TIMEOUT,
} from './config/index.js'
import { gray, orange } from './config/colors.js'
import { createApiKey } from './utils/index.js'

export default class ServerlessOffline {
  #cliOptions = null

  #http = null

  #lambda = null

  #options = null

  #schedule = null

  #serverless = null

  #webSocket = null

  commands = {
    offline: {
      // add start nested options
      commands: {
        functionsUpdated: {
          lifecycleEvents: ['cleanup'],
          type: 'entrypoint',
        },
        start: {
          lifecycleEvents: ['init', 'ready', 'end'],
          options: commandOptions,
          usage:
            'Simulates API Gateway to call your lambda functions offline using backward compatible initialization.',
        },
      },
      lifecycleEvents: ['start'],
      options: commandOptions,
      usage: 'Simulates API Gateway to call your lambda functions offline.',
    },
  }

  hooks = {
    'offline:functionsUpdated:cleanup': this.#cleanupFunctions.bind(this),
    'offline:start': this.#startWithExplicitEnd.bind(this),
    'offline:start:end': this.end.bind(this),
    'offline:start:init': this.start.bind(this),
    'offline:start:ready': this.#ready.bind(this),
  }

  constructor(serverless, cliOptions) {
    this.#cliOptions = cliOptions
    this.#serverless = serverless
  }

  // Entry point for the plugin (sls offline) when running 'sls offline start'
  async start() {
    this.#mergeOptions()

    const { httpEvents, lambdas, scheduleEvents, webSocketEvents } =
      this.#getEvents()

    // if (lambdas.length > 0) {
    await this.#createLambda(lambdas)
    // }

    const eventModules = []

    if (httpEvents.length > 0) {
      eventModules.push(this.#createHttp(httpEvents))
    }

    if (!this.#options.disableScheduledEvents && scheduleEvents.length > 0) {
      eventModules.push(this.#createSchedule(scheduleEvents))
    }

    if (webSocketEvents.length > 0) {
      eventModules.push(this.#createWebSocket(webSocketEvents))
    }

    await Promise.all(eventModules)
  }

  async #ready() {
    await this.#listenForTermination()
  }

  async end(skipExit) {
    log.info('Halting offline server')

    const eventModules = []

    if (this.#lambda) {
      eventModules.push(this.#lambda.cleanup())
      eventModules.push(this.#lambda.stop(SERVER_SHUTDOWN_TIMEOUT))
    }

    if (this.#http) {
      eventModules.push(this.#http.stop(SERVER_SHUTDOWN_TIMEOUT))
    }

    // if (this.#schedule) {
    //   eventModules.push(this.#schedule.stop())
    // }

    if (this.#webSocket) {
      eventModules.push(this.#webSocket.stop(SERVER_SHUTDOWN_TIMEOUT))
    }

    await Promise.all(eventModules)

    if (!skipExit) {
      exit(0)
    }
  }

  async #cleanupFunctions() {
    if (this.#lambda) {
      log.debug('Forcing cleanup of Lambda functions')
      await this.#lambda.cleanup()
    }
  }

  /**
   * Entry point for the plugin (serverless offline) when running 'serverless offline'
   * The call to this.end() would terminate the process before 'offline:start:end' could be consumed
   * by downstream plugins. When running serverless offline that can be expected, but docs say that
   * 'serverless offline start' will provide the init and end hooks for other plugins to consume
   * */
  async #startWithExplicitEnd() {
    await this.start()
    await this.#ready()
    await this.end()
  }

  async #listenForTermination() {
    const command = await new Promise((resolve) => {
      process
        // SIGINT will be usually sent when user presses ctrl+c
        .on('SIGINT', () => resolve('SIGINT'))
        // SIGTERM is a default termination signal in many cases,
        // for example when "killing" a subprocess spawned in node
        // with child_process methods
        .on('SIGTERM', () => resolve('SIGTERM'))
    })

    log.info(`Got ${command} signal. Offline Halting...`)
  }

  async #createLambda(lambdas, skipStart) {
    const { default: Lambda } = await import('./lambda/index.js')

    this.#lambda = new Lambda(this.#serverless, this.#options)

    this.#lambda.create(lambdas)

    if (!skipStart) {
      await this.#lambda.start()
    }
  }

  async #createHttp(events, skipStart) {
    const { default: Http } = await import('./events/http/index.js')

    this.#http = new Http(this.#serverless, this.#options, this.#lambda)

    await this.#http.createServer()

    this.#http.create(events)

    // HTTP Proxy defined in Resource
    this.#http.createResourceRoutes()

    // Not found handling
    // we have to create the 404 routes last, otherwise we could have
    // collisions with catch all routes, e.g. any (proxy+}
    this.#http.create404Route()

    if (!skipStart) {
      await this.#http.start()
    }
  }

  async #createSchedule(events) {
    const { default: Schedule } = await import('./events/schedule/index.js')

    this.#schedule = new Schedule(
      this.#lambda,
      this.#serverless.service.provider.region,
    )

    this.#schedule.create(events)
  }

  async #createWebSocket(events) {
    const { default: WebSocket } = await import('./events/websocket/index.js')

    this.#webSocket = new WebSocket(
      this.#serverless,
      this.#options,
      this.#lambda,
    )

    this.#webSocket.create(events)

    return this.#webSocket.start()
  }

  #mergeOptions() {
    const {
      service: { custom = {}, provider },
    } = this.#serverless

    const customOptions = custom[CUSTOM_OPTION]

    // merge options
    // order of Precedence: command line options, custom options, defaults.
    this.#options = {
      ...defaultOptions,
      ...customOptions,
      ...this.#cliOptions,
    }

    // Parse CORS options
    this.#options.corsAllowHeaders = this.#options.corsAllowHeaders
      .replace(/\s/g, '')
      .split(',')
    this.#options.corsAllowOrigin = this.#options.corsAllowOrigin
      .replace(/\s/g, '')
      .split(',')
    this.#options.corsExposedHeaders = this.#options.corsExposedHeaders
      .replace(/\s/g, '')
      .split(',')

    this.#options.corsConfig = {
      credentials: !this.#options.corsDisallowCredentials,
      exposedHeaders: this.#options.corsExposedHeaders,
      headers: this.#options.corsAllowHeaders,
      origin: this.#options.corsAllowOrigin,
    }

    log.notice()
    log.notice(
      `Starting Offline at stage ${
        this.#options.stage || provider.stage
      } ${gray(`(${this.#options.region || provider.region})`)}`,
    )
    log.notice()
    log.debug('options:', this.#options)
  }

  #getEvents() {
    const { service } = this.#serverless

    const httpEvents = []
    const lambdas = []
    const scheduleEvents = []
    const webSocketEvents = []

    const functionKeys = service.getAllFunctions()

    let hasPrivateHttpEvent = false

    functionKeys.forEach((functionKey) => {
      const functionDefinition = service.getFunction(functionKey)

      lambdas.push({ functionDefinition, functionKey })

      const events = service.getAllEventsInFunction(functionKey) || []

      events.forEach((event) => {
        const { http, httpApi, schedule, websocket } = event

        if ((http || httpApi) && functionDefinition.handler) {
          const httpEvent = {
            functionKey,
            handler: functionDefinition.handler,
            http: http || httpApi,
          }

          if (httpApi) {
            // Ensure definitions for 'httpApi' events are objects so that they can be marked
            // with an 'isHttpApi' property (they are handled differently to 'http' events)
            if (typeof httpEvent.http === 'string') {
              httpEvent.http = {
                routeKey: httpEvent.http === '*' ? '$default' : httpEvent.http,
              }
            } else if (typeof httpEvent.http === 'object') {
              if (!httpEvent.http.method) {
                log.warning(
                  `Event definition is missing a method for function "${functionKey}"`,
                )
                httpEvent.http.method = ''
              }
              if (
                httpEvent.http.method === '*' &&
                httpEvent.http.path === '*'
              ) {
                httpEvent.http.routeKey = '$default'
              } else {
                const resolvedMethod =
                  httpEvent.http.method === '*'
                    ? 'ANY'
                    : httpEvent.http.method.toUpperCase()
                httpEvent.http.routeKey = `${resolvedMethod} ${httpEvent.http.path}`
              }
              // Clear these properties to avoid confusion (they will be derived from the routeKey
              // when needed later)
              delete httpEvent.http.method
              delete httpEvent.http.path
            } else {
              log.warning(
                `Event definition must be a string or object but received ${typeof httpEvent.http} for function "${functionKey}"`,
              )
              httpEvent.http.routeKey = ''
            }

            httpEvent.http.isHttpApi = true

            if (
              functionDefinition.httpApi &&
              functionDefinition.httpApi.payload
            ) {
              httpEvent.http.payload = functionDefinition.httpApi.payload
            } else {
              httpEvent.http.payload =
                service.provider.httpApi && service.provider.httpApi.payload
                  ? service.provider.httpApi.payload
                  : '2.0'
            }
          }

          if (http && http.private) {
            hasPrivateHttpEvent = true
          }

          httpEvents.push(httpEvent)
        }

        if (schedule) {
          scheduleEvents.push({
            functionKey,
            schedule,
          })
        }

        if (websocket) {
          webSocketEvents.push({
            functionKey,
            websocket,
          })
        }
      })
    })

    // for simple API Key authentication model
    if (hasPrivateHttpEvent) {
      if (this.#options.apiKey) {
        log.notice()
        log.warning(
          orange(`'--apiKey' is deprecated and will be removed in the next major version.
  Please define the apiKey value in the 'provider.apiGateway.apiKeys' section of the serverless config.
  If you are experiencing any issues please let us know: https://github.com/dherault/serverless-offline/issues`),
        )
        log.notice()
      } else {
        this.#options.apiKey = createApiKey()
      }

      log.notice(`Key with token: ${this.#options.apiKey}`)

      if (this.#options.noAuth) {
        log.notice(
          `Authorizers are turned off. You do not need to use 'x-api-key' header.`,
        )
      } else {
        log.notice(`Remember to use 'x-api-key' on the request headers.`)
      }
    }

    return {
      httpEvents,
      lambdas,
      scheduleEvents,
      webSocketEvents,
    }
  }

  // TODO FIXME
  // TEMP quick fix to expose for testing, look for better solution
  internals() {
    return {
      createHttp: (events, skipStart) => {
        return this.#createHttp(events, skipStart)
      },

      createLambda: (lambdas, skipStart) => {
        return this.#createLambda(lambdas, skipStart)
      },

      getApiGatewayServer: () => {
        return this.#http.getServer()
      },

      getEvents: () => {
        return this.#getEvents()
      },

      mergeOptions: () => {
        this.#mergeOptions()
      },
    }
  }
}
<commit_msg>refactor: use optional chaining<commit_after>import process, { exit } from 'node:process'
import { log } from '@serverless/utils/log.js'
import {
  commandOptions,
  CUSTOM_OPTION,
  defaultOptions,
  SERVER_SHUTDOWN_TIMEOUT,
} from './config/index.js'
import { gray, orange } from './config/colors.js'
import { createApiKey } from './utils/index.js'

export default class ServerlessOffline {
  #cliOptions = null

  #http = null

  #lambda = null

  #options = null

  #schedule = null

  #serverless = null

  #webSocket = null

  commands = {
    offline: {
      // add start nested options
      commands: {
        functionsUpdated: {
          lifecycleEvents: ['cleanup'],
          type: 'entrypoint',
        },
        start: {
          lifecycleEvents: ['init', 'ready', 'end'],
          options: commandOptions,
          usage:
            'Simulates API Gateway to call your lambda functions offline using backward compatible initialization.',
        },
      },
      lifecycleEvents: ['start'],
      options: commandOptions,
      usage: 'Simulates API Gateway to call your lambda functions offline.',
    },
  }

  hooks = {
    'offline:functionsUpdated:cleanup': this.#cleanupFunctions.bind(this),
    'offline:start': this.#startWithExplicitEnd.bind(this),
    'offline:start:end': this.end.bind(this),
    'offline:start:init': this.start.bind(this),
    'offline:start:ready': this.#ready.bind(this),
  }

  constructor(serverless, cliOptions) {
    this.#cliOptions = cliOptions
    this.#serverless = serverless
  }

  // Entry point for the plugin (sls offline) when running 'sls offline start'
  async start() {
    this.#mergeOptions()

    const { httpEvents, lambdas, scheduleEvents, webSocketEvents } =
      this.#getEvents()

    // if (lambdas.length > 0) {
    await this.#createLambda(lambdas)
    // }

    const eventModules = []

    if (httpEvents.length > 0) {
      eventModules.push(this.#createHttp(httpEvents))
    }

    if (!this.#options.disableScheduledEvents && scheduleEvents.length > 0) {
      eventModules.push(this.#createSchedule(scheduleEvents))
    }

    if (webSocketEvents.length > 0) {
      eventModules.push(this.#createWebSocket(webSocketEvents))
    }

    await Promise.all(eventModules)
  }

  async #ready() {
    await this.#listenForTermination()
  }

  async end(skipExit) {
    log.info('Halting offline server')

    const eventModules = []

    if (this.#lambda) {
      eventModules.push(this.#lambda.cleanup())
      eventModules.push(this.#lambda.stop(SERVER_SHUTDOWN_TIMEOUT))
    }

    if (this.#http) {
      eventModules.push(this.#http.stop(SERVER_SHUTDOWN_TIMEOUT))
    }

    // if (this.#schedule) {
    //   eventModules.push(this.#schedule.stop())
    // }

    if (this.#webSocket) {
      eventModules.push(this.#webSocket.stop(SERVER_SHUTDOWN_TIMEOUT))
    }

    await Promise.all(eventModules)

    if (!skipExit) {
      exit(0)
    }
  }

  async #cleanupFunctions() {
    if (this.#lambda) {
      log.debug('Forcing cleanup of Lambda functions')
      await this.#lambda.cleanup()
    }
  }

  /**
   * Entry point for the plugin (serverless offline) when running 'serverless offline'
   * The call to this.end() would terminate the process before 'offline:start:end' could be consumed
   * by downstream plugins. When running serverless offline that can be expected, but docs say that
   * 'serverless offline start' will provide the init and end hooks for other plugins to consume
   * */
  async #startWithExplicitEnd() {
    await this.start()
    await this.#ready()
    await this.end()
  }

  async #listenForTermination() {
    const command = await new Promise((resolve) => {
      process
        // SIGINT will be usually sent when user presses ctrl+c
        .on('SIGINT', () => resolve('SIGINT'))
        // SIGTERM is a default termination signal in many cases,
        // for example when "killing" a subprocess spawned in node
        // with child_process methods
        .on('SIGTERM', () => resolve('SIGTERM'))
    })

    log.info(`Got ${command} signal. Offline Halting...`)
  }

  async #createLambda(lambdas, skipStart) {
    const { default: Lambda } = await import('./lambda/index.js')

    this.#lambda = new Lambda(this.#serverless, this.#options)

    this.#lambda.create(lambdas)

    if (!skipStart) {
      await this.#lambda.start()
    }
  }

  async #createHttp(events, skipStart) {
    const { default: Http } = await import('./events/http/index.js')

    this.#http = new Http(this.#serverless, this.#options, this.#lambda)

    await this.#http.createServer()

    this.#http.create(events)

    // HTTP Proxy defined in Resource
    this.#http.createResourceRoutes()

    // Not found handling
    // we have to create the 404 routes last, otherwise we could have
    // collisions with catch all routes, e.g. any (proxy+}
    this.#http.create404Route()

    if (!skipStart) {
      await this.#http.start()
    }
  }

  async #createSchedule(events) {
    const { default: Schedule } = await import('./events/schedule/index.js')

    this.#schedule = new Schedule(
      this.#lambda,
      this.#serverless.service.provider.region,
    )

    this.#schedule.create(events)
  }

  async #createWebSocket(events) {
    const { default: WebSocket } = await import('./events/websocket/index.js')

    this.#webSocket = new WebSocket(
      this.#serverless,
      this.#options,
      this.#lambda,
    )

    this.#webSocket.create(events)

    return this.#webSocket.start()
  }

  #mergeOptions() {
    const {
      service: { custom = {}, provider },
    } = this.#serverless

    const customOptions = custom[CUSTOM_OPTION]

    // merge options
    // order of Precedence: command line options, custom options, defaults.
    this.#options = {
      ...defaultOptions,
      ...customOptions,
      ...this.#cliOptions,
    }

    // Parse CORS options
    this.#options.corsAllowHeaders = this.#options.corsAllowHeaders
      .replace(/\s/g, '')
      .split(',')
    this.#options.corsAllowOrigin = this.#options.corsAllowOrigin
      .replace(/\s/g, '')
      .split(',')
    this.#options.corsExposedHeaders = this.#options.corsExposedHeaders
      .replace(/\s/g, '')
      .split(',')

    this.#options.corsConfig = {
      credentials: !this.#options.corsDisallowCredentials,
      exposedHeaders: this.#options.corsExposedHeaders,
      headers: this.#options.corsAllowHeaders,
      origin: this.#options.corsAllowOrigin,
    }

    log.notice()
    log.notice(
      `Starting Offline at stage ${
        this.#options.stage || provider.stage
      } ${gray(`(${this.#options.region || provider.region})`)}`,
    )
    log.notice()
    log.debug('options:', this.#options)
  }

  #getEvents() {
    const { service } = this.#serverless

    const httpEvents = []
    const lambdas = []
    const scheduleEvents = []
    const webSocketEvents = []

    const functionKeys = service.getAllFunctions()

    let hasPrivateHttpEvent = false

    functionKeys.forEach((functionKey) => {
      const functionDefinition = service.getFunction(functionKey)

      lambdas.push({ functionDefinition, functionKey })

      const events = service.getAllEventsInFunction(functionKey) || []

      events.forEach((event) => {
        const { http, httpApi, schedule, websocket } = event

        if ((http || httpApi) && functionDefinition.handler) {
          const httpEvent = {
            functionKey,
            handler: functionDefinition.handler,
            http: http || httpApi,
          }

          if (httpApi) {
            // Ensure definitions for 'httpApi' events are objects so that they can be marked
            // with an 'isHttpApi' property (they are handled differently to 'http' events)
            if (typeof httpEvent.http === 'string') {
              httpEvent.http = {
                routeKey: httpEvent.http === '*' ? '$default' : httpEvent.http,
              }
            } else if (typeof httpEvent.http === 'object') {
              if (!httpEvent.http.method) {
                log.warning(
                  `Event definition is missing a method for function "${functionKey}"`,
                )
                httpEvent.http.method = ''
              }
              if (
                httpEvent.http.method === '*' &&
                httpEvent.http.path === '*'
              ) {
                httpEvent.http.routeKey = '$default'
              } else {
                const resolvedMethod =
                  httpEvent.http.method === '*'
                    ? 'ANY'
                    : httpEvent.http.method.toUpperCase()
                httpEvent.http.routeKey = `${resolvedMethod} ${httpEvent.http.path}`
              }
              // Clear these properties to avoid confusion (they will be derived from the routeKey
              // when needed later)
              delete httpEvent.http.method
              delete httpEvent.http.path
            } else {
              log.warning(
                `Event definition must be a string or object but received ${typeof httpEvent.http} for function "${functionKey}"`,
              )
              httpEvent.http.routeKey = ''
            }

            httpEvent.http.isHttpApi = true

            if (
              functionDefinition.httpApi &&
              functionDefinition.httpApi.payload
            ) {
              httpEvent.http.payload = functionDefinition.httpApi.payload
            } else {
              httpEvent.http.payload =
                service.provider.httpApi && service.provider.httpApi.payload
                  ? service.provider.httpApi.payload
                  : '2.0'
            }
          }

          if (http?.private) {
            hasPrivateHttpEvent = true
          }

          httpEvents.push(httpEvent)
        }

        if (schedule) {
          scheduleEvents.push({
            functionKey,
            schedule,
          })
        }

        if (websocket) {
          webSocketEvents.push({
            functionKey,
            websocket,
          })
        }
      })
    })

    // for simple API Key authentication model
    if (hasPrivateHttpEvent) {
      if (this.#options.apiKey) {
        log.notice()
        log.warning(
          orange(`'--apiKey' is deprecated and will be removed in the next major version.
  Please define the apiKey value in the 'provider.apiGateway.apiKeys' section of the serverless config.
  If you are experiencing any issues please let us know: https://github.com/dherault/serverless-offline/issues`),
        )
        log.notice()
      } else {
        this.#options.apiKey = createApiKey()
      }

      log.notice(`Key with token: ${this.#options.apiKey}`)

      if (this.#options.noAuth) {
        log.notice(
          `Authorizers are turned off. You do not need to use 'x-api-key' header.`,
        )
      } else {
        log.notice(`Remember to use 'x-api-key' on the request headers.`)
      }
    }

    return {
      httpEvents,
      lambdas,
      scheduleEvents,
      webSocketEvents,
    }
  }

  // TODO FIXME
  // TEMP quick fix to expose for testing, look for better solution
  internals() {
    return {
      createHttp: (events, skipStart) => {
        return this.#createHttp(events, skipStart)
      },

      createLambda: (lambdas, skipStart) => {
        return this.#createLambda(lambdas, skipStart)
      },

      getApiGatewayServer: () => {
        return this.#http.getServer()
      },

      getEvents: () => {
        return this.#getEvents()
      },

      mergeOptions: () => {
        this.#mergeOptions()
      },
    }
  }
}
<|endoftext|>


<commit_before>// @flow

import type {ApplicationAppState} from './ApplicationState';
import boilerplateReducer from './Actions/BoilerplateActions';
import {initialState} from './initialState';
import {descriptionReducer, filterConfigsReducer, inputText, regexpFlowClearer, regexpFlowDeleter, regexpFlowLoader, regexpFlowSaver} from './Actions/RegexpFlowActions';
import FilterRunner from '../RegexpFlow/FilterRunner';

const mainReducer = (state: ApplicationAppState | void, action: any) => {

    if (state === undefined) {
        // https://redux.js.org/docs/api/combineReducers.html#notes
        // > If the state given to it is undefined, it must return the initial state for this specific reducer. According to the previous rule, the initial state must not be undefined either.
        // combineReducers() is used by redux-persis
        return initialState;
    }

    let filterConfigsNew = filterConfigsReducer(state.currentRegexpFlow.filterConfigs, action);
    let descriptionNew = descriptionReducer(state.currentRegexpFlow.description, action);
    let inputTextNew = inputText(state.inputText, action);

    let runner = new FilterRunner();

    let outputTextNew = runner.processString(filterConfigsNew, inputTextNew);

    let newState: ApplicationAppState = Object.assign({}, state, {
        inputText: inputTextNew,
        outputText: outputTextNew,
        currentRegexpFlow: {
            id: state.currentRegexpFlow.id,
            filterConfigs: filterConfigsNew,
            description: descriptionNew,
        },
        regexpFlows: state.regexpFlows,
        nextRegexpFlowIndex: state.nextRegexpFlowIndex,
    });

    // changes .currentRegexpFlow, .regexpFlows
    const newState2 = regexpFlowSaver(newState, action);
    Object.assign(newState, newState2);

    // changes .currentRegexpFlow
    const newState3 = regexpFlowLoader(newState, action);
    Object.assign(newState, newState3);

    // changes .currentRegexpFlow
    const newState4 = regexpFlowClearer(newState, action);
    Object.assign(newState, newState4);

    // changes .currentRegexpFlow
    const newState5 = regexpFlowDeleter(newState, action);
    Object.assign(newState, newState5);

    const newState6 = boilerplateReducer(newState, action);
    Object.assign(newState, newState6);

    return newState;
};

export default mainReducer;
<commit_msg>redux-persist: handling REHYDRATE action<commit_after>// @flow

import type {ApplicationAppState} from './ApplicationState';
import boilerplateReducer from './Actions/BoilerplateActions';
import {initialState} from './initialState';
import {descriptionReducer, filterConfigsReducer, inputText, regexpFlowClearer, regexpFlowDeleter, regexpFlowLoader, regexpFlowSaver} from './Actions/RegexpFlowActions';
import FilterRunner from '../RegexpFlow/FilterRunner';
import {REHYDRATE} from 'redux-persist/es/constants';

const mainReducer = (state: ApplicationAppState | void, action: any) => {

    if (state === undefined) {
        // https://redux.js.org/docs/api/combineReducers.html#notes
        // > If the state given to it is undefined, it must return the initial state for this specific reducer. According to the previous rule, the initial state must not be undefined either.
        // combineReducers() is used by redux-persis
        return initialState;
    }

    if (action.type === REHYDRATE) {
        if (action.payload === undefined) {
            return initialState.app;
        }

        return action.payload.app;
    }

    let filterConfigsNew = filterConfigsReducer(state.currentRegexpFlow.filterConfigs, action);
    let descriptionNew = descriptionReducer(state.currentRegexpFlow.description, action);
    let inputTextNew = inputText(state.inputText, action);

    let runner = new FilterRunner();

    let outputTextNew = runner.processString(filterConfigsNew, inputTextNew);

    let newState: ApplicationAppState = Object.assign({}, state, {
        inputText: inputTextNew,
        outputText: outputTextNew,
        currentRegexpFlow: {
            id: state.currentRegexpFlow.id,
            filterConfigs: filterConfigsNew,
            description: descriptionNew,
        },
        regexpFlows: state.regexpFlows,
        nextRegexpFlowIndex: state.nextRegexpFlowIndex,
    });

    // changes .currentRegexpFlow, .regexpFlows
    const newState2 = regexpFlowSaver(newState, action);
    Object.assign(newState, newState2);

    // changes .currentRegexpFlow
    const newState3 = regexpFlowLoader(newState, action);
    Object.assign(newState, newState3);

    // changes .currentRegexpFlow
    const newState4 = regexpFlowClearer(newState, action);
    Object.assign(newState, newState4);

    // changes .currentRegexpFlow
    const newState5 = regexpFlowDeleter(newState, action);
    Object.assign(newState, newState5);

    const newState6 = boilerplateReducer(newState, action);
    Object.assign(newState, newState6);

    return newState;
};

export default mainReducer;
<|endoftext|>


<commit_before>/**
 * Created by DT314 on 2017/5/5.
 */
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import Event from '../_vendors/Event';

import './TextArea.css';

export default class TextArea extends Component {
    constructor(props) {
        super();

        this.state = {
            focus: false,
            width: props.initialWidth,
            height: props.initialHeight,
            value: props.value
        };

        this.onFocus = this :: this.onFocus;
        this.onBlur = this :: this.onBlur;
        this.onChange = this :: this.onChange;
        this.resizeHandle = this :: this.resizeHandle;
        this.mouseUpHandle = this :: this.mouseUpHandle;

    }

    onFocus() {
        this.setState({
            focus: true
        }, () => {
            this.props.onFocus && this.props.onFocus();
        });
    }

    onBlur() {
        this.setState({
            focus: false
        }, () => {
            this.props.onBlur && this.props.onBlur();
        });
    }

    /**
     * 根据textArea的宽高设置state中宽高
     */
    resizeHandle(e) {
        this.setState({
            width: e.target.clientWidth + 2,
            height: e.target.clientHeight + 2
        });
    }

    /**
     * mouseUp时，若textArea的宽高右边，则resizeHandle
     */
    mouseUpHandle(e) {
        if (e.target.clientWidth + 2 !== this.state.width || e.target.clientHeight + 2 !== this.state.height) {
            this.resizeHandle(e);
        }
    }

    /**
     * input变化时，如果为autoSize，则控制高度，使之随内容变化而变化
     */
    onChange(ev) {
        const {initialHeight, maxHeight, autoSize} = this.props;
        const oEvent = ev || event;
        const {target} = oEvent;

        if (autoSize) {

            const {style} = target;

            style.height = initialHeight + 'px';
            style.overflow = 'hidden';
            if (initialHeight < target.scrollHeight + 2) {
                if (maxHeight && maxHeight > target.scrollHeight + 2) {
                    style.height = target.scrollHeight + 2 + 'px';
                } else {
                    style.height = maxHeight + 'px';
                    style.overflow = 'auto';
                }
            }
        }

        this.setState({
            value: target.value
        }, () => this.props.onChange && this.props.onChange());

    }

    componentWillReceiveProps(nextProps) {
        const {width, height, value} = this.state;

        if (nextProps.initialWidth !== width || nextProps.initialHeight !== height || nextProps.value !== value) {
            this.setState({
                width: nextProps.initialWidth,
                height: nextProps.initialHeight,
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        const {autoSize, autoFocus} = this.props;

        if (!autoSize) {
            Event.addEvent(this.refs.textarea, 'mouseup', this.mouseUpHandle);
        }

        if (autoFocus === true) {
            this.refs.textarea.focus();
        }
    }

    componentWillUnmount() {
        const {autoSize} = this.props;

        if (!autoSize) {
            Event.removeEvent(this.refs.textarea, 'mouseup', this.mouseUpHandle);
        }
    }

    render() {
        const {className, style, cols, rows, autoSize, placeholder, maxWidth} = this.props;
        const {focus, width, height, value} = this.state;

        let textStyle = {
            height: height,
            width: width,
            overflow: autoSize ? 'hidden' : 'auto',
            resize: autoSize ? 'none' : 'both',
            maxWidth: maxWidth,
            ...style
        };

        return (
            <textarea ref="textarea"
                      className={`text-area ${focus ? 'area-focus' : ''} ${className}`}
                      style={{...textStyle}}
                      cols={cols}
                      rows={rows}
                      placeholder={placeholder}
                      value={value}
                      autoComplete="off"
                      autoCorrect="off"
                      autoCapitalize="off"
                      spellCheck="false"
                      onFocus={this.onFocus}
                      onBlur={this.onBlur}
                      onInput={this.onChange}>
            </textarea>
        );
    }
};

TextArea.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The number of cols.
     */
    cols: PropTypes.number,

    /**
     * The number of rows.
     */
    rows: PropTypes.number,

    /**
     * The initial height of textArea.
     */
    initialHeight: PropTypes.number,

    /**
     * The initial height of textArea.
     */
    initialWidth: PropTypes.number,

    /**
     * The value of textArea.
     */
    value: PropTypes.string,

    /**
     * The max height of textArea.
     */
    maxHeight: PropTypes.number,

    /**
     * The max width of textArea.
     */
    maxWidth: PropTypes.number,

    /**
     * If true, the textArea will change its size by words.
     */
    autoSize: PropTypes.bool,

    /**
     * If true,the textField will autoFocus.
     */
    autoFocus: PropTypes.bool,

    /**
     * The placeholder of textArea.
     */
    placeholder: PropTypes.string,

    /**
     * When the value of textArea change, it will execute.
     */
    onChange: PropTypes.func,

    /**
     * When the textArea onBlur, it will execute.
     */
    onBlur: PropTypes.func,

    /**
     * When the textArea onFocus, it will execute.
     */
    onFocus: PropTypes.func

};

TextArea.defaultProps = {

    className: '',
    style: null,

    cols: 50,
    rows: 3,
    initialHeight: 30,
    initialWidth: 200,
    maxHeight: 100,
    maxWidth: 500,
    autoSize: true,
    autoFocus: false,
    placeholder: 'Please input something.',
    value: ''

};<commit_msg>update TextArea: add onChange callback<commit_after>/**
 * Created by DT314 on 2017/5/5.
 */
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import Event from '../_vendors/Event';

import './TextArea.css';

export default class TextArea extends Component {
    constructor(props) {
        super();

        this.state = {
            focus: false,
            width: props.initialWidth,
            height: props.initialHeight,
            value: props.value
        };

        this.onFocus = this :: this.onFocus;
        this.onBlur = this :: this.onBlur;
        this.onChange = this :: this.onChange;
        this.resizeHandle = this :: this.resizeHandle;
        this.mouseUpHandle = this :: this.mouseUpHandle;

    }

    onFocus() {
        this.setState({
            focus: true
        }, () => {
            this.props.onFocus && this.props.onFocus();
        });
    }

    onBlur() {
        this.setState({
            focus: false
        }, () => {
            this.props.onBlur && this.props.onBlur();
        });
    }

    /**
     * 根据textArea的宽高设置state中宽高
     */
    resizeHandle(e) {
        this.setState({
            width: e.target.clientWidth + 2,
            height: e.target.clientHeight + 2
        });
    }

    /**
     * mouseUp时，若textArea的宽高右边，则resizeHandle
     */
    mouseUpHandle(e) {
        if (e.target.clientWidth + 2 !== this.state.width || e.target.clientHeight + 2 !== this.state.height) {
            this.resizeHandle(e);
        }
    }

    /**
     * input变化时，如果为autoSize，则控制高度，使之随内容变化而变化
     */
    onChange(ev) {
        const {initialHeight, maxHeight, autoSize} = this.props;
        const oEvent = ev || event;
        const {target} = oEvent;

        if (autoSize) {

            const {style} = target;

            style.height = initialHeight + 'px';
            style.overflow = 'hidden';
            if (initialHeight < target.scrollHeight + 2) {
                if (maxHeight && maxHeight > target.scrollHeight + 2) {
                    style.height = target.scrollHeight + 2 + 'px';
                } else {
                    style.height = maxHeight + 'px';
                    style.overflow = 'auto';
                }
            }
        }

        this.setState({
            value: target.value
        }, () => this.props.onChange && this.props.onChange());

    }

    componentWillReceiveProps(nextProps) {
        const {width, height, value} = this.state;

        if (nextProps.initialWidth !== width || nextProps.initialHeight !== height || nextProps.value !== value) {
            this.setState({
                width: nextProps.initialWidth,
                height: nextProps.initialHeight,
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        const {autoSize, autoFocus} = this.props;

        if (!autoSize) {
            Event.addEvent(this.refs.textarea, 'mouseup', this.mouseUpHandle);
        }

        if (autoFocus === true) {
            this.refs.textarea.focus();
        }
    }

    componentWillUnmount() {
        const {autoSize} = this.props;

        if (!autoSize) {
            Event.removeEvent(this.refs.textarea, 'mouseup', this.mouseUpHandle);
        }
    }

    render() {
        const {className, style, cols, rows, autoSize, placeholder, maxWidth} = this.props;
        const {focus, width, height, value} = this.state;

        let textStyle = {
            height: height,
            width: width,
            overflow: autoSize ? 'hidden' : 'auto',
            resize: autoSize ? 'none' : 'both',
            maxWidth: maxWidth,
            ...style
        };

        return (
            <textarea ref="textarea"
                      className={`text-area ${focus ? 'area-focus' : ''} ${className}`}
                      style={{...textStyle}}
                      cols={cols}
                      rows={rows}
                      placeholder={placeholder}
                      value={value}
                      autoComplete="off"
                      autoCorrect="off"
                      autoCapitalize="off"
                      spellCheck="false"
                      onFocus={this.onFocus}
                      onBlur={this.onBlur}
                      onChange={this.onChange}
                      onInput={this.onChange}>
            </textarea>
        );
    }
};

TextArea.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The number of cols.
     */
    cols: PropTypes.number,

    /**
     * The number of rows.
     */
    rows: PropTypes.number,

    /**
     * The initial height of textArea.
     */
    initialHeight: PropTypes.number,

    /**
     * The initial height of textArea.
     */
    initialWidth: PropTypes.number,

    /**
     * The value of textArea.
     */
    value: PropTypes.string,

    /**
     * The max height of textArea.
     */
    maxHeight: PropTypes.number,

    /**
     * The max width of textArea.
     */
    maxWidth: PropTypes.number,

    /**
     * If true, the textArea will change its size by words.
     */
    autoSize: PropTypes.bool,

    /**
     * If true,the textField will autoFocus.
     */
    autoFocus: PropTypes.bool,

    /**
     * The placeholder of textArea.
     */
    placeholder: PropTypes.string,

    /**
     * When the value of textArea change, it will execute.
     */
    onChange: PropTypes.func,

    /**
     * When the textArea onBlur, it will execute.
     */
    onBlur: PropTypes.func,

    /**
     * When the textArea onFocus, it will execute.
     */
    onFocus: PropTypes.func

};

TextArea.defaultProps = {

    className: '',
    style: null,

    cols: 50,
    rows: 3,
    initialHeight: 30,
    initialWidth: 200,
    maxHeight: 100,
    maxWidth: 500,
    autoSize: true,
    autoFocus: false,
    placeholder: 'Please input something.',
    value: ''

};<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Phone from './source/Input.js';
import metadata from 'libphonenumber-js/metadata.min.json';

import './component/rrui.css';
import './component/style.css';

export default class PhoneNumber extends Component {

  static propTypes = {
    id: PropTypes.string,
    labelText: PropTypes.string,
    infoMessage: PropTypes.string,
    errorMessage: PropTypes.string
  }

  static defaultProps = {
    fancy: true
  }

  constructor(props) {
    super(props)
  }

  render() {
    const { id, country, placeholder, onChange, value, onKeyDown, disabled,
            onCountryChange, countries, international, convertToNational,
            selectMaxItems, className, inputClassName, labelText,
            infoMessage, errorMessage, fancy } = this.props;

    const errorLabel = errorMessage ? '--label_error' :'';
    const errorInput = errorMessage ? 'react-phone-number-input__phone--error' : inputClassName;

    return (

        <label className={`pe-textLabelInput__label${errorLabel}`}>{labelText}

        <Phone
          id={id}
          value={value}
          country={country}
          placeholder={placeholder}
          onChange={onChange}
          onKeyDown={onKeyDown}
          disabled={disabled}
          onCountryChange={onCountryChange}
          countries={countries}
          international={international}
          convertToNational={convertToNational}
          selectMaxItems={selectMaxItems}
          className={className}
          inputClassName={errorInput}
          metadata={metadata}
          fancy={fancy}
        />
        {infoMessage &&
{infoMessage}
}
        {errorMessage &&
{errorMessage}
}

    );
  }
}
<commit_msg>chore: fix path<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Phone from './components/Input.js';
import metadata from 'libphonenumber-js/metadata.min.json';

import './component/rrui.css';
import './component/style.css';

export default class PhoneNumber extends Component {

  static propTypes = {
    id: PropTypes.string,
    labelText: PropTypes.string,
    infoMessage: PropTypes.string,
    errorMessage: PropTypes.string
  }

  static defaultProps = {
    fancy: true
  }

  constructor(props) {
    super(props)
  }

  render() {
    const { id, country, placeholder, onChange, value, onKeyDown, disabled,
            onCountryChange, countries, international, convertToNational,
            selectMaxItems, className, inputClassName, labelText,
            infoMessage, errorMessage, fancy } = this.props;

    const errorLabel = errorMessage ? '--label_error' :'';
    const errorInput = errorMessage ? 'react-phone-number-input__phone--error' : inputClassName;

    return (

        <label className={`pe-textLabelInput__label${errorLabel}`}>{labelText}

        <Phone
          id={id}
          value={value}
          country={country}
          placeholder={placeholder}
          onChange={onChange}
          onKeyDown={onKeyDown}
          disabled={disabled}
          onCountryChange={onCountryChange}
          countries={countries}
          international={international}
          convertToNational={convertToNational}
          selectMaxItems={selectMaxItems}
          className={className}
          inputClassName={errorInput}
          metadata={metadata}
          fancy={fancy}
        />
        {infoMessage &&
{infoMessage}
}
        {errorMessage &&
{errorMessage}
}

    );
  }
}
<|endoftext|>


<commit_before>import Aurora from "./Aurora";
import AuroraCmd from "./AuroraCmd";
import AuroraCmdTransformReadPacket from "./AuroraCmdTransformReadPacket.jsx";
import _ from "lodash";

export default class AuroraCmdReadFile extends AuroraCmd {

    static defaultOptions = {

        respTimeout: 180000,
        respTypeSuccess: AuroraCmd.RespTypes.STRING,
        packetMode: true,
        packetSize: 128
    };

    constructor(srcPath, options) {

        options =  _.defaultsDeep(options, AuroraCmdReadFile.defaultOptions);

        let srcPathSegments = srcPath.split('/');

        let args = [
            srcPathSegments.pop(),
            srcPathSegments.length ? srcPathSegments.join('/') : '/',
            options.packetMode ? options.packetSize : 0
        ];

        super('sd-file-read', args, options);

        this.srcPath = srcPath;
    }

    exec(){

        //disable packet mode for old aurora firmware
        //TODO: remove once stable
        if (Aurora.firmwareInfo.version.number < 900){
            this.args[2] = false;            //TODO: don't store args like this...
            this.options.packetMode = false; //for this very reason
        }

        super.exec();
    }

    _setupRespSuccess() {

        super._setupRespSuccess();

        if (this.options.packetMode && this.options.packetSize) {

            this.respSuccessStreamBack = this.respSuccessStreamFront;
            this.respSuccessStreamFront = new AuroraCmdTransformReadPacket(this);
            this.respSuccessStreamFront.pipe(this.respSuccessStreamBack);
        }

    }
}
<commit_msg>Fixed wrong file extension in import statement.<commit_after>import Aurora from "./Aurora";
import AuroraCmd from "./AuroraCmd";
import AuroraCmdTransformReadPacket from "./AuroraCmdTransformReadPacket";
import _ from "lodash";

export default class AuroraCmdReadFile extends AuroraCmd {

    static defaultOptions = {

        respTimeout: 180000,
        respTypeSuccess: AuroraCmd.RespTypes.STRING,
        packetMode: true,
        packetSize: 128
    };

    constructor(srcPath, options) {

        options =  _.defaultsDeep(options, AuroraCmdReadFile.defaultOptions);

        let srcPathSegments = srcPath.split('/');

        let args = [
            srcPathSegments.pop(),
            srcPathSegments.length ? srcPathSegments.join('/') : '/',
            options.packetMode ? options.packetSize : 0
        ];

        super('sd-file-read', args, options);

        this.srcPath = srcPath;
    }

    exec(){

        //disable packet mode for old aurora firmware
        //TODO: remove once stable
        if (Aurora.firmwareInfo.version.number < 900){
            this.args[2] = false;            //TODO: don't store args like this...
            this.options.packetMode = false; //for this very reason
        }

        super.exec();
    }

    _setupRespSuccess() {

        super._setupRespSuccess();

        if (this.options.packetMode && this.options.packetSize) {

            this.respSuccessStreamBack = this.respSuccessStreamFront;
            this.respSuccessStreamFront = new AuroraCmdTransformReadPacket(this);
            this.respSuccessStreamFront.pipe(this.respSuccessStreamBack);
        }

    }
}
<|endoftext|>


<commit_before>'use strict';

var chai = require('chai'),
    vain = require('./../lib/vain'),
    fs = require('fs');

chai.should();

describe('Vain', function() {
  it('should export the correct properties', function() {
    vain.registerSnippet.should.be.a('function');
    vain.unregisterSnippet.should.be.a('function');
    vain.render.should.be.a('function');
    vain.renderFile.should.be.a('function');
    vain.__express.should.be.a('function');
  });

  describe('.render', function() {
    it('should return the same markup if no snippets were invoked', function(callback) {
      var startMarkup = 'Google A search engine.'

      vain.render(startMarkup, function(error, output) {
        output.should.equal(startMarkup);
        callback();
      });
    });

    it('should pass in the entire matching node to a snippet', function(callback) {
      var startMarkup = 'Google A search engine.',
          expectedMarkup = 'Google A search engine.',
          snippetHandler = function($, element, request, response, params, finished) {
            var passedInMarkup = $("
").append(element).html();

            passedInMarkup.should.equal(expectedMarkup);
            callback();
            finished();
          }

      vain.render(startMarkup, {snippets: {'test': snippetHandler}}, function(error, output) {
      });
    });

    it('should return transformed node markup', function(callback) {
      var startMarkup = 'Google',
          expectedMarkup = 'Google',
          snippetHandler = function($, element, request, response, params, finished) {
            $(element).addClass("fancy");
            finished();
          }

      vain.render(startMarkup, {snippets: {'test': snippetHandler}}, function(error, output) {
        output.should.equal(expectedMarkup);
        callback();
      });
    });

    it('should correctly transform nested markup', function(callback) {
      var startMarkup = '

I like robots.

I really enjoy robots.

',
          expectedMarkup = '

I like robots.

I really enjoy robots.

';

      var parentHandler = function($, element, request, response, params, finished) {
        $(element)
          .addClass("cheese")
          .find("h1")
            .addClass("pig")
            .end()
          .find("p")
            .addClass("sauce");

        finished();
      }

      var childHandler = function($, element, request, response, params, finished) {
        $(element).addClass("enjoy");
        finished();
      }

      vain.render(startMarkup, {snippets: {
        'parent': parentHandler,
        'child': childHandler
      }}, function(error, output) {
        output.should.equal(expectedMarkup);
        callback();
      });
    });

    it('should run snippets from the global snippet registry', function(callback) {
      var startMarkup = 'Google',
          expectedMarkup = 'Google',
          snippetHandler = function($, element, request, response, params, finished) {
            $(element).addClass("fancy");
            finished();
          };

      vain.registerSnippet("globally-registered", snippetHandler);

      vain.render(startMarkup, function(error, output) {
        output.should.equal(expectedMarkup);
        callback();
      });
    });

    it('should pass params into snippets', function(callback) {
      var startMarkup = 'Google A search engine.',
          snippetHandler = function($, element, req, res, params) {
            params.one.should.equal("1");
            params.two.should.equal("2");
            callback();
          };

      vain.render(startMarkup, {snippets: {'test': snippetHandler}}, function() {});
    });
  });

  describe(".renderFile", function() {
    it("should correctly render a full HTML file on the file system", function(callback) {
      var inputFilePath = './test/examples/render-input.html',
          expectedOuput = fs.readFileSync('./test/examples/render-output.html', 'utf8');

      vain.registerSnippet('page-title', function($, element, request, response, params, finished) {
        $(element).text("Welcome to vain");
        finished();
      });

      vain.registerSnippet("page-header", function($, element, request, response, params, finished) {
        $(element).text("Welcome to vain");
        finished();
      });

      vain.registerSnippet("page-content", function($, element, request, response, params, finished) {
        $(element)
          .attr("id", "content")
          .text("Welcome to vain, a view first templating engine / middleware for Node.");

        finished();
      });

      vain.renderFile(inputFilePath, function(error, output) {
        output.should.equal(expectedOuput);
        callback();
      });
    });
  });

  describe(".router", function() {
    it("should dispatch for valid paths", function(done) {
      var testPath = '/render-input',
          router = vain.router("./test/examples"),
          res = {
            render: function(val) {
              val.should.equal('./test/examples/render-input.html');
              done();
            },

            send: function(code) {
              done("Template was not found.");
            }
          };

      router.handle({ url: testPath, method: 'GET', path: testPath}, res);
    });

    it("should return a 404 for invalid paths", function(done) {
      var testPath = '/nonexistent-path',
          router = vain.router("./test/examples"),
          res = {
            render: function(val) {
              done("Render was invoked.");
            },

            send: function(code) {
              code.should.equal(404);
              done();
            }
          };

      router.handle({ url: testPath, method: 'GET', path: testPath}, res);
    });
  });
});
<commit_msg>Update tests for new snippet argument list.<commit_after>'use strict';

var chai = require('chai'),
    vain = require('./../lib/vain'),
    fs = require('fs');

chai.should();

describe('Vain', function() {
  it('should export the correct properties', function() {
    vain.registerSnippet.should.be.a('function');
    vain.unregisterSnippet.should.be.a('function');
    vain.render.should.be.a('function');
    vain.renderFile.should.be.a('function');
    vain.__express.should.be.a('function');
  });

  describe('.render', function() {
    it('should return the same markup if no snippets were invoked', function(callback) {
      var startMarkup = 'Google A search engine.'

      vain.render(startMarkup, function(error, output) {
        output.should.equal(startMarkup);
        callback();
      });
    });

    it('should pass in the entire matching node to a snippet', function(callback) {
      var startMarkup = 'Google A search engine.',
          expectedMarkup = 'Google A search engine.',
          snippetHandler = function($, params, finished) {
            var passedInMarkup = $("
").append(this).html();

            passedInMarkup.should.equal(expectedMarkup);
            callback();
            finished();
          }

      vain.render(startMarkup, {snippets: {'test': snippetHandler}}, function(error, output) {
      });
    });

    it('should return transformed node markup', function(callback) {
      var startMarkup = 'Google',
          expectedMarkup = 'Google',
          snippetHandler = function($, params, finished) {
            $(this).addClass("fancy");
            finished();
          }

      vain.render(startMarkup, {snippets: {'test': snippetHandler}}, function(error, output) {
        output.should.equal(expectedMarkup);
        callback();
      });
    });

    it('should correctly transform nested markup', function(callback) {
      var startMarkup = '

I like robots.

I really enjoy robots.

',
          expectedMarkup = '

I like robots.

I really enjoy robots.

';

      var parentHandler = function($, params, finished) {
        $(this)
          .addClass("cheese")
          .find("h1")
            .addClass("pig")
            .end()
          .find("p")
            .addClass("sauce");

        finished();
      }

      var childHandler = function($, params, finished) {
        $(this).addClass("enjoy");
        finished();
      }

      vain.render(startMarkup, {snippets: {
        'parent': parentHandler,
        'child': childHandler
      }}, function(error, output) {
        output.should.equal(expectedMarkup);
        callback();
      });
    });

    it('should run snippets from the global snippet registry', function(callback) {
      var startMarkup = 'Google',
          expectedMarkup = 'Google',
          snippetHandler = function($, params, finished) {
            $(this).addClass("fancy");
            finished();
          };

      vain.registerSnippet("globally-registered", snippetHandler);

      vain.render(startMarkup, function(error, output) {
        output.should.equal(expectedMarkup);
        callback();
      });
    });

    it('should pass params into snippets', function(callback) {
      var startMarkup = 'Google A search engine.',
          snippetHandler = function($, params, finished) {
            params.one.should.equal("1");
            params.two.should.equal("2");
            callback();
            finished();
          };

      vain.render(startMarkup, {snippets: {'test': snippetHandler}}, function() {});
    });
  });

  describe(".renderFile", function() {
    it("should correctly render a full HTML file on the file system", function(callback) {
      var inputFilePath = './test/examples/render-input.html',
          expectedOuput = fs.readFileSync('./test/examples/render-output.html', 'utf8');

      vain.registerSnippet('page-title', function($, params, finished) {
        $(this).text("Welcome to vain");
        finished();
      });

      vain.registerSnippet("page-header", function($, params, finished) {
        $(this).text("Welcome to vain");
        finished();
      });

      vain.registerSnippet("page-content", function($, params, finished) {
        $(this)
          .attr("id", "content")
          .text("Welcome to vain, a view first templating engine / middleware for Node.");

        finished();
      });

      vain.renderFile(inputFilePath, function(error, output) {
        output.should.equal(expectedOuput);
        callback();
      });
    });
  });

  describe(".router", function() {
    it("should dispatch for valid paths", function(done) {
      var testPath = '/render-input',
          router = vain.router("./test/examples"),
          res = {
            render: function(val) {
              val.should.equal('./test/examples/render-input.html');
              done();
            },

            send: function(code) {
              done("Template was not found.");
            }
          };

      router.handle({ url: testPath, method: 'GET', path: testPath}, res);
    });

    it("should return a 404 for invalid paths", function(done) {
      var testPath = '/nonexistent-path',
          router = vain.router("./test/examples"),
          res = {
            render: function(val) {
              done("Render was invoked.");
            },

            send: function(code) {
              code.should.equal(404);
              done();
            }
          };

      router.handle({ url: testPath, method: 'GET', path: testPath}, res);
    });
  });
});
<|endoftext|>


<commit_before>/* jshint -W085 */
// DEV: The above directive ignores `with` errors
// Load in dependencies
var expect = require('chai').expect;
var scriptUtils = require('./utils/script');

// Run our tests
describe('ecma-variable-scope', function () {
  describe('marking up an AST with a var inside referenced through a `with`', function () {
    scriptUtils.interpretFnAst(function () {
      var hello = 'world';
      var obj = {hello: 'moon'};
      with (obj) {
        console.log(hello);
      }
    });

    it('marks the initialization without a `with`', function () {
      // {Program} (ast) -> {var} (body[0]) -> hello (declarations[0].id)
      var identifier = this.ast.body[0].declarations[0].id;
      expect(identifier.scopeInfo).to.have.property('insideWith', false);
    });

    it('marks the inner `with` variable as with a `with`', function () {
      // {Program} (ast) -> {with content} (body[2].body.body) -> `hello` ([0].expression['arguments'][0])
      var identifier = this.ast.body[2].body.body[0].expression['arguments'][0];
      expect(identifier.scopeInfo).to.have.property('insideWith', true);
    });

    it('marks the outer variable with a `usedInAWith`', function () {
      var identifier = this.ast.body[0].declarations[0].id;
      expect(identifier.scopeInfo).to.have.property('usedInAWith', true);
    });

    it('marks the inner `with` variable with a `usedInAWith`', function () {
      var identifier = this.ast.body[2].body.body[0].expression['arguments'][0];
      expect(identifier.scopeInfo).to.have.property('usedInAWith', true);
    });

    it.only('marks the `with` object without a `usedInAWith`', function () {
      // {Program} (ast) -> {with} (body[2]) -> obj (object)
      var identifier = this.ast.body[2].object;
      expect(identifier.scopeInfo).to.have.property('usedInAWith', false);    });
  });
});
<commit_msg>Added another test for insideWith<commit_after>/* jshint -W085 */
// DEV: The above directive ignores `with` errors
// Load in dependencies
var expect = require('chai').expect;
var scriptUtils = require('./utils/script');

// Run our tests
describe('ecma-variable-scope', function () {
  describe('marking up an AST with a var inside referenced through a `with`', function () {
    scriptUtils.interpretFnAst(function () {
      var hello = 'world';
      var obj = {hello: 'moon'};
      with (obj) {
        console.log(hello);
      }
    });

    it('marks the initialization without a `with`', function () {
      // {Program} (ast) -> {var} (body[0]) -> hello (declarations[0].id)
      var identifier = this.ast.body[0].declarations[0].id;
      expect(identifier.scopeInfo).to.have.property('insideWith', false);
    });

    it('marks the inner `with` variable as with a `with`', function () {
      // {Program} (ast) -> {with content} (body[2].body.body) -> `hello` ([0].expression['arguments'][0])
      var identifier = this.ast.body[2].body.body[0].expression['arguments'][0];
      expect(identifier.scopeInfo).to.have.property('insideWith', true);
    });

    it.only('marks the `with` object as not used in a `with`', function () {
      // {Program} (ast) -> {with} (body[2]) -> obj (object)
      var identifier = this.ast.body[2].object;
      expect(identifier.scopeInfo).to.have.property('insideWith', false);
    });

    it('marks the outer variable with a `usedInAWith`', function () {
      var identifier = this.ast.body[0].declarations[0].id;
      expect(identifier.scopeInfo).to.have.property('usedInAWith', true);
    });

    it('marks the inner `with` variable with a `usedInAWith`', function () {
      var identifier = this.ast.body[2].body.body[0].expression['arguments'][0];
      expect(identifier.scopeInfo).to.have.property('usedInAWith', true);
    });

    it('marks the `with` object without a `usedInAWith`', function () {
      // {Program} (ast) -> {with} (body[2]) -> obj (object)
      var identifier = this.ast.body[2].object;
      expect(identifier.scopeInfo).to.have.property('usedInAWith', false);    });
  });
});
<|endoftext|>


<commit_before>// @flow
// Imports go here.
import React from 'react'
import { omit } from 'lodash'
import { StyleSheet, css } from 'aphrodite-jss'

// Define the props and types our app needs.
type Props = {
  animationType?: string | false,
  background?: string,
  color?: string,
  fontSize?: string,
  fontStyle?: string,
  opacity?: string,
  style?: Object,
  children: Array<React.createElement>,
  border?: string,
  cursor?: string,
  sizes?: string,
};

// Create stylesheet of sizes.
const sizes: Object = {
  s: () => ({ padding: '1px 3px', fontSize: '12px' }),
  l: () => ({ padding: '5px 7px', fontSize: '18px' }),
  xl: () => ({ padding: '9px 11px', fontSize: '20px' }),
  xxl: () => ({ padding: '12px 14px', fontSize: '25px' }),
  default: () => ({ padding: '5px 4px', fontSize: '16px' })
}

// Finally.. our component.
export default function RSGButton (props: Props): React.createElement {
  let sizeId: string
  if (props.sizes) sizeId = props.sizes
  else { sizeId = 'default' }
  let padding = '5px 4px'
  if (sizeId !== 'default') padding = sizes[sizeId]().padding

  const animationStyling = {
    elevated: {
      '&:hover': {
        boxShadow: '2px 2px 2px #888888'
      },
      '&:active': {
        position: 'relative',
        top: '1px',
        left: '1px',
        boxShadow: 'none'
      }
    },
    ripple: {
      position: 'relative',
      overflow: 'hidden',
      transform: 'translate3d(0, 0, 0)',
      '&:after': {
        content: '""',
        display: 'block',
        position: 'absolute',
        width: '100%',
        height: '100%',
        top: 0,
        left: 0,
        pointerEvents: 'none',
        backgroundImage: 'radial-gradient(circle, #FFF 10%, transparent 10%)',
        backgroundRepeat: 'no-repeat',
        backgroundPosition: '50%',
        transform: 'scale(10,10)',
        opacity: '0',
        transition: 'transform .5s, opacity 1s'
      },
      '&:active:after': {
        transform: 'scale(0,0)',
        opacity: '.2',
        transition: '0s'
      }
    }
  }

  const styles: Object = StyleSheet.create((() => {
    let extraAnimationStyling = {}
    if (props.animationType) {
      extraAnimationStyling = animationStyling[props.animationType]
    }
    return {
      mainButton: {
        border: props.border,
        borderRadius: '3px',
        background: props.background,
        color: props.color,
        opacity: props.opacity,
        padding,
        ...props.style,
        fontSize: props.fontSize ? props.fontSize : sizes[sizeId]().fontSize,
        fontStyle: props.fontStyle,
        ...extraAnimationStyling,
        cursor: props.cursor
      }
    }
  })())

  const exclude = ['style', 'background', 'color', 'fontSize', 'fontStyle', 'opacity', 'sizes', 'animationType', 'cursor', 'border']

  return (
    <button className={css(styles.mainButton)}{...omit(props, exclude)} >
      { props.children }
    </button>
  )
}

// and sir default props.
RSGButton.defaultProps = {
  background: 'rgb(50, 120, 180)',
  color: 'white',
  opacity: '1',
  style: {},
  fontSize: undefined,
  fontStyle: undefined,
  sizes: 'default',
  cursor: 'default',
  border: '1px solid rgb(30, 100, 160)',
  animationType: false
}
<commit_msg>Removed the arrow functions from the Object `sizes`<commit_after>// @flow
// Imports go here.
import React from 'react'
import { omit } from 'lodash'
import { StyleSheet, css } from 'aphrodite-jss'

// Define the props and types our app needs.
type Props = {
  animationType?: string | false,
  background?: string,
  color?: string,
  fontSize?: string,
  fontStyle?: string,
  opacity?: string,
  style?: Object,
  children: Array<React.createElement>,
  border?: string,
  cursor?: string,
  sizes?: string,
};

// Create stylesheet of sizes.
const sizes: Object = {
  s: { padding: '1px 3px', fontSize: '12px' },
  l: { padding: '5px 7px', fontSize: '18px' },
  xl: { padding: '9px 11px', fontSize: '20px' },
  xxl: { padding: '12px 14px', fontSize: '25px' },
  default: { padding: '5px 4px', fontSize: '16px' }
}

// Finally.. our component.
export default function RSGButton (props: Props): React.createElement {
  let sizeId: string
  if (props.sizes) sizeId = props.sizes
  else { sizeId = 'default' }
  let padding = '5px 4px'
  if (sizeId !== 'default') padding = sizes[sizeId]().padding

  const animationStyling = {
    elevated: {
      '&:hover': {
        boxShadow: '2px 2px 2px #888888'
      },
      '&:active': {
        position: 'relative',
        top: '1px',
        left: '1px',
        boxShadow: 'none'
      }
    },
    ripple: {
      position: 'relative',
      overflow: 'hidden',
      transform: 'translate3d(0, 0, 0)',
      '&:after': {
        content: '""',
        display: 'block',
        position: 'absolute',
        width: '100%',
        height: '100%',
        top: 0,
        left: 0,
        pointerEvents: 'none',
        backgroundImage: 'radial-gradient(circle, #FFF 10%, transparent 10%)',
        backgroundRepeat: 'no-repeat',
        backgroundPosition: '50%',
        transform: 'scale(10,10)',
        opacity: '0',
        transition: 'transform .5s, opacity 1s'
      },
      '&:active:after': {
        transform: 'scale(0,0)',
        opacity: '.2',
        transition: '0s'
      }
    }
  }

  const styles: Object = StyleSheet.create((() => {
    let extraAnimationStyling = {}
    if (props.animationType) {
      extraAnimationStyling = animationStyling[props.animationType]
    }
    return {
      mainButton: {
        border: props.border,
        borderRadius: '3px',
        background: props.background,
        color: props.color,
        opacity: props.opacity,
        padding,
        ...props.style,
        fontSize: props.fontSize ? props.fontSize : sizes[sizeId]().fontSize,
        fontStyle: props.fontStyle,
        ...extraAnimationStyling,
        cursor: props.cursor
      }
    }
  })())

  const exclude = ['style', 'background', 'color', 'fontSize', 'fontStyle', 'opacity', 'sizes', 'animationType', 'cursor', 'border']

  return (
    <button className={css(styles.mainButton)}{...omit(props, exclude)} >
      { props.children }
    </button>
  )
}

// and sir default props.
RSGButton.defaultProps = {
  background: 'rgb(50, 120, 180)',
  color: 'white',
  opacity: '1',
  style: {},
  fontSize: undefined,
  fontStyle: undefined,
  sizes: 'default',
  cursor: 'default',
  border: '1px solid rgb(30, 100, 160)',
  animationType: false
}
<|endoftext|>


<commit_before>"use strict";
module.exports = function(Promise, INTERNAL) {
    var errors = require("./errors.js");
    var async = require("./async.js");
    var ASSERT = require("./assert.js");
    var CancellationError = errors.CancellationError;
    var SYNC_TOKEN = {};

    Promise.prototype._cancel = function Promise$_cancel(reason) {
        if (!this.isCancellable()) return this;
        var parent;
        //Propagate to the last cancellable parent
        if ((parent = this._cancellationParent) !== void 0) {
            parent.cancel(reason, SYNC_TOKEN);
            return;
        }

        if (reason === undefined) {
            reason = new CancellationError();
            this._attachExtraTrace(reason);
        }
        this._rejectUnchecked(reason);
    };

    Promise.prototype.cancel = function Promise$cancel(reason, token) {
        if (!this.isCancellable()) return this;
        ASSERT("_cancellationParent" in this);

        if (reason === undefined) {
            var err = new CancellationError();
            this._attachExtraTrace(err);
        }

        if (token === SYNC_TOKEN) {
            this._cancel(reason);
            return this;
        }

        async.invokeLater(this._cancel, this, reason);
        return this;
    };

    Promise.prototype.cancellable = function Promise$cancellable() {
        if (this._cancellable()) return this;
        this._setCancellable();
        this._cancellationParent = void 0;
        return this;
    };

    Promise.prototype.uncancellable = function Promise$uncancellable() {
        var ret = new Promise(INTERNAL);
        ret._setTrace(this.uncancellable, this);
        ret._follow(this);
        ret._unsetCancellable();
        if (this._isBound()) ret._setBoundTo(this._boundTo);
        return ret;
    };

    Promise.prototype.fork =
    function Promise$fork(didFulfill, didReject, didProgress) {
        var ret = this._then(didFulfill, didReject, didProgress,
            void 0, void 0, this.fork);

        ret._setCancellable();
        ret._cancellationParent = void 0;
        return ret;
    };
};
<commit_msg>Fixing error pass-through on cancel<commit_after>"use strict";
module.exports = function(Promise, INTERNAL) {
    var errors = require("./errors.js");
    var async = require("./async.js");
    var ASSERT = require("./assert.js");
    var CancellationError = errors.CancellationError;
    var SYNC_TOKEN = {};

    Promise.prototype._cancel = function Promise$_cancel(reason) {
        if (!this.isCancellable()) return this;
        var parent;
        //Propagate to the last cancellable parent
        if ((parent = this._cancellationParent) !== void 0) {
            parent.cancel(reason, SYNC_TOKEN);
            return;
        }

        if (reason === undefined) {
            reason = new CancellationError();
            this._attachExtraTrace(reason);
        }
        this._rejectUnchecked(reason);
    };

    Promise.prototype.cancel = function Promise$cancel(reason, token) {
        if (!this.isCancellable()) return this;
        ASSERT("_cancellationParent" in this);

        if (reason === undefined) {
            reason = new CancellationError();
            this._attachExtraTrace(reason);
        }

        if (token === SYNC_TOKEN) {
            this._cancel(reason);
            return this;
        }

        async.invokeLater(this._cancel, this, reason);
        return this;
    };

    Promise.prototype.cancellable = function Promise$cancellable() {
        if (this._cancellable()) return this;
        this._setCancellable();
        this._cancellationParent = void 0;
        return this;
    };

    Promise.prototype.uncancellable = function Promise$uncancellable() {
        var ret = new Promise(INTERNAL);
        ret._setTrace(this.uncancellable, this);
        ret._follow(this);
        ret._unsetCancellable();
        if (this._isBound()) ret._setBoundTo(this._boundTo);
        return ret;
    };

    Promise.prototype.fork =
    function Promise$fork(didFulfill, didReject, didProgress) {
        var ret = this._then(didFulfill, didReject, didProgress,
            void 0, void 0, this.fork);

        ret._setCancellable();
        ret._cancellationParent = void 0;
        return ret;
    };
};
<|endoftext|>