<commit_before>define([
	'dojo/_base/lang',
	'dojo/_base/array',
	'dojo/aspect',
	'dojo/has',
	'dojo/when',
	'dojo/Deferred',
	'dojo/_base/declare',
	'./Model',
	'dojo/Evented'
], function(lang, arrayUtil, aspect, has, when, Deferred, declare, Model, Evented){

// module:
//		dstore/Store
// detect __proto__
has.add('object-proto', !!{}.__proto__);
var hasProto = has('object-proto');
return declare(Evented, {
	constructor: function(options){
		// perform the mixin
		declare.safeMixin(this, options);
		if (!this.hasOwnProperty('model')) {
			// we need a distinct model for each store, so we can
			// save the reference back to this store on it
			this.model = declare(Model, {});
		}
		// give a reference back to the store for saving, etc.
		this.model.prototype._store = this;
	},
	map: function(callback, thisObject){
		var results = [];
		// like forEach, except we collect results
		return when(this.forEach(function(object){
			results.push(callback.call(thisObject, object));
		}, thisObject), function(){
			return results;
		});
	},
	forEach: function(callback, thisObject){
		return when(this.fetch(), function(data){
			for(var i = 0, l = data.length; i < l; i++){
				callback.call(thisObject, data[i]);
			}
			return data;
		});
	},
	on: function(type, listener){
		//	summary:
		//		Listen for data changes
		if (type !== 'refresh' && this.store && this.store !== this){
			return this.store.on(type, listener);
		}
		return this.inherited(arguments);
	},
	emit: function(type, event){
		event = event || {};
		event.type = type;
		return this.inherited(arguments);
	},

	// parse: Function
	//		One can provide a parsing function that will permit the parsing of the data. By
	//		default we assume the provide data is a simple JavaScript array that requires
	//		no parsing
	parse: null,

	// model: Function
	//		This should be a entity (like a class/constructor) with a 'prototype' property that will be
	//		used as the prototype for all objects returned from this store. One can set this
	//		to an empty object if you don't want any methods to decorate the returned
	//		objects (this can improve performance by avoiding prototype setting)
	model: Model,

	assignPrototype: function(object){
		// Set the object's prototype
		var model = this.model;
		if(model && object){
			var prototype = model.prototype;
			if(hasProto){
				// the fast easy way
				// http://jsperf.com/setting-the-prototype
				object.__proto__ = prototype;
			}else{
				// create a new object with the correct prototype
				object = lang.delegate(prototype, object);
			}
		}
		return object;
	},

	create: function(properties){
		return new this.model(properties);
	},

	_createSubCollection: function(kwArgs){
		var store = this.store || this,
			newCollection = lang.delegate(store.constructor.prototype, lang.mixin({ store: store }, this));

		delete newCollection.data;
		delete newCollection.total;

		return lang.mixin(newCollection, kwArgs);
	},

	filter: function(filter){
		return this._createSubCollection({
			filtered: (this.filtered || []).concat(filter)
		});
	},

	sort: function(property, descending){
		var sorted;

		if(typeof property === 'function'){
			sorted = property;
		}else if(lang.isArray(property)){
			sorted = property.slice(0);
		}else if(typeof property === 'object'){
			sorted = [].slice.call(arguments, 0);
		}else{
			sorted = [{
				property: property,
				descending: !!descending
			}];
		}

		return this._createSubCollection({ sorted: sorted });
	},

	range: function(start, end){
		return this._createSubCollection({
			ranged: { start: start, end: end }
		});
	}
});
});
<commit_msg>Include index for forEach and map<commit_after>define([
	'dojo/_base/lang',
	'dojo/_base/array',
	'dojo/aspect',
	'dojo/has',
	'dojo/when',
	'dojo/Deferred',
	'dojo/_base/declare',
	'./Model',
	'dojo/Evented'
], function(lang, arrayUtil, aspect, has, when, Deferred, declare, Model, Evented){

// module:
//		dstore/Store
// detect __proto__
has.add('object-proto', !!{}.__proto__);
var hasProto = has('object-proto');
return declare(Evented, {
	constructor: function(options){
		// perform the mixin
		declare.safeMixin(this, options);
		if (!this.hasOwnProperty('model')) {
			// we need a distinct model for each store, so we can
			// save the reference back to this store on it
			this.model = declare(Model, {});
		}
		// give a reference back to the store for saving, etc.
		this.model.prototype._store = this;
	},
	map: function(callback, thisObject){
		var results = [];
		// like forEach, except we collect results
		return when(this.forEach(function(object, i){
			results.push(callback.call(thisObject, object, i));
		}, thisObject), function(){
			return results;
		});
	},
	forEach: function(callback, thisObject){
		return when(this.fetch(), function(data){
			for(var i = 0, l = data.length; i < l; i++){
				callback.call(thisObject, data[i], i);
			}
			return data;
		});
	},
	on: function(type, listener){
		//	summary:
		//		Listen for data changes
		if (type !== 'refresh' && this.store && this.store !== this){
			return this.store.on(type, listener);
		}
		return this.inherited(arguments);
	},
	emit: function(type, event){
		event = event || {};
		event.type = type;
		return this.inherited(arguments);
	},

	// parse: Function
	//		One can provide a parsing function that will permit the parsing of the data. By
	//		default we assume the provide data is a simple JavaScript array that requires
	//		no parsing
	parse: null,

	// model: Function
	//		This should be a entity (like a class/constructor) with a 'prototype' property that will be
	//		used as the prototype for all objects returned from this store. One can set this
	//		to an empty object if you don't want any methods to decorate the returned
	//		objects (this can improve performance by avoiding prototype setting)
	model: Model,

	assignPrototype: function(object){
		// Set the object's prototype
		var model = this.model;
		if(model && object){
			var prototype = model.prototype;
			if(hasProto){
				// the fast easy way
				// http://jsperf.com/setting-the-prototype
				object.__proto__ = prototype;
			}else{
				// create a new object with the correct prototype
				object = lang.delegate(prototype, object);
			}
		}
		return object;
	},

	create: function(properties){
		return new this.model(properties);
	},

	_createSubCollection: function(kwArgs){
		var store = this.store || this,
			newCollection = lang.delegate(store.constructor.prototype, lang.mixin({ store: store }, this));

		delete newCollection.data;
		delete newCollection.total;

		return lang.mixin(newCollection, kwArgs);
	},

	filter: function(filter){
		return this._createSubCollection({
			filtered: (this.filtered || []).concat(filter)
		});
	},

	sort: function(property, descending){
		var sorted;

		if(typeof property === 'function'){
			sorted = property;
		}else if(lang.isArray(property)){
			sorted = property.slice(0);
		}else if(typeof property === 'object'){
			sorted = [].slice.call(arguments, 0);
		}else{
			sorted = [{
				property: property,
				descending: !!descending
			}];
		}

		return this._createSubCollection({ sorted: sorted });
	},

	range: function(start, end){
		return this._createSubCollection({
			ranged: { start: start, end: end }
		});
	}
});
});
<|endoftext|>


<commit_before>define(["dcl/dcl", "dojo/_base/lang", "dojo/when", "./Invalidating"], function (dcl, lang, when, Invalidating) {

	var isStoreInvalidated = function (props) {
		return props.store || props.query || props.queryOptions;
	};

	var setStoreValidate = function (props) {
		props.store = props.query = props.queryOptions = false;
	};

	return dcl(Invalidating, {

		// summary:
		//		Mixin for widgets for store management that creates widget render items from store items after
		//		querying the store. The receiving class must extend dojo/Stateful and dojo/Evented or
		//		delite/Widget.
		// description:
		//		Classes extending this mixin automatically create render items that are consumable by the widget
		//		from store items after querying the store. This happens each time the widget store, query or
		//		queryOptions properties are set. If that store is Observable it will be observed and render items
		//		will be automatically updated, added or deleted from the items property based on store notifications.

		// store: dojo/store/Store
		//		The store that contains the items to display.
		store: null,

		// query: Object
		//		A query that can be passed to when querying the store.
		query: {},

		// queryOptions: dojo/store/api/Store.QueryOptions?
		//		Options to be applied when querying the store.
		queryOptions: null,

		// renderItems: Array
		//		The render items corresponding to the store items for this widget. This is filled from the store and
		//		is not supposed to be modified directly. Initially null.
		renderItems: null,

		preCreate: function () {
			// we want to be able to wait for potentially several of those properties to be set before
			// actually firing the store request
			this.addInvalidatingProperties({
					"store": "invalidateProperty",
					"query": "invalidateProperty",
					"queryOptions": "invalidateProperty"
				}
			);
		},

		renderItemToItem: function (/*Object*/ renderItem) {
			// summary:
			//		Creates a store item from the render item. By default it returns the render item itself.
			// renderItem: Object
			//		The render item.
			// returns: Object
			return renderItem;
		},

		itemToRenderItem: function (item) {
			// summary:
			//		Returns the render item for a given store item. By default it returns the store item itself.
			// item: Object
			//		The store item.
			// tags:
			//		protected
			return item;
		},

		initItems: function (renderItems) {
			// summary:
			//		This method is called once the query has been executed to initial the renderItems array
			//		with the list of initial render items.
			// description:
			//		This method sets the renderItems property to the render items array passed as parameter. Once
			//		done, it fires a 'query-success' event.
			// renderItems:
			//		The array of initial render items to be set in the renderItems property.
			// tags:
			//		protected
			this.renderItems = renderItems;
			this.emit("query-success", { renderItems: renderItems, cancelable: false, bubbles: true });
		},

		refreshProperties: function (props) {
			// summary:
			//		Query the store, create the render items and call initItems() when ready. If an error occurs
			//		a 'query-error' event will be fired.
			// tags:
			//		protected
			if (isStoreInvalidated(props)) {
				if (this._observeHandler) {
					this._observeHandler.remove();
					this._observeHandler = null;
				}
				setStoreValidate(props);
				var store = this.store;
				if (store != null) {
					var results = store.query(this.query, this.queryOptions);
					if (results.observe) {
						// user asked us to observe the store
						this._observeHandler = results.observe(lang.hitch(this, "_updateItem"), true);
					}
					// if we have a mapping function between store item and some intermediary items use it
					results = results.map(lang.hitch(this, function (item) {
						return this.itemToRenderItem(item);
					}));
					when(results, lang.hitch(this, this.initItems), lang.hitch(this, "_queryError"));
				} else {
					this.initItems([]);
				}
			}
		},

		_queryError: function (error) {
			this.emit("query-error", { error: error, cancelable: false, bubbles: true });
		},

		_updateItem: function (object, previousIndex, newIndex) {
			// tags:
			//		private

			var items = this.renderItems;

			// if we have a mapping function between store item and some intermediary items use it
			var newItem = this.itemToRenderItem(object);

			if (previousIndex !== -1) {
				// this is a remove or a move
				if (newIndex !== previousIndex) {
					// remove
					this.removeItem(previousIndex, newItem, items);
				} else {
					// this is a put, previous and new index identical
					this.putItem(previousIndex, newItem, items);
				}
			} else if (newIndex !== -1) {
				// this is a add
				this.addItem(newIndex, newItem, items);

			}
			// set back the modified items property
			this.renderItems = items;
		},

		destroy: function () {
			if (this._observeHandler) {
				this._observeHandler.remove();
				this._observeHandler = null;
			}
		},

		removeItem: function (index, renderItem, renderItems) {
			// summary:
			//		When the store is observed and an item is removed in the store this method is called to remove the
			//		corresponding render item. This can be redefined but must not be called directly.
			// index: Number
			//		The index of the item to remove.
			// renderItem: Object
			//		The render item to be removed.
			// renderItems: Array
			//		The array of render items to remove the render item from.
			// tags:
			//		protected
			renderItems.splice(index, 1);
		},

		putItem: function (index,  renderItem, renderItems) {
			// summary:
			//		When the store is observed and an item is updated in the store this method is called to update the
			//		corresponding render item. This can be redefined but must not be called directly.
			// index: Number
			//		The index of the updated item.
			// renderItem: Object
			//		The render item data the render item must be updated with.
			// renderItems: Array
			//		The array of render items to render item to be updated is part of.
			// tags:
			//		protected

			// we want to keep the same item object and mixin new values into old object
			dcl.mix(renderItems[index], renderItem);
		},

		addItem: function (index, item, items) {
			// summary:
			//		When the store is observed and an item is added in the store this method is called to add the
			//		corresponding render item. This can be redefined but must not be called directly.
			// index: Number
			//		The index of the item to add.
			// renderItem: Object
			//		The render item to be added.
			// renderItems: Array
			//		The array of render items to add the render item to.
			// tags:
			//		protected
			items.splice(index, 0, item);
		}
	});
});
<commit_msg>fix discrepancy between the doc and the naming of the arguments<commit_after>define(["dcl/dcl", "dojo/_base/lang", "dojo/when", "./Invalidating"], function (dcl, lang, when, Invalidating) {

	var isStoreInvalidated = function (props) {
		return props.store || props.query || props.queryOptions;
	};

	var setStoreValidate = function (props) {
		props.store = props.query = props.queryOptions = false;
	};

	return dcl(Invalidating, {

		// summary:
		//		Mixin for widgets for store management that creates widget render items from store items after
		//		querying the store. The receiving class must extend dojo/Stateful and dojo/Evented or
		//		delite/Widget.
		// description:
		//		Classes extending this mixin automatically create render items that are consumable by the widget
		//		from store items after querying the store. This happens each time the widget store, query or
		//		queryOptions properties are set. If that store is Observable it will be observed and render items
		//		will be automatically updated, added or deleted from the items property based on store notifications.

		// store: dojo/store/Store
		//		The store that contains the items to display.
		store: null,

		// query: Object
		//		A query that can be passed to when querying the store.
		query: {},

		// queryOptions: dojo/store/api/Store.QueryOptions?
		//		Options to be applied when querying the store.
		queryOptions: null,

		// renderItems: Array
		//		The render items corresponding to the store items for this widget. This is filled from the store and
		//		is not supposed to be modified directly. Initially null.
		renderItems: null,

		preCreate: function () {
			// we want to be able to wait for potentially several of those properties to be set before
			// actually firing the store request
			this.addInvalidatingProperties({
					"store": "invalidateProperty",
					"query": "invalidateProperty",
					"queryOptions": "invalidateProperty"
				}
			);
		},

		renderItemToItem: function (/*Object*/ renderItem) {
			// summary:
			//		Creates a store item from the render item. By default it returns the render item itself.
			// renderItem: Object
			//		The render item.
			// returns: Object
			return renderItem;
		},

		itemToRenderItem: function (item) {
			// summary:
			//		Returns the render item for a given store item. By default it returns the store item itself.
			// item: Object
			//		The store item.
			// tags:
			//		protected
			return item;
		},

		initItems: function (renderItems) {
			// summary:
			//		This method is called once the query has been executed to initial the renderItems array
			//		with the list of initial render items.
			// description:
			//		This method sets the renderItems property to the render items array passed as parameter. Once
			//		done, it fires a 'query-success' event.
			// renderItems:
			//		The array of initial render items to be set in the renderItems property.
			// tags:
			//		protected
			this.renderItems = renderItems;
			this.emit("query-success", { renderItems: renderItems, cancelable: false, bubbles: true });
		},

		refreshProperties: function (props) {
			// summary:
			//		Query the store, create the render items and call initItems() when ready. If an error occurs
			//		a 'query-error' event will be fired.
			// tags:
			//		protected
			if (isStoreInvalidated(props)) {
				if (this._observeHandler) {
					this._observeHandler.remove();
					this._observeHandler = null;
				}
				setStoreValidate(props);
				var store = this.store;
				if (store != null) {
					var results = store.query(this.query, this.queryOptions);
					if (results.observe) {
						// user asked us to observe the store
						this._observeHandler = results.observe(lang.hitch(this, "_updateItem"), true);
					}
					// if we have a mapping function between store item and some intermediary items use it
					results = results.map(lang.hitch(this, function (item) {
						return this.itemToRenderItem(item);
					}));
					when(results, lang.hitch(this, this.initItems), lang.hitch(this, "_queryError"));
				} else {
					this.initItems([]);
				}
			}
		},

		_queryError: function (error) {
			this.emit("query-error", { error: error, cancelable: false, bubbles: true });
		},

		_updateItem: function (object, previousIndex, newIndex) {
			// tags:
			//		private

			var items = this.renderItems;

			// if we have a mapping function between store item and some intermediary items use it
			var newItem = this.itemToRenderItem(object);

			if (previousIndex !== -1) {
				// this is a remove or a move
				if (newIndex !== previousIndex) {
					// remove
					this.removeItem(previousIndex, newItem, items);
				} else {
					// this is a put, previous and new index identical
					this.putItem(previousIndex, newItem, items);
				}
			} else if (newIndex !== -1) {
				// this is a add
				this.addItem(newIndex, newItem, items);

			}
			// set back the modified items property
			this.renderItems = items;
		},

		destroy: function () {
			if (this._observeHandler) {
				this._observeHandler.remove();
				this._observeHandler = null;
			}
		},

		removeItem: function (index, renderItem, renderItems) {
			// summary:
			//		When the store is observed and an item is removed in the store this method is called to remove the
			//		corresponding render item. This can be redefined but must not be called directly.
			// index: Number
			//		The index of the item to remove.
			// renderItem: Object
			//		The render item to be removed.
			// renderItems: Array
			//		The array of render items to remove the render item from.
			// tags:
			//		protected
			renderItems.splice(index, 1);
		},

		putItem: function (index,  renderItem, renderItems) {
			// summary:
			//		When the store is observed and an item is updated in the store this method is called to update the
			//		corresponding render item. This can be redefined but must not be called directly.
			// index: Number
			//		The index of the updated item.
			// renderItem: Object
			//		The render item data the render item must be updated with.
			// renderItems: Array
			//		The array of render items to render item to be updated is part of.
			// tags:
			//		protected

			// we want to keep the same item object and mixin new values into old object
			dcl.mix(renderItems[index], renderItem);
		},

		addItem: function (index, renderItem, renderItems) {
			// summary:
			//		When the store is observed and an item is added in the store this method is called to add the
			//		corresponding render item. This can be redefined but must not be called directly.
			// index: Number
			//		The index of the item to add.
			// renderItem: Object
			//		The render item to be added.
			// renderItems: Array
			//		The array of render items to add the render item to.
			// tags:
			//		protected
			renderItems.splice(index, 0, renderItem);
		}
	});
});
<|endoftext|>


<commit_before>// @client-side

import ns from 'ima/namespace';
import WindowInterface from 'ima/window/Window';

ns.namespace('ima.window');

/**
 * Client-side implementation of the {@code ima.window.Window} utility API.
 *
 * @class ClientWindow
 * @implements ima.window.Window
 * @namespace ima.window
 * @module ima
 * @submodule ima.window
 */
export default class ClientWindow extends WindowInterface {

	/**
	 * @inheritdoc
	 * @method isClient
	 */
	isClient() {
		return true;
	}

	/**
	 * @inheritdoc
	 * @method isCookieEnabled
	 */
	isCookieEnabled() {
		return navigator.cookieEnabled;
	}

	/**
	 * @inheritdoc
	 * @method hasSessionStorage
	 */
	hasSessionStorage() {
		try {
			if (window.sessionStorage) {
				var sessionKey = 'IMA.jsTest';

				try {
					sessionStorage.setItem(sessionKey, 1);
					sessionStorage.removeItem(sessionKey);
				} catch (e) {
					return false;
				}

				return true;
			}
		} catch (e) {
			if ($Debug) {
				console.warn('Session Storage is not accessible!', e);
			}
			return false;
		}
		return false;
	}

	/**
	 * @inheritdoc
	 * @method hasWebSocket
	 */
	hasWebSocket() {
		return window.WebSocket;
	}

	/**
	 * @inheritdoc
	 * @method hasHistoryAPI
	 */
	hasHistoryAPI() {
		return !!window.history && !!window.history.pushState;
	}

	/**
	 * @inheritdoc
	 * @method setTitle
	 */
	setTitle(title) {
		document.title = title;
	}

	/**
	 * @inheritdoc
	 * @method getWebSocket
	 */
	getWebSocket() {
		return window.WebSocket;
	}

	/**
	 * @inheritdoc
	 * @method getWindow
	 */
	getWindow() {
		return window;
	}

	/**
	 * @inheritdoc
	 * @method getScrollX
	 */
	getScrollX() {
		var pageOffsetSupported = window.pageXOffset !== undefined;
		var isCSS1Compatible = (document.compatMode || '') === 'CSS1Compat';

		return pageOffsetSupported ? window.pageXOffset :
				(
					isCSS1Compatible ?
					document.documentElement.scrollLeft :
					document.body.scrollLeft
				);
	}

	/**
	 * @inheritdoc
	 * @method getScrollY
	 */
	getScrollY() {
		var pageOffsetSupported = window.pageYOffset !== undefined;
		var isCSS1Compatible = (document.compatMode || '') === 'CSS1Compat';

		return pageOffsetSupported ? window.pageYOffset :
				(
					isCSS1Compatible ?
					document.documentElement.scrollTop :
					document.body.scrollTop
				);
	}

	/**
	 * @inheritdoc
	 * @method scrollTo
	 */
	scrollTo(x, y) {
		window.scrollTo(x, y);
	}

	/**
	 * @inheritdoc
	 * @method getDomain
	 */
	getDomain() {
		return window.location.protocol + '//' + window.location.host;
	}

	/**
	 * @inheritdoc
	 * @method getHost
	 */
	getHost() {
		return window.location.host;
	}

	/**
	 * @inheritdoc
	 * @method getPath
	 */
	getPath() {
		return window.location.pathname + window.location.search;
	}

	/**
	 * @inheritdoc
	 * @method getUrl
	 */
	getUrl() {
		return window.location.href;
	}

	/**
	 * @inheritdoc
	 * @method getBody
	 */
	getBody() {
		return document.body;
	}

	/**
	 * @inheritdoc
	 * @method getElementById
	 */
	getElementById(id) {
		return document.getElementById(id);
	}

	/**
	 * @inheritdoc
	 * @method querySelector
	 */
	querySelector(selector) {
		return document.querySelector(selector);
	}

	/**
	 * @inheritdoc
	 * @method querySelectorAll
	 */
	querySelectorAll(selector) {
		return document.querySelectorAll(selector);
	}

	/**
	 * @inheritdoc
	 * @method redirect
	 */
	redirect(url) {
		window.location.href = url;
	}

	/**
	 * @inheritdoc
	 * @method pushState
	 */
	pushState(state, title, url) {
		if (window.history.pushState) {
			window.history.pushState(state, title, url);
		}
	}

	/**
	 * @inheritdoc
	 * @method replaceState
	 */
	replaceState(state, title, url) {
		if (window.history.replaceState) {
			window.history.replaceState(state, title, url);
		}
	}

	/**
	 * @inheritdoc
	 * @method createCustomEvent
	 */
	createCustomEvent(name, options) {
		return new CustomEvent(name, options);
	}

	/**
	 * @inheritdoc
	 * @method bindEventListener
	 */
	bindEventListener(eventTarget, event, listener, useCapture = false) {
		if (eventTarget.addEventListener) {
			eventTarget.addEventListener(event, listener, useCapture);
		}
	}

	/**
	 * @inheritdoc
	 * @method unbindEventListener
	 */
	unbindEventListener(eventTarget, event, listener, useCapture = false) {
		if (eventTarget.removeEventListener) {
			eventTarget.removeEventListener(event, listener, useCapture);
		}
	}
}

ns.ima.window.ClientWindow = ClientWindow;
<commit_msg>#! Fixed bug with JS error when storages are forbidden in browser.<commit_after>// @client-side

import ns from 'ima/namespace';
import WindowInterface from 'ima/window/Window';

ns.namespace('ima.window');

/**
 * Client-side implementation of the {@code ima.window.Window} utility API.
 *
 * @class ClientWindow
 * @implements ima.window.Window
 * @namespace ima.window
 * @module ima
 * @submodule ima.window
 */
export default class ClientWindow extends WindowInterface {

	/**
	 * @inheritdoc
	 * @method isClient
	 */
	isClient() {
		return true;
	}

	/**
	 * @inheritdoc
	 * @method isCookieEnabled
	 */
	isCookieEnabled() {
		return navigator.cookieEnabled;
	}

	/**
	 * @inheritdoc
	 * @method hasSessionStorage
	 */
	hasSessionStorage() {
		try {
			if (window.sessionStorage) {
				var sessionKey = 'IMA.jsTest';

				sessionStorage.setItem(sessionKey, 1);
				sessionStorage.removeItem(sessionKey);

				return true;
			}
		} catch (e) {
			if ($Debug) {
				console.warn('Session Storage is not accessible!', e);
			}
			return false;
		}
		return false;
	}

	/**
	 * @inheritdoc
	 * @method hasWebSocket
	 */
	hasWebSocket() {
		return window.WebSocket;
	}

	/**
	 * @inheritdoc
	 * @method hasHistoryAPI
	 */
	hasHistoryAPI() {
		return !!window.history && !!window.history.pushState;
	}

	/**
	 * @inheritdoc
	 * @method setTitle
	 */
	setTitle(title) {
		document.title = title;
	}

	/**
	 * @inheritdoc
	 * @method getWebSocket
	 */
	getWebSocket() {
		return window.WebSocket;
	}

	/**
	 * @inheritdoc
	 * @method getWindow
	 */
	getWindow() {
		return window;
	}

	/**
	 * @inheritdoc
	 * @method getScrollX
	 */
	getScrollX() {
		var pageOffsetSupported = window.pageXOffset !== undefined;
		var isCSS1Compatible = (document.compatMode || '') === 'CSS1Compat';

		return pageOffsetSupported ? window.pageXOffset :
				(
					isCSS1Compatible ?
					document.documentElement.scrollLeft :
					document.body.scrollLeft
				);
	}

	/**
	 * @inheritdoc
	 * @method getScrollY
	 */
	getScrollY() {
		var pageOffsetSupported = window.pageYOffset !== undefined;
		var isCSS1Compatible = (document.compatMode || '') === 'CSS1Compat';

		return pageOffsetSupported ? window.pageYOffset :
				(
					isCSS1Compatible ?
					document.documentElement.scrollTop :
					document.body.scrollTop
				);
	}

	/**
	 * @inheritdoc
	 * @method scrollTo
	 */
	scrollTo(x, y) {
		window.scrollTo(x, y);
	}

	/**
	 * @inheritdoc
	 * @method getDomain
	 */
	getDomain() {
		return window.location.protocol + '//' + window.location.host;
	}

	/**
	 * @inheritdoc
	 * @method getHost
	 */
	getHost() {
		return window.location.host;
	}

	/**
	 * @inheritdoc
	 * @method getPath
	 */
	getPath() {
		return window.location.pathname + window.location.search;
	}

	/**
	 * @inheritdoc
	 * @method getUrl
	 */
	getUrl() {
		return window.location.href;
	}

	/**
	 * @inheritdoc
	 * @method getBody
	 */
	getBody() {
		return document.body;
	}

	/**
	 * @inheritdoc
	 * @method getElementById
	 */
	getElementById(id) {
		return document.getElementById(id);
	}

	/**
	 * @inheritdoc
	 * @method querySelector
	 */
	querySelector(selector) {
		return document.querySelector(selector);
	}

	/**
	 * @inheritdoc
	 * @method querySelectorAll
	 */
	querySelectorAll(selector) {
		return document.querySelectorAll(selector);
	}

	/**
	 * @inheritdoc
	 * @method redirect
	 */
	redirect(url) {
		window.location.href = url;
	}

	/**
	 * @inheritdoc
	 * @method pushState
	 */
	pushState(state, title, url) {
		if (window.history.pushState) {
			window.history.pushState(state, title, url);
		}
	}

	/**
	 * @inheritdoc
	 * @method replaceState
	 */
	replaceState(state, title, url) {
		if (window.history.replaceState) {
			window.history.replaceState(state, title, url);
		}
	}

	/**
	 * @inheritdoc
	 * @method createCustomEvent
	 */
	createCustomEvent(name, options) {
		return new CustomEvent(name, options);
	}

	/**
	 * @inheritdoc
	 * @method bindEventListener
	 */
	bindEventListener(eventTarget, event, listener, useCapture = false) {
		if (eventTarget.addEventListener) {
			eventTarget.addEventListener(event, listener, useCapture);
		}
	}

	/**
	 * @inheritdoc
	 * @method unbindEventListener
	 */
	unbindEventListener(eventTarget, event, listener, useCapture = false) {
		if (eventTarget.removeEventListener) {
			eventTarget.removeEventListener(event, listener, useCapture);
		}
	}
}

ns.ima.window.ClientWindow = ClientWindow;
<|endoftext|>


<commit_before>const {describe, it, beforeEach} = require('mocha')
const {expect} = require('chai')
  .use(require('./index.js'))

let element

beforeEach(function createElement() {
  element = document.createElement('div')
  element.style.color = 'red'
  element.style.fontSize = '12px'
})

describe('chai-style', () => {
  describe('module', () => {
    it('should exports a named function', () => {
      const module = require('./index.js')
      expect(module).to.be.a('function')
      expect(module.name).to.be.equal('chaiStyle')
    })
  })

  describe('properties', () => {
    it('should assert success', () => {
      expect(element).to.have.style('color')
    })

    it('should assert success', () => {
      expect(element).to.not.have.style('background-color')
    })

    it('should assert using hifen', () => {
      expect(element).to.have.style('font-size')
    })

    it('should assert using camel case', () => {
      expect(element).to.have.style('fontSize')
    })

    it('should assert with external style', () => {
      expect(element).to.have.style('textTransform')
    })
  })

  describe('values', () => {
    it('should assert success', () => {
      expect(element).to.have.style('color', 'red')
    })

    it('should assert fail', () => {
      expect(() => {
        expect(element).to.have.style('color', 'blue')
      })
      .to.throw('expect DIV to have a blue color, is receiving red')
    })

    it('should assert fail, using .not', () => {
      expect(() => {
        expect(element).to.not.have.style('color', 'red')
      })
      .to.throw('expect DIV to not have a red color, is receiving red')
    })

    it('should assert using hifen', () => {
      expect(element).to.have.style('font-size', '12px')
    })

    it('should assert using camel case', () => {
      expect(element).to.have.style('fontSize', '12px')
    })

    it('should assert with external style', () => {
      expect(element).to.have.style('textTransform', 'uppercase')
    })
  })
})
<commit_msg>better description to each spec<commit_after>const {describe, it, beforeEach} = require('mocha')
const {expect} = require('chai')
  .use(require('./index.js'))

let element

beforeEach(function createElement() {
  element = document.createElement('div')
  element.style.color = 'red'
  element.style.fontSize = '12px'
})

describe('chai-style', () => {
  describe('module', () => {
    it('should exports a named function', () => {
      const module = require('./index.js')
      expect(module).to.be.a('function')
      expect(module.name).to.be.equal('chaiStyle')
    })
  })

  describe('properties', () => {
    it('should have property', () => {
      expect(element).to.have.style('color')
    })

    it('should not have property', () => {
      expect(element).to.not.have.style('background-color')
    })

    it('should get property using hifen', () => {
      expect(element).to.have.style('font-size')
    })

    it('should get property using camelCase', () => {
      expect(element).to.have.style('fontSize')
    })

    it('should get property defined in external css', () => {
      expect(element).to.have.style('textTransform')
    })
  })

  describe('values', () => {
    it('should get value of property', () => {
      expect(element).to.have.style('color', 'red')
    })

    it('should throw error, when value expected is different', () => {
      expect(() => {
        expect(element).to.have.style('color', 'blue')
      })
      .to.throw('expect DIV to have a blue color, is receiving red')
    })

    it('should throw, when value expected not be equal', () => {
      expect(() => {
        expect(element).to.not.have.style('color', 'red')
      })
      .to.throw('expect DIV to not have a red color, is receiving red')
    })

    it('should get value using hifen in property name', () => {
      expect(element).to.have.style('font-size', '12px')
    })

    it('should get value using camelCase in property name', () => {
      expect(element).to.have.style('fontSize', '12px')
    })

    it('should get value defined in external css', () => {
      expect(element).to.have.style('textTransform', 'uppercase')
    })
  })
})
<|endoftext|>


<commit_before>/* vim: set ts=4 shiftwidth=4 expandtab: */
const Desklet = imports.ui.desklet;
const Settings = imports.ui.settings;
const St = imports.gi.St;
const Soup = imports.gi.Soup;
const Mainloop = imports.mainloop;

function ThingyDesklet(metadata, desklet_id) {
    this._init(metadata, desklet_id);
}

ThingyDesklet.prototype = {
    __proto__: Desklet.Desklet.prototype,

    soup: new Soup.SessionAsync(),

    labels: [],

    _init: function(metadata, desklet_id) {
        Desklet.Desklet.prototype._init.call(this, metadata,
desklet_id);

        this.settings = new Settings.DeskletSettings(this, this.metadata["uuid"], desklet_id);

        this.settings.bindProperty(Settings.BindingDirection.IN, "username", "username", this.updateStreams, null);

        this.initUI();
        this.updateStreams();
    },

    on_desklet_removed: function() {
       Mainloop.source_remove(this.timer);
    },

    updateStreams: function() {
        // clear the desklet
        Mainloop.source_remove(this.timer);
        this.title.set_text("online streams for " + this.username);
        this.labels.forEach(label => {
            this.window.remove_actor(label);
        });

        let localUsername = this.username; // to prevent async responses from creating labels for usernames that have been changed
                                           // this is unfortunately necessary because cinnamon doesn't wait for the user to finish inputting text into a entry.

        let res = Soup.Message.new("GET", "https://api.twitch.tv/kraken/users/"+ localUsername +"/follows/channels");
        this.soup.queue_message(res, (session, message) => {
            if (message.status_code !== 200) {
                return;
            }
            let j = JSON.parse(message.response_body.data);
            j.follows.forEach(obj => {
                let stream = "https://api.twitch.tv/kraken/streams/" + obj.channel.name;
                let msg = Soup.Message.new("GET", stream);
                this.soup.queue_message(msg, (session, message) => {
                    if (message.status_code !== 200) {
                        return;
                    }
                    let k = JSON.parse(message.response_body.data);
                    if (k.stream !== null && localUsername === this.username) {
                        let lbl = new St.Label();
                        lbl.set_text(obj.channel.name + " online");
                        this.window.add_actor(lbl);
                        this.labels.push(lbl);
                    }
                });
            });
        });
        this.timer = Mainloop.timeout_add_seconds(600, this.updateStreams.bind(this));
    },

    initUI: function() {
        this.window = new St.BoxLayout({vertical: true});
        this.title = new St.Label();
        this.title.set_text("starting up...");

        this.window.add_actor(this.title);
        this.setContent(this.window);
    }
}

function main(metadata, desklet_id) {
    return new ThingyDesklet(metadata, desklet_id)
}
<commit_msg>clear the labels array when updating<commit_after>/* vim: set ts=4 shiftwidth=4 expandtab: */
const Desklet = imports.ui.desklet;
const Settings = imports.ui.settings;
const St = imports.gi.St;
const Soup = imports.gi.Soup;
const Mainloop = imports.mainloop;

function ThingyDesklet(metadata, desklet_id) {
    this._init(metadata, desklet_id);
}

ThingyDesklet.prototype = {
    __proto__: Desklet.Desklet.prototype,

    soup: new Soup.SessionAsync(),

    labels: [],

    _init: function(metadata, desklet_id) {
        Desklet.Desklet.prototype._init.call(this, metadata,
desklet_id);

        this.settings = new Settings.DeskletSettings(this, this.metadata["uuid"], desklet_id);

        this.settings.bindProperty(Settings.BindingDirection.IN, "username", "username", this.updateStreams, null);

        this.initUI();
        this.updateStreams();
    },

    on_desklet_removed: function() {
       Mainloop.source_remove(this.timer);
    },

    updateStreams: function() {
        // clear the desklet
        Mainloop.source_remove(this.timer);
        this.title.set_text("online streams for " + this.username);
        this.labels.forEach(label => {
            this.window.remove_actor(label);
        });
        this.labels = [];

        let localUsername = this.username; // to prevent async responses from creating labels for usernames that have been changed
                                           // this is unfortunately necessary because cinnamon doesn't wait for the user to finish inputting text into a entry.

        let res = Soup.Message.new("GET", "https://api.twitch.tv/kraken/users/"+ localUsername +"/follows/channels");
        this.soup.queue_message(res, (session, message) => {
            if (message.status_code !== 200) {
                return;
            }
            let j = JSON.parse(message.response_body.data);
            j.follows.forEach(obj => {
                let stream = "https://api.twitch.tv/kraken/streams/" + obj.channel.name;
                let msg = Soup.Message.new("GET", stream);
                this.soup.queue_message(msg, (session, message) => {
                    if (message.status_code !== 200) {
                        return;
                    }
                    let k = JSON.parse(message.response_body.data);
                    if (k.stream !== null && localUsername === this.username) {
                        let lbl = new St.Label();
                        lbl.set_text(obj.channel.name + " online");
                        this.window.add_actor(lbl);
                        this.labels.push(lbl);
                    }
                });
            });
        });
        this.timer = Mainloop.timeout_add_seconds(600, this.updateStreams.bind(this));
    },

    initUI: function() {
        this.window = new St.BoxLayout({vertical: true});
        this.title = new St.Label();
        this.title.set_text("starting up...");

        this.window.add_actor(this.title);
        this.setContent(this.window);
    }
}

function main(metadata, desklet_id) {
    return new ThingyDesklet(metadata, desklet_id)
}
<|endoftext|>


<commit_before>/*
 * phyx2ontology.js: A tool for synthesizing all PHYX files into a single
 * Clade Ontology in JSON-LD.
 *
 * Synopsis: phyx2ontology.js [-d root] > ontology.json
 *
 * Arguments:
 *  - `-d`: Set the directory to look for PHYX files in.
 */

// Configuration options.
const PHYX_CONTEXT_JSON = 'http://www.phyloref.org/curation-tool/json/phyx.json';
const CLADE_ONTOLOGY_BASEURI = 'http://phyloref.org/clade-ontology/clado.owl';

// Load necessary modules.
const process = require('process');
const fs = require('fs');
const path = require('path');

/*
 * phyx.js uses some code (in particular through phylotree.js) that expects certain
 * Javascript libraries to be loaded via the browser using <script>. To replicate
 * this in Node, we load them and add them to the global object.
 */

// Load moment as a global variable so it can be accessed by phyx.js.
global.moment = require('moment');

// Load jQuery.extend as a global variable so it can be accessed by phyx.js.
global.jQuery = {};
global.jQuery.extend = require('extend');

// Load d3 as a global variable so it can be accessed by both phylotree.js (which
// needs to add additional objects to it) and phyx.js (which needs to call it).
global.d3 = require('d3');

// Define hasOwnProperty() so we can use it on all classes.
function hasOwnProperty(obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key);
}

// phylotree.js does not export functions itself, but adds them to global.d3.layout.
// So we set up a global.d3.layout object for them to be added to, and then we include
// phylotree.js ourselves.
if (!hasOwnProperty(global.d3, 'layout')) {
  global.d3.layout = {};
}
require('../curation-tool/lib/phylotree.js/phylotree.js');

// Load phyx.js, our PHYX library.
const phyx = require('../curation-tool/js/phyx');

// Helper methods.

/*
 * Returns a list of PHYX files that we can test in the provided directory.
 *
 * We use a simple flatMap to search for these files. Since flatMap isn't in
 * Node.js yet, I use a map to recursively find the files and then flatten it
 * using reduce() and concat().
 *
 * This could be implemented asynchronously, but we need a list of files to
 * test each one individually in Mocha, so we need a synchronous implementation
 * to get that list before we start testing.
 */
function findPHYXFiles(dirPath) {
    return fs.readdirSync(dirPath).map(function(filename) {
        const filePath = path.join(dirPath, filename);

        if(fs.lstatSync(filePath).isDirectory()) {
            // Recurse into this directory.
            return findPHYXFiles(filePath);
        } else {
            // Look for .json files (but not for '_as_owl.json' files, for historical reasons).
            if(filePath.endsWith('.json') && !filePath.endsWith('_as_owl.json')) {
                return [filePath];
            } else {
                return [];
            }
        }
   }).reduce((x, y) => x.concat(y), []); // This flattens the list of results.
}

// Read command-line arguments.
const argv = require('yargs')
  .usage('Usage: $0 [-d root]')
  .alias('d', 'dir')
  .nargs('d', 1)
  .describe('d', 'Directory to look for PHYX files in')
  .help('h')
  .alias('h', 'help')
  .argv;

// Make sure there are no unknown arguments.
if (argv._.length > 0) {
  throw new Error('Unknown arguments: ' + argv._);
}

// Determine the directory to process.
const dir = argv.dir || process.cwd();
// process.stderr.write(`Searching directory: ${dir}\n`);

// Get list of PHYX files to process.
const phyxFiles = findPHYXFiles(dir);
// process.stderr.write(`PHYX files to combine: ${phyxFiles}`);

// It would be pretty easy to convert each individual PHYX file into a JSON-LD file by
// using the phyx2jsonld module. However, we would then have to identify every URI in
// the file and rename all of them, which could be confusing.
let entityIndex = 0;
function getIdentifier(index) {
  return `${CLADE_ONTOLOGY_BASEURI}#CLADO_${index.toString().padStart(8, '0')}`;
}

// Loop through our files and assemble a combined ontology.
const jsons = phyxFiles
  .map(filename => {
    const content = fs.readFileSync(filename);

    // Some of these PHYX files are encrypted! We need to ignore those.
    if(content.slice(0, 9).equals(Buffer.from("\x00GITCRYPT"))) {
      return [];
    }

    // Try to read this PHYX file as a JSON file.
    let json;
    try {
      json = JSON.parse(content.toString('utf8'));
    } catch(err) {
      process.stderr.write(`WARNING: ${filename} could not be read as JSON, skipping.`);
      return [];
    }

    // If not encrypted, we can treat this file as UTF-8 and read it as a string.
    return [json];
  })
  // Flatten map to remove files that could not be read or parsed.
  .reduce((a, b) => a.concat(b), []);

/*
 * We'd like our combine JSON-LD file to be in this format:
 * [{
 *    '@context': 'http://phyloref.org/curation-tool/json/phyx.json',
 *    '@id': 'http://vocab.phyloref.org/clado/',
 *    '@type': 'owl:Ontology'
 *    [... ontology metadata ...]
 * }, {
 *    '@context': 'http://phyloref.org/curation-tool/json/phyx.json',
 *    '@id': 'http://vocab.phyloref.org/clado/CLADO_PHYLOREF_1',
 *    '@type': 'phyloref:Phyloreference',
 *    'label': '...',
 *    'hasInternalSpecifier': ...,
 *    'internalSpecifiers': ...
 * }, {
 *    '@context': 'http://phyloref.org/curation-tool/json/phyx.json',
 *    '@id': 'http://vocab.phyloref.org/clado/CLADO_PHYLOGENY_1',
 *    '@type': 'cdao:???',
 *    'includesNodes': [
 *      {'@id': 'http://vocab.phyloref.org/clado/CLADO_PHYLOGENY_1_node1', ...},
 *      {'@id': 'http://vocab.phyloref.org/clado/CLADO_PHYLOGENY_1_node2', ...}
 *    ]
 * }]
 */
const phylorefs = [];
let specifiers = [];
for (let phyxFile of jsons) {
  for (let phyloref of phyxFile.phylorefs) {
    entityIndex += 1;
    const jsonld = new phyx.PhylorefWrapper(phyloref).exportAsJSONLD(getIdentifier(entityIndex));

    jsonld['@context'] = PHYX_CONTEXT_JSON;
    phylorefs.push(jsonld);
  }
}

const phylogenies = [];
for (var phyxFile of jsons) {
  for (var phylogeny of phyxFile.phylogenies) {
    entityIndex += 1;

    // TODO: Add phylogeny, nodes and taxonomic units.
    // TODO: Add matching to *all* specifiers.
  }
}

const cladeOntology = [
  {
    '@context': PHYX_CONTEXT_JSON,
    '@id': CLADE_ONTOLOGY_BASEURI,
    '@type': 'owl:Ontology',
    'owl:imports': [
      'http://raw.githubusercontent.com/phyloref/curation-workflow/develop/ontologies/phyloref_testcase.owl',
      'http://ontology.phyloref.org/phyloref.owl',
      'http://purl.obolibrary.org/obo/bco.owl'
    ]
  }
];
console.log(JSON.stringify(
  cladeOntology.concat(phylorefs).concat(phylogenies),
  null,
  4
));
<commit_msg>Added taxonomic unit matching.<commit_after>/*
 * phyx2ontology.js: A tool for synthesizing all PHYX files into a single
 * Clade Ontology in JSON-LD.
 *
 * Synopsis: phyx2ontology.js [-d root] > ontology.json
 *
 * Arguments:
 *  - `-d`: Set the directory to look for PHYX files in.
 */

// Configuration options.
const PHYX_CONTEXT_JSON = 'http://www.phyloref.org/curation-tool/json/phyx.json';
const CLADE_ONTOLOGY_BASEURI = 'http://phyloref.org/clade-ontology/clado.owl';

// Load necessary modules.
const process = require('process');
const fs = require('fs');
const path = require('path');

/*
 * phyx.js uses some code (in particular through phylotree.js) that expects certain
 * Javascript libraries to be loaded via the browser using <script>. To replicate
 * this in Node, we load them and add them to the global object.
 */

// Load moment as a global variable so it can be accessed by phyx.js.
global.moment = require('moment');

// Load jQuery.extend as a global variable so it can be accessed by phyx.js.
global.jQuery = {};
global.jQuery.extend = require('extend');

// Load d3 as a global variable so it can be accessed by both phylotree.js (which
// needs to add additional objects to it) and phyx.js (which needs to call it).
global.d3 = require('d3');

// Define hasOwnProperty() so we can use it on all classes.
function hasOwnProperty(obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key);
}

// phylotree.js does not export functions itself, but adds them to global.d3.layout.
// So we set up a global.d3.layout object for them to be added to, and then we include
// phylotree.js ourselves.
if (!hasOwnProperty(global.d3, 'layout')) {
  global.d3.layout = {};
}
require('../curation-tool/lib/phylotree.js/phylotree.js');

// Load phyx.js, our PHYX library.
const phyx = require('../curation-tool/js/phyx');

// Helper methods.

/*
 * Returns a list of PHYX files that we can test in the provided directory.
 *
 * We use a simple flatMap to search for these files. Since flatMap isn't in
 * Node.js yet, I use a map to recursively find the files and then flatten it
 * using reduce() and concat().
 *
 * This could be implemented asynchronously, but we need a list of files to
 * test each one individually in Mocha, so we need a synchronous implementation
 * to get that list before we start testing.
 */
function findPHYXFiles(dirPath) {
    return fs.readdirSync(dirPath).map(function(filename) {
        const filePath = path.join(dirPath, filename);

        if(fs.lstatSync(filePath).isDirectory()) {
            // Recurse into this directory.
            return findPHYXFiles(filePath);
        } else {
            // Look for .json files (but not for '_as_owl.json' files, for historical reasons).
            if(filePath.endsWith('.json') && !filePath.endsWith('_as_owl.json')) {
                return [filePath];
            } else {
                return [];
            }
        }
   }).reduce((x, y) => x.concat(y), []); // This flattens the list of results.
}

// Read command-line arguments.
const argv = require('yargs')
  .usage('Usage: $0 [-d root]')
  .alias('d', 'dir')
  .nargs('d', 1)
  .describe('d', 'Directory to look for PHYX files in')
  .help('h')
  .alias('h', 'help')
  .argv;

// Make sure there are no unknown arguments.
if (argv._.length > 0) {
  throw new Error('Unknown arguments: ' + argv._);
}

// Determine the directory to process.
const dir = argv.dir || process.cwd();
// process.stderr.write(`Searching directory: ${dir}\n`);

// Get list of PHYX files to process.
const phyxFiles = findPHYXFiles(dir);
// process.stderr.write(`PHYX files to combine: ${phyxFiles}`);

// It would be pretty easy to convert each individual PHYX file into a JSON-LD file by
// using the phyx2jsonld module. However, we would then have to identify every URI in
// the file and rename all of them, which could be confusing.
let entityIndex = 0;
function getIdentifier(index) {
  return `${CLADE_ONTOLOGY_BASEURI}#CLADO_${index.toString().padStart(8, '0')}`;
}

// Loop through our files and assemble a combined ontology.
const jsons = phyxFiles
  .map(filename => {
    const content = fs.readFileSync(filename);

    // Some of these PHYX files are encrypted! We need to ignore those.
    if(content.slice(0, 9).equals(Buffer.from("\x00GITCRYPT"))) {
      return [];
    }

    // Try to read this PHYX file as a JSON file.
    let json;
    try {
      json = JSON.parse(content.toString('utf8'));
    } catch(err) {
      process.stderr.write(`WARNING: ${filename} could not be read as JSON, skipping.`);
      return [];
    }

    // If not encrypted, we can treat this file as UTF-8 and read it as a string.
    return [json];
  })
  // Flatten map to remove files that could not be read or parsed.
  .reduce((a, b) => a.concat(b), []);

/*
 * We'd like our combine JSON-LD file to be in this format:
 * [{
 *    '@context': 'http://phyloref.org/curation-tool/json/phyx.json',
 *    '@id': 'http://vocab.phyloref.org/clado/',
 *    '@type': 'owl:Ontology'
 *    [... ontology metadata ...]
 * }, {
 *    '@context': 'http://phyloref.org/curation-tool/json/phyx.json',
 *    '@id': 'http://vocab.phyloref.org/clado/CLADO_PHYLOREF_1',
 *    '@type': 'phyloref:Phyloreference',
 *    'label': '...',
 *    'hasInternalSpecifier': ...,
 *    'internalSpecifiers': ...
 * }, {
 *    '@context': 'http://phyloref.org/curation-tool/json/phyx.json',
 *    '@id': 'http://vocab.phyloref.org/clado/CLADO_PHYLOGENY_1',
 *    '@type': 'cdao:???',
 *    'includesNodes': [
 *      {'@id': 'http://vocab.phyloref.org/clado/CLADO_PHYLOGENY_1_node1', ...},
 *      {'@id': 'http://vocab.phyloref.org/clado/CLADO_PHYLOGENY_1_node2', ...}
 *    ]
 * }]
 */
const phylorefs = [];
let specifiers = [];
for (let phyxFile of jsons) {
  for (let phyloref of phyxFile.phylorefs) {
    entityIndex += 1;
    const jsonld = new phyx.PhylorefWrapper(phyloref).exportAsJSONLD(getIdentifier(entityIndex));

    jsonld['@context'] = PHYX_CONTEXT_JSON;
    phylorefs.push(jsonld);
  }
}

const phylogenies = [];
const tunitMatches = [];
for (let phyxFile of jsons) {
  for (let phylogeny of phyxFile.phylogenies) {
    entityIndex += 1;
    const phylogenyAsJSONLD = new phyx.PhylogenyWrapper(phylogeny).asJSONLD(getIdentifier(entityIndex));

    phylogenyAsJSONLD['@context'] = PHYX_CONTEXT_JSON;
    phylogenies.push(phylogenyAsJSONLD);
  }
}

for(let phyloref of phylorefs) {
  let specifiers = [];
  if (hasOwnProperty(phyloref, 'internalSpecifiers')) specifiers = phyloref.internalSpecifiers;
  if (hasOwnProperty(phyloref, 'externalSpecifiers')) specifiers = specifiers.concat(phyloref.externalSpecifiers);

  for(let specifier of specifiers) {
    let countMatchedNodes = 0;

    if (hasOwnProperty(specifier, 'referencesTaxonomicUnits')) {
      for(let specifierTU of specifier.referencesTaxonomicUnits) {

        for(let phylogenyAsJSONLD of phylogenies) {
          for(let node of phylogenyAsJSONLD.nodes) {
            if (!hasOwnProperty(node, 'representsTaxonomicUnits')) continue;

            for(let nodeTU of node.representsTaxonomicUnits) {
              const matcher = new phyx.TaxonomicUnitMatcher(specifierTU, nodeTU);
              if (matcher.matched) {
                entityIndex += 1;
                const tuMatchAsJSONLD = matcher.asJSONLD(getIdentifier(entityIndex));

                countMatchedNodes += 1;
                tuMatchAsJSONLD['@context'] = PHYX_CONTEXT_JSON;
                tunitMatches.push(tuMatchAsJSONLD);
              }
            }
          }
        }
      }

      // If this specifier could not be matched, record it as an unmatched specifier.
      if(countMatchedNodes == 0) {
        if (!hasOwnProperty(phyloref, 'hasUnmatchedSpecifiers')) phyloref.hasUnmatchedSpecifiers = [];
        phyloref.hasUnmatchedSpecifiers.push({'@id': specifier['@id']});
      }
    }
  }
}

const cladeOntology = [
  {
    '@context': PHYX_CONTEXT_JSON,
    '@id': CLADE_ONTOLOGY_BASEURI,
    '@type': 'owl:Ontology',
    'owl:imports': [
      'http://raw.githubusercontent.com/phyloref/curation-workflow/develop/ontologies/phyloref_testcase.owl',
      'http://ontology.phyloref.org/phyloref.owl',
      'http://purl.obolibrary.org/obo/bco.owl'
    ]
  }
];
console.log(JSON.stringify(
  cladeOntology.concat(phylorefs).concat(phylogenies).concat(tunitMatches),
  null,
  4
));
<|endoftext|>


<commit_before>$(document).ready(function() {
  var patterns = {
    'buttons': [
      'patterns/buttons/html/close-button.html',
      'patterns/buttons/html/danger-button.html',
      'patterns/buttons/html/primary-button.html',
      'patterns/buttons/html/secondary-button.html',
      'patterns/buttons/html/tertiary-button.html',
      'patterns/buttons/html/warning-button.html'
    ],
    'cards': [
      'patterns/cards/html/card.html'
    ],
    'forms': [
      'patterns/forms/html/form.html'
    ],
    'links': [
      'patterns/links/html/content-link.html'
    ],
    'lists': [
      'patterns/lists/html/card-list.html',
      'patterns/lists/html/nested-list.html',
      'patterns/lists/html/ordered-list.html',
      'patterns/lists/html/unordered-list.html'
    ],
    'modals': [
      'patterns/modals/html/modal.html'
    ],
    'nav': [
      'patterns/nav/html/global-nav.html',
      'patterns/nav/html/secondary-nav.html'
    ],
    'radio': [
      'patterns/radio/html/radio.html'
    ],
    'tables': [
      'patterns/tables/html/table.html'
    ],
    'tooltips': [
      'patterns/tooltips/html/tooltip.html'
    ]
  };

  // Loop over each property in patterns object
  for (var prop in patterns) {

    // Handle the nav patterns differently
    if (prop === 'nav') {
      var navArray = patterns[prop];

      for (var i = 0; i < navArray.length; i++ ) {
        $.get(navArray[i], function (data) {

          // Append nav patterns to #nav because bluemix nav needs to be at the top of the viewport in order for the styles to work responsively.
          $('#nav').append(data);
        })
      }
    }

    else if (prop === 'cards') {
      var cardArray = patterns[prop];

      for (var i=0; i < cardArray.length; i++) {
        $.get(cardArray[i], function (data) {
          $('#cards').append(data);
        });
      }
    }
    // For all other patterns, append them to #patterns.
    else {
      var patternArray = patterns[prop];

      for (var i = 0; i < patternArray.length; i++) {
        $.get(patternArray[i], function (data) {
          $('#patterns').append('
' + data + '
');
        });
      }
    }
  };
});
<commit_msg>Delete trailing spaces<commit_after>$(document).ready(function() {
  var patterns = {
    'buttons': [
      'patterns/buttons/html/close-button.html',
      'patterns/buttons/html/danger-button.html',
      'patterns/buttons/html/primary-button.html',
      'patterns/buttons/html/secondary-button.html',
      'patterns/buttons/html/tertiary-button.html',
      'patterns/buttons/html/warning-button.html'
    ],
    'cards': [
      'patterns/cards/html/card.html'
    ],
    'forms': [
      'patterns/forms/html/form.html'
    ],
    'links': [
      'patterns/links/html/content-link.html'
    ],
    'lists': [
      'patterns/lists/html/card-list.html',
      'patterns/lists/html/nested-list.html',
      'patterns/lists/html/ordered-list.html',
      'patterns/lists/html/unordered-list.html'
    ],
    'modals': [
      'patterns/modals/html/modal.html'
    ],
    'nav': [
      'patterns/nav/html/global-nav.html',
      'patterns/nav/html/secondary-nav.html'
    ],
    'radio': [
      'patterns/radio/html/radio.html'
    ],
    'tables': [
      'patterns/tables/html/table.html'
    ],
    'tooltips': [
      'patterns/tooltips/html/tooltip.html'
    ]
  };

  // Loop over each property in patterns object
  for (var prop in patterns) {

    // Handle the nav patterns differently
    if (prop === 'nav') {
      var navArray = patterns[prop];

      for (var i = 0; i < navArray.length; i++ ) {
        $.get(navArray[i], function (data) {

          // Append nav patterns to #nav because bluemix nav needs to be at the top of the viewport in order for the styles to work responsively.
          $('#nav').append(data);
        })
      }
    }

    else if (prop === 'cards') {
      var cardArray = patterns[prop];

      for (var i=0; i < cardArray.length; i++) {
        $.get(cardArray[i], function (data) {
          $('#cards').append(data);
        });
      }
    }
    // For all other patterns, append them to #patterns.
    else {
      var patternArray = patterns[prop];

      for (var i = 0; i < patternArray.length; i++) {
        $.get(patternArray[i], function (data) {
          $('#patterns').append('
' + data + '
');
        });
      }
    }
  };
});
<|endoftext|>


<commit_before>import path from 'path'
import inquirer from 'inquirer'
import {copy} from 'copy-paste'
import {past as episodes} from '../episodes'
import {displayListify, sortPeople} from '../shared/utils'
import getEpisodeData from '../shared/get-episode-data'
import shortenEpisodeUrl from './shorten-episode-url/logic'

getEpisodeDirectory()
  .then(episodePathToEpisodeData)
  .then(generateMessage)
  .then(validateMessage)
  .then(copyMessage)
  .then(result => console.log(result))
  .catch(err => console.error(err))

function episodePathToEpisodeData(episodePath) {
  const episodeData = getEpisodeData(episodePath)
  const {title, shortUrl, guests} = episodeData
  if (shortUrl) {
    return {title, shortUrl, guests}
  }
  console.warn('This episode has no short URL.')
  return new Promise(resolve => {
    inquirer.prompt([
      {
        name: 'createShortUrl',
        type: 'confirm',
        message: 'Would you like to create one right now?',
        default: true,
      },
    ], ({createShortUrl}) => {
      if (createShortUrl) {
        const noCopy = true
        const result = shortenEpisodeUrl(episodeData, noCopy)
          .then(createdShortUrl => {
            return {title, guests, shortUrl: createdShortUrl}
          })
        resolve(result)
      } else {
        resolve({title, guests})
      }
    })
  })
}

function generateMessage({title, guests, shortUrl}) {
  const twitterHandles = sortPeople(guests).map(g => g.twitter ? `@${g.twitter}` : g.name)
  const url = shortUrl ? ` ${shortUrl}` : ''

  return `"${title}" w/ ${displayListify(twitterHandles).join('')} is up!${url}`
}

function validateMessage(message) {
  const MAX_TWEET_LENGTH = 140
  const LENGTH_OF_IMAGE = 24
  const AVAILABLE_TWEET_LENGTH = MAX_TWEET_LENGTH - LENGTH_OF_IMAGE
  if (message.length > AVAILABLE_TWEET_LENGTH) {
    console.warn('**The tweet is too long**')
  }
  return message
}

function copyMessage(message) {
  return new Promise((resolve, reject) => {
    copy(message, err => {
      if (err) {
        reject(err)
      }
      resolve(`\`${message}\` was copied to your clipboard`)
    })
  })
}

function getEpisodeDirectory() {
  if (process.argv[2]) {
    return Promise.resolve(here(process.argv[2]))
  }
  return new Promise(resolve => {
    inquirer.prompt([
      {
        name: 'episodeDate',
        type: 'list',
        choices: episodes.map(e => ({name: e.title, value: e.date})),
        message: 'Which episode is this tweet for?',
        default: episodes.length - 1,
      },
    ], ({episodeDate}) => {
      resolve(here(`episodes/${episodeDate}`))
    })
  })
}

function here(d) {
  return path.join(process.cwd(), d || '')
}
<commit_msg>Emoji-ify the tweet<commit_after>import path from 'path'
import inquirer from 'inquirer'
import {copy} from 'copy-paste'
import {sample} from 'lodash'
import {past as episodes} from '../episodes'
import {displayListify, sortPeople} from '../shared/utils'
import getEpisodeData from '../shared/get-episode-data'
import shortenEpisodeUrl from './shorten-episode-url/logic'

getEpisodeDirectory()
  .then(episodePathToEpisodeData)
  .then(generateMessage)
  .then(validateMessageAndAddEmojiIfOk)
  .then(copyMessage)
  .then(result => console.log(result))
  .catch(err => console.error(err))

function episodePathToEpisodeData(episodePath) {
  const episodeData = getEpisodeData(episodePath)
  const {title, shortUrl, guests} = episodeData
  if (shortUrl) {
    return {title, shortUrl, guests}
  }
  console.warn('This episode has no short URL.')
  return new Promise(resolve => {
    inquirer.prompt([
      {
        name: 'createShortUrl',
        type: 'confirm',
        message: 'Would you like to create one right now?',
        default: true,
      },
    ], ({createShortUrl}) => {
      if (createShortUrl) {
        const noCopy = true
        const result = shortenEpisodeUrl(episodeData, noCopy)
          .then(createdShortUrl => {
            return {title, guests, shortUrl: createdShortUrl}
          })
        resolve(result)
      } else {
        resolve({title, guests})
      }
    })
  })
}

function generateMessage({title, guests, shortUrl}) {
  const twitterHandles = sortPeople(guests).map(g => g.twitter ? `@${g.twitter}` : g.name)
  const url = shortUrl ? ` ${shortUrl}` : ''

  return `"${title}" w/ ${displayListify(twitterHandles).join('')} is up!${url}`
}

function validateMessageAndAddEmojiIfOk(message) {
  const emoji = sample(['✨', '💥', '🎉', '🚀', '🌟', '🎊', '👍', '💯', '👏', '🙌', '😎', '🔥', '😻'])
  const MAX_TWEET_LENGTH = 140
  const LENGTH_OF_IMAGE = 24
  const AVAILABLE_TWEET_LENGTH = MAX_TWEET_LENGTH - LENGTH_OF_IMAGE
  if (AVAILABLE_TWEET_LENGTH - message.length >= emoji.length) {
    return `${message} ${emoji}`
  } else if (message.length > AVAILABLE_TWEET_LENGTH) {
    console.warn('**The tweet is too long**')
  }
  return message
}

function copyMessage(message) {
  return new Promise((resolve, reject) => {
    copy(message, err => {
      if (err) {
        reject(err)
      }
      resolve(`\`${message}\` was copied to your clipboard`)
    })
  })
}

function getEpisodeDirectory() {
  if (process.argv[2]) {
    return Promise.resolve(here(process.argv[2]))
  }
  return new Promise(resolve => {
    inquirer.prompt([
      {
        name: 'episodeDate',
        type: 'list',
        choices: episodes.map(e => ({name: e.title, value: e.date})),
        message: 'Which episode is this tweet for?',
        default: episodes.length - 1,
      },
    ], ({episodeDate}) => {
      resolve(here(`episodes/${episodeDate}`))
    })
  })
}

function here(d) {
  return path.join(process.cwd(), d || '')
}
<|endoftext|>


<commit_before>// 文件监控

var path     = require('path'),
    pngquant = require('imagemin-pngquant'),
    md5      = require('js-md5');

// 逻辑变量
var justAddedImage = [],
    justBeforeAddedImage = [], // 记录压缩的图片
    qmuiSupportScssFile = '../project/_qmuiSupport.scss', // QMUI 框架需要用作功能支撑的 scss 文件
    watchTaskDesciption = '文件监控，自动执行基本的工作流，包括 Compass 自动编译，模板 include 自动编译，图片文件夹操作同步以及图片文件自动压缩';

module.exports = function(gulp, common) {
  gulp.task('watch', watchTaskDesciption, function () {

    common.log('Watch', 'QMUI 进入自动监听');

    // 监控 SASS 文件变化并自动执行 Compass 编译

    // Compass 下存在一个 bug：有四个 scss 文件 A，B，C，D，其中 A 和 B import C，C import D，那么当 D 修改后时重新 compile，只有 A 文件会被更新，B 不会被更新。但如果修改的是 C，那么 A 和 B 都会被更新。在 QMUI 的项目结构中，组件样式刚好命中这个 bug，因此这里做了一个处理，当组件样式被更新时，会触发修改一个空的 scss 文件然后 compile，这个文件被所有主样式文件 import，因此最终所有样式文件都得到更新。
    var _widgetStyleSourcePath = '../project/widget/',
        _compassWatch = gulp.watch(['../**/*.scss', '!' + _widgetStyleSourcePath, '!' + _widgetStyleSourcePath + '**/*'], ['compass']);
    _compassWatch.on('change', function() {
      common.log('');
      common.log('Compass', '进行 Compass 编译');
    });
    var _compassWidgetWatch = gulp.watch([_widgetStyleSourcePath, _widgetStyleSourcePath + '**/*']);
    _compassWidgetWatch.on('change', function() {
      gulp.src(qmuiSupportScssFile)
          .pipe(gulp.dest('../project'));
    });

    // 图片管理（图片文件夹操作同步以及图片文件自动压缩）

    // 公共部分
    var _imageMinOnSameDir = function(_dir) {
      gulp.src(_dir)
          .pipe(common.plugins.plumber({
            errorHandler: function(_error) {
              common.error('Min Image', _error);
              common.plugins.util.beep();
            }}))
          .pipe(common.plugins.imagemin({
            progressive: true,
            svgoPlugins: [{removeViewBox: false}],
            use: [pngquant()]
          }))
          .pipe(gulp.dest(path.dirname(_dir)));
    }

    // 独立图片部分

    // 自动同步独立图片文件夹的操作
    var _independentImagesSourcePath = common.config.imagesSourcePath + common.config.independentImagesDirectory,
        _independentImagesResultPath = common.config.imagesResultPath + common.config.independentImagesDirectory,
        _shouldOutputEmptyLineForSyncImage;

    // 如果有需要，则在执行同步图片任务之前输出一个空行
    var _outputEmptyForSyncImageIfNeed = function() {
      if(_shouldOutputEmptyLineForSyncImage) {
        common.log('');
        _shouldOutputEmptyLineForSyncImage = false;
      }
    }

    // 压缩独立图片并同步独立图片到 public 目录
    gulp.watch([_independentImagesSourcePath, _independentImagesSourcePath + '/**/*'], function() {
      _shouldOutputEmptyLineForSyncImage = true;
      common.plugins.fileSync(_independentImagesSourcePath, _independentImagesResultPath, {
        ignore: '.DS_Store',
        beforeAddFileCallback: function(_fullPathSrc) {
         var _absoluteMinImageFilePath = path.resolve(_fullPathSrc),
             _absoluteMinImageFilePathMd5 = md5(_absoluteMinImageFilePath);

         if(!common.lib.isElementInArray(justBeforeAddedImage, _absoluteMinImageFilePathMd5)) {
           // 这里为了避免发生“增加图片到 public/images 或修改 public/images 原有的图片，触发压缩图片，因此图片又被修改，再次触发压缩图片”的情况发生，
           // 做了一个判断，压缩一张图片时会标记下来，当再次发生图片改变时会判断这张图片是否为刚刚压缩过的图片，如果是则不执行该次压缩图片的逻辑
           // 如果不是，则说明准备处理另一张图片了，这时清空标记，进入下一张图片的处理
           justBeforeAddedImage.push(_absoluteMinImageFilePathMd5);
           _outputEmptyForSyncImageIfNeed();
           common.log('Min Image', '对 ' + _absoluteMinImageFilePath + ' 进行图片压缩');
           _imageMinOnSameDir(_absoluteMinImageFilePath);
         }
        },
        beforeUpdateFileCallback: function(_fullPathSrc) {
         var _absoluteMinImageFilePath = path.resolve(_fullPathSrc),
             _absoluteMinImageFilePathMd5 = md5(_absoluteMinImageFilePath);

         if(!common.lib.isElementInArray(justBeforeAddedImage, _absoluteMinImageFilePathMd5)) {
           justBeforeAddedImage.push(_absoluteMinImageFilePathMd5);
           _outputEmptyForSyncImageIfNeed();
           common.log('Min Image', '对 ' + _absoluteMinImageFilePath + ' 进行图片压缩');
           _imageMinOnSameDir(_absoluteMinImageFilePath);
         } else {
           justBeforeAddedImage = common.lib.deleteElementInArray(justBeforeAddedImage, _absoluteMinImageFilePathMd5);
         }
        },
        addFileCallback: function(_fullPathSrc, _fullPathDist) {
         var _absoluteMinImageFilePath = path.resolve(_fullPathDist),
             _absoluteMinImageFilePathMd5 = md5(_absoluteMinImageFilePath);

         if(!common.lib.isElementInArray(justAddedImage, _absoluteMinImageFilePathMd5)) {
           justAddedImage.push(_absoluteMinImageFilePathMd5);
           _outputEmptyForSyncImageIfNeed();
           common.log('Sync Image', '同步增加文件到 ' + _absoluteMinImageFilePath);
         }
        },
        deleteFileCallback: function(_fullPathSrc, _fullPathDist) {
         _outputEmptyForSyncImageIfNeed();
         common.log('Sync Image', '同步删除文件 ' + path.resolve(_fullPathDist));
        },
        updateFileCallback: function(_fullPathSrc, _fullPathDist) {
         var _absoluteMinImageFilePath = path.resolve(_fullPathDist),
             _absoluteMinImageFilePathMd5 = md5(_absoluteMinImageFilePath);

         if(!common.lib.isElementInArray(justAddedImage, _absoluteMinImageFilePathMd5)) {
           justAddedImage.push(_absoluteMinImageFilePathMd5);
           _outputEmptyForSyncImageIfNeed();
           common.log('Sync Image', '同步更新文件到 ' + _absoluteMinImageFilePath);
         } else {
           justAddedImage = common.lib.deleteElementInArray(justAddedImage, _absoluteMinImageFilePathMd5);
         }
        }
      });
    });

    // Compass 图片部分（即由 Compass 支持的雪碧图部分）

    // 监控雪碧图原图，若有修改触发 scss 修改，从而触发 Compass 重新生成雪碧图
    var _imageSpriteWatch = gulp.watch([common.config.imagesSourcePath + '/*/*.*', '!' + _independentImagesSourcePath, '!' + _independentImagesSourcePath + '**/*']);
    _imageSpriteWatch.on('change', function() {
      gulp.src(qmuiSupportScssFile)
          .pipe(gulp.dest('../project'));
    });

    // 压缩雪碧图
    var _minImageWatcher = gulp.watch(common.config.imagesResultPath + '/*.*');
    _minImageWatcher.on('change', function(event) {
      var _minImageFile = event.path,
          _minImageFilePathMd5 = md5(_minImageFile);
      // 这里为了避免发生“增加图片到 public/images 或修改 public/images 原有的图片，触发压缩图片，因此图片又被修改，再次触发压缩图片”的情况发生，
      // 做了一个判断，压缩一张图片时会标记下来，当再次发生图片改变时会判断这张图片是否为刚刚压缩过的图片，如果是则不执行该次压缩图片的逻辑
      // 如果不是，则说明准备处理另一张图片了，这时清空标记，进入下一张图片的处理
      if(event.type !== 'deleted' && !common.lib.isElementInArray(justAddedImage, _minImageFilePathMd5)) {

        justAddedImage.push(_minImageFilePathMd5);

        common.log('Min Image', '对 ' + _minImageFile + ' 进行图片压缩');
        _imageMinOnSameDir(_minImageFile);

      } else if(common.lib.isElementInArray(justAddedImage, _minImageFilePathMd5)) {
        justAddedImage = common.lib.deleteElementInArray(justAddedImage, _minImageFilePathMd5);
      }
    });

    // 模板自动 include
    if(common.config.openIncludeFunction) {
      var _includeWatcher = gulp.watch(common.config.htmlSourcePath, ['include']);
      _includeWatcher.on('change', function(event) {
        common.log('');
        common.log('Include', '模板 ' + event.path + ' was ' + event.type);
      });
    }

  });
};
<commit_msg>同步图片目录时排除版本管理文件<commit_after>// 文件监控

var path     = require('path'),
    pngquant = require('imagemin-pngquant'),
    md5      = require('js-md5');

// 逻辑变量
var justAddedImage = [],
    justBeforeAddedImage = [], // 记录压缩的图片
    qmuiSupportScssFile = '../project/_qmuiSupport.scss', // QMUI 框架需要用作功能支撑的 scss 文件
    watchTaskDesciption = '文件监控，自动执行基本的工作流，包括 Compass 自动编译，模板 include 自动编译，图片文件夹操作同步以及图片文件自动压缩';

module.exports = function(gulp, common) {
  gulp.task('watch', watchTaskDesciption, function () {

    common.log('Watch', 'QMUI 进入自动监听');

    // 监控 SASS 文件变化并自动执行 Compass 编译

    // Compass 下存在一个 bug：有四个 scss 文件 A，B，C，D，其中 A 和 B import C，C import D，那么当 D 修改后时重新 compile，只有 A 文件会被更新，B 不会被更新。但如果修改的是 C，那么 A 和 B 都会被更新。在 QMUI 的项目结构中，组件样式刚好命中这个 bug，因此这里做了一个处理，当组件样式被更新时，会触发修改一个空的 scss 文件然后 compile，这个文件被所有主样式文件 import，因此最终所有样式文件都得到更新。
    var _widgetStyleSourcePath = '../project/widget/',
        _compassWatch = gulp.watch(['../**/*.scss', '!' + _widgetStyleSourcePath, '!' + _widgetStyleSourcePath + '**/*'], ['compass']);
    _compassWatch.on('change', function() {
      common.log('');
      common.log('Compass', '进行 Compass 编译');
    });
    var _compassWidgetWatch = gulp.watch([_widgetStyleSourcePath, _widgetStyleSourcePath + '**/*']);
    _compassWidgetWatch.on('change', function() {
      gulp.src(qmuiSupportScssFile)
          .pipe(gulp.dest('../project'));
    });

    // 图片管理（图片文件夹操作同步以及图片文件自动压缩）

    // 公共部分
    var _imageMinOnSameDir = function(_dir) {
      gulp.src(_dir)
          .pipe(common.plugins.plumber({
            errorHandler: function(_error) {
              common.error('Min Image', _error);
              common.plugins.util.beep();
            }}))
          .pipe(common.plugins.imagemin({
            progressive: true,
            svgoPlugins: [{removeViewBox: false}],
            use: [pngquant()]
          }))
          .pipe(gulp.dest(path.dirname(_dir)));
    }

    // 独立图片部分

    // 自动同步独立图片文件夹的操作
    var _independentImagesSourcePath = common.config.imagesSourcePath + common.config.independentImagesDirectory,
        _independentImagesResultPath = common.config.imagesResultPath + common.config.independentImagesDirectory,
        _shouldOutputEmptyLineForSyncImage;

    // 如果有需要，则在执行同步图片任务之前输出一个空行
    var _outputEmptyForSyncImageIfNeed = function() {
      if(_shouldOutputEmptyLineForSyncImage) {
        common.log('');
        _shouldOutputEmptyLineForSyncImage = false;
      }
    }

    // 压缩独立图片并同步独立图片到 public 目录
    gulp.watch([_independentImagesSourcePath, _independentImagesSourcePath + '/**/*'], function() {
      _shouldOutputEmptyLineForSyncImage = true;
      common.plugins.fileSync(_independentImagesSourcePath, _independentImagesResultPath, {
        ignore: ['.DS_Store', '.svn/**/*', '.git/**/*'],
        beforeAddFileCallback: function(_fullPathSrc) {
         var _absoluteMinImageFilePath = path.resolve(_fullPathSrc),
             _absoluteMinImageFilePathMd5 = md5(_absoluteMinImageFilePath);

         if(!common.lib.isElementInArray(justBeforeAddedImage, _absoluteMinImageFilePathMd5)) {
           // 这里为了避免发生“增加图片到 public/images 或修改 public/images 原有的图片，触发压缩图片，因此图片又被修改，再次触发压缩图片”的情况发生，
           // 做了一个判断，压缩一张图片时会标记下来，当再次发生图片改变时会判断这张图片是否为刚刚压缩过的图片，如果是则不执行该次压缩图片的逻辑
           // 如果不是，则说明准备处理另一张图片了，这时清空标记，进入下一张图片的处理
           justBeforeAddedImage.push(_absoluteMinImageFilePathMd5);
           _outputEmptyForSyncImageIfNeed();
           common.log('Min Image', '对 ' + _absoluteMinImageFilePath + ' 进行图片压缩');
           _imageMinOnSameDir(_absoluteMinImageFilePath);
         }
        },
        beforeUpdateFileCallback: function(_fullPathSrc) {
         var _absoluteMinImageFilePath = path.resolve(_fullPathSrc),
             _absoluteMinImageFilePathMd5 = md5(_absoluteMinImageFilePath);

         if(!common.lib.isElementInArray(justBeforeAddedImage, _absoluteMinImageFilePathMd5)) {
           justBeforeAddedImage.push(_absoluteMinImageFilePathMd5);
           _outputEmptyForSyncImageIfNeed();
           common.log('Min Image', '对 ' + _absoluteMinImageFilePath + ' 进行图片压缩');
           _imageMinOnSameDir(_absoluteMinImageFilePath);
         } else {
           justBeforeAddedImage = common.lib.deleteElementInArray(justBeforeAddedImage, _absoluteMinImageFilePathMd5);
         }
        },
        addFileCallback: function(_fullPathSrc, _fullPathDist) {
         var _absoluteMinImageFilePath = path.resolve(_fullPathDist),
             _absoluteMinImageFilePathMd5 = md5(_absoluteMinImageFilePath);

         if(!common.lib.isElementInArray(justAddedImage, _absoluteMinImageFilePathMd5)) {
           justAddedImage.push(_absoluteMinImageFilePathMd5);
           _outputEmptyForSyncImageIfNeed();
           common.log('Sync Image', '同步增加文件到 ' + _absoluteMinImageFilePath);
         }
        },
        deleteFileCallback: function(_fullPathSrc, _fullPathDist) {
         _outputEmptyForSyncImageIfNeed();
         common.log('Sync Image', '同步删除文件 ' + path.resolve(_fullPathDist));
        },
        updateFileCallback: function(_fullPathSrc, _fullPathDist) {
         var _absoluteMinImageFilePath = path.resolve(_fullPathDist),
             _absoluteMinImageFilePathMd5 = md5(_absoluteMinImageFilePath);

         if(!common.lib.isElementInArray(justAddedImage, _absoluteMinImageFilePathMd5)) {
           justAddedImage.push(_absoluteMinImageFilePathMd5);
           _outputEmptyForSyncImageIfNeed();
           common.log('Sync Image', '同步更新文件到 ' + _absoluteMinImageFilePath);
         } else {
           justAddedImage = common.lib.deleteElementInArray(justAddedImage, _absoluteMinImageFilePathMd5);
         }
        }
      });
    });

    // Compass 图片部分（即由 Compass 支持的雪碧图部分）

    // 监控雪碧图原图，若有修改触发 scss 修改，从而触发 Compass 重新生成雪碧图
    var _imageSpriteWatch = gulp.watch([common.config.imagesSourcePath + '/*/*.*', '!' + _independentImagesSourcePath, '!' + _independentImagesSourcePath + '**/*']);
    _imageSpriteWatch.on('change', function() {
      gulp.src(qmuiSupportScssFile)
          .pipe(gulp.dest('../project'));
    });

    // 压缩雪碧图
    var _minImageWatcher = gulp.watch(common.config.imagesResultPath + '/*.*');
    _minImageWatcher.on('change', function(event) {
      var _minImageFile = event.path,
          _minImageFilePathMd5 = md5(_minImageFile);
      // 这里为了避免发生“增加图片到 public/images 或修改 public/images 原有的图片，触发压缩图片，因此图片又被修改，再次触发压缩图片”的情况发生，
      // 做了一个判断，压缩一张图片时会标记下来，当再次发生图片改变时会判断这张图片是否为刚刚压缩过的图片，如果是则不执行该次压缩图片的逻辑
      // 如果不是，则说明准备处理另一张图片了，这时清空标记，进入下一张图片的处理
      if(event.type !== 'deleted' && !common.lib.isElementInArray(justAddedImage, _minImageFilePathMd5)) {

        justAddedImage.push(_minImageFilePathMd5);

        common.log('Min Image', '对 ' + _minImageFile + ' 进行图片压缩');
        _imageMinOnSameDir(_minImageFile);

      } else if(common.lib.isElementInArray(justAddedImage, _minImageFilePathMd5)) {
        justAddedImage = common.lib.deleteElementInArray(justAddedImage, _minImageFilePathMd5);
      }
    });

    // 模板自动 include
    if(common.config.openIncludeFunction) {
      var _includeWatcher = gulp.watch(common.config.htmlSourcePath, ['include']);
      _includeWatcher.on('change', function(event) {
        common.log('');
        common.log('Include', '模板 ' + event.path + ' was ' + event.type);
      });
    }

  });
};
<|endoftext|>


<commit_before>/**
 * Main diagram class.
 *
 * @copyright   copyright (c) 2015 by Harald Lapp
 * @author      Harald Lapp <harald@octris.org>
 */

;var diagram = (function() {
    /**
     * Constructor.
     *
     * @param   mixed       canvas              Canvas selector.
     * @param   object      options             Optional options.
     */
    function diagram(canvas, options)
    {
        this.canvas = paper.setup(canvas);
        this.options = this.extend({'raster': 10}, options || {});
        this.nodes = [];
        this.wires = [];
        this.scopes = {};

        this.layers = {
            'wires': new this.canvas.Layer(),
            'nodes': new this.canvas.Layer(),
            'draw': new this.canvas.Layer()
        };

        this.wire = new diagram.wire(this);
    }

    /**
     * Helper method to create shallow copies of objects.
     *
     * @param   object      origin              Object to extend.
     * @param   object      add                 Object with properties to add.
     */
    diagram.prototype.extend = function(origin, add) {
        if (typeof add === 'object') {
            Object.keys(add).forEach(function(k) {
                origin[k] = add[k];
            });
        }

        return origin;
    };

    /**
     * Add a connector scope.
     *
     * @param   string      name                Name of scope.
     * @param   object      settings            Scope settings.
     */
    diagram.prototype.addScope = function(name, settings)
    {
        this.scopes[name] = settings;
    }

    /**
     * Test if scope is available.
     *
     * @param   string      name                Name of scope.
     * @return  bool                            Returns true if scope is available.
     */
    diagram.prototype.hasScope = function(name)
    {
        return (name in this.scopes);
    }

    /**
     * Return scope settings.
     *
     * @param   string      name                Name of scope.
     * @return  object                          Scope settings.
     */
    diagram.prototype.getScope = function(name)
    {
        return this.scopes[name];
    }

    /**
     * Return and activate layer of specified name. Returns the canvas layer, if no layer name
     * is specified.
     *
     * @param   string      name                Name of layer to get.
     * @return  node                            Layer node.
     */
    diagram.prototype.getLayer = function(name)
    {
        var layer = (typeof name !== 'undefined'
                        ? this.layers[name]
                        : this.canvas);

        layer.activate();

        return layer;
    }

    /**
     * Return wires.
     *
     * @return  array                           Array ofwires.
     */
    diagram.prototype.exportWires = function()
    {
        return this.wire.exportWires();
    }

    /**
     * Export nodes.
     *
     * @return  array                           Array of nodes.
     */
    diagram.prototype.exportNodes = function()
    {
        return this.nodes.map(function(node) {
            return node.getData();
        });
    }

    /**
     * Add multiple nodes.
     *
     * @param   array       nodes               Array of nodes.
     */
    diagram.prototype.addNodes = function(nodes)
    {
        nodes.forEach(function(data) {
            this.addNode(data);
        }, this);
    }

    /**
     * Add a single node to diagram.
     *
     * @param   object      data                Node data.
     */
    diagram.prototype.addNode = function(data)
    {
        var node = new node_types[data.node](this, data);

        this.nodes.push(node);
    }

    /**
     * Add multiple wires.
     *
     * @param   array       wires               Array of wires.
     */
    diagram.prototype.addWires = function(wires)
    {
        this.wires = this.wires.concat(wires);
    }

    /**
     * Add a single wire to diagram.
     *
     * @param   object      wire                Wire to add.
     */
    diagram.prototype.addWire = function(wire)
    {
        this.wires.push(wire);
    }

    /**
     * Remove node of the specified ID.
     *
     * @param   string      id                  ID of node to remove.
     */
    diagram.prototype.removeNode = function(id)
    {
        this.nodes = this.nodes.filter(function(node) {
            var ret = true;

            if (node.getId() == id) {
                node.destroy();

                ret = false;
            }

            return ret;
        });
    }

    /**
     * Render diagram and wire all nodes as specified.
     */
    diagram.prototype.render = function()
    {
        // determine if dagre should be used for layouting
        var use_dagre = false;

        if (typeof dagre !== 'undefined') {
            for (var i = 0, rect = null, cnt = this.nodes.length; i < cnt; ++i) {
                rect = this.nodes[i].getRect();

                if ((use_dagre = (rect.x === null || rect.y === null))) {
                    break;
                }
            }
        }

        if (use_dagre) {
            // render nodes with calculated graph layout using dagre library
            var g = new dagre.graphlib.Graph();

            g.setGraph({'rankdir': 'LR'});
            g.setDefaultEdgeLabel(function() { return {}; });

            this.nodes.forEach(function(node) {
                var rect = node.getRect();

                g.setNode(node.getId(), {'width': rect.width, 'height': rect.height});
            });

            this.wires.forEach(function(wire) {
                var source = this.wire.registry[wire.source];
                var target = this.wire.registry[wire.target];

                g.setEdge(source.getNode().getId(), target.getNode().getId());
            }, this);

            dagre.layout(g);

            this.nodes.forEach(function(node) {
                var rect = g.node(node.getId());

                node.render({'x': rect.x, 'y': rect.y});
            }, this);
        } else {
            // render without using dagre
            this.nodes.forEach(function(node) {
                node.render();
            });
        }

        // render wires
        this.wires.forEach(function(wire) {
            this.wire.addWire(wire.source, wire.target);
        }, this);

        //
        paper.view.draw();
    }

    return diagram;
})();
<commit_msg>implemented zoom and pan functionality<commit_after>/**
 * Main diagram class.
 *
 * @copyright   copyright (c) 2015 by Harald Lapp
 * @author      Harald Lapp <harald@octris.org>
 */

;var diagram = (function() {
    /*
     * zoom and pan inspired by StableZoom
     * https://github.com/mberth/PanAndZoom/blob/master/app/scripts/pan_and_zoom.coffee
     */
    function zoom(old_zoom, delta, c, p) {
        var factor = 1.05;
        var new_zoom = (delta < 0
                        ? old_zoom * factor
                        : (delta > 0
                            ? old_zoom / factor
                            : old_zoom));

        return {
            zoom: new_zoom,
            offset: p.subtract(p.subtract(c).multiply(old_zoom / new_zoom)).subtract(c)
        };
    }

    function pan(old_center, delta_x, delta_y) {
        var factor = 0.75;

        return old_center.add((new paper.Point(-delta_x, -delta_y)).multiply(factor));
    }

    /**
     * Constructor.
     *
     * @param   mixed       canvas              Canvas selector.
     * @param   object      options             Optional options.
     */
    function diagram(canvas, options)
    {
        this.canvas = paper.setup(canvas);
        this.options = this.extend({'raster': 10}, options || {});
        this.nodes = [];
        this.wires = [];
        this.scopes = {};

        // make diagram zoomable and panable
        $('#' + canvas).mousewheel(function(event) {
            var pos = paper.view.viewToProject(new paper.Point(event.offsetX, event.offsetY));
            var ret = zoom(paper.view.zoom, event.deltaY, paper.view.center, pos);

            paper.view.zoom = ret.zoom;
            paper.view.center = paper.view.center.add(ret.offset);

            event.preventDefault();

            paper.view.draw();
        });

        // pan
        var background = new this.canvas.Layer();

        var rect = new paper.Path.Rectangle({
            point: [0, 0],
            size: [paper.view.viewSize.width, paper.view.viewSize.height],
            fillColor: 'lightgray'
        });

        rect.onMouseEnter = function() {
            document.body.style.cursor = 'pointer';
        }
        rect.onMouseLeave = function() {
            document.body.style.cursor = 'default';
        }
        rect.onMouseDrag = function(event) {
            var center = pan(paper.view.center, event.delta.x, event.delta.y);
            paper.view.center = center;

            event.preventDefault();
        }

        // finish setup
        this.layers = {
            'wires': new this.canvas.Layer(),
            'nodes': new this.canvas.Layer(),
            'draw': new this.canvas.Layer()
        };

        this.wire = new diagram.wire(this);
    }

    /**
     * Helper method to create shallow copies of objects.
     *
     * @param   object      origin              Object to extend.
     * @param   object      add                 Object with properties to add.
     */
    diagram.prototype.extend = function(origin, add) {
        if (typeof add === 'object') {
            Object.keys(add).forEach(function(k) {
                origin[k] = add[k];
            });
        }

        return origin;
    };

    /**
     * Add a connector scope.
     *
     * @param   string      name                Name of scope.
     * @param   object      settings            Scope settings.
     */
    diagram.prototype.addScope = function(name, settings)
    {
        this.scopes[name] = settings;
    }

    /**
     * Test if scope is available.
     *
     * @param   string      name                Name of scope.
     * @return  bool                            Returns true if scope is available.
     */
    diagram.prototype.hasScope = function(name)
    {
        return (name in this.scopes);
    }

    /**
     * Return scope settings.
     *
     * @param   string      name                Name of scope.
     * @return  object                          Scope settings.
     */
    diagram.prototype.getScope = function(name)
    {
        return this.scopes[name];
    }

    /**
     * Return and activate layer of specified name. Returns the canvas layer, if no layer name
     * is specified.
     *
     * @param   string      name                Name of layer to get.
     * @return  node                            Layer node.
     */
    diagram.prototype.getLayer = function(name)
    {
        var layer = (typeof name !== 'undefined'
                        ? this.layers[name]
                        : this.canvas);

        layer.activate();

        return layer;
    }

    /**
     * Return wires.
     *
     * @return  array                           Array ofwires.
     */
    diagram.prototype.exportWires = function()
    {
        return this.wire.exportWires();
    }

    /**
     * Export nodes.
     *
     * @return  array                           Array of nodes.
     */
    diagram.prototype.exportNodes = function()
    {
        return this.nodes.map(function(node) {
            return node.getData();
        });
    }

    /**
     * Add multiple nodes.
     *
     * @param   array       nodes               Array of nodes.
     */
    diagram.prototype.addNodes = function(nodes)
    {
        nodes.forEach(function(data) {
            this.addNode(data);
        }, this);
    }

    /**
     * Add a single node to diagram.
     *
     * @param   object      data                Node data.
     */
    diagram.prototype.addNode = function(data)
    {
        var node = new node_types[data.node](this, data);

        this.nodes.push(node);
    }

    /**
     * Add multiple wires.
     *
     * @param   array       wires               Array of wires.
     */
    diagram.prototype.addWires = function(wires)
    {
        this.wires = this.wires.concat(wires);
    }

    /**
     * Add a single wire to diagram.
     *
     * @param   object      wire                Wire to add.
     */
    diagram.prototype.addWire = function(wire)
    {
        this.wires.push(wire);
    }

    /**
     * Remove node of the specified ID.
     *
     * @param   string      id                  ID of node to remove.
     */
    diagram.prototype.removeNode = function(id)
    {
        this.nodes = this.nodes.filter(function(node) {
            var ret = true;

            if (node.getId() == id) {
                node.destroy();

                ret = false;
            }

            return ret;
        });
    }

    /**
     * Render diagram and wire all nodes as specified.
     */
    diagram.prototype.render = function()
    {
        // determine if dagre should be used for layouting
        var use_dagre = false;

        if (typeof dagre !== 'undefined') {
            for (var i = 0, rect = null, cnt = this.nodes.length; i < cnt; ++i) {
                rect = this.nodes[i].getRect();

                if ((use_dagre = (rect.x === null || rect.y === null))) {
                    break;
                }
            }
        }

        if (use_dagre) {
            // render nodes with calculated graph layout using dagre library
            var g = new dagre.graphlib.Graph();

            g.setGraph({'rankdir': 'LR'});
            g.setDefaultEdgeLabel(function() { return {}; });

            this.nodes.forEach(function(node) {
                var rect = node.getRect();

                g.setNode(node.getId(), {'width': rect.width, 'height': rect.height});
            });

            this.wires.forEach(function(wire) {
                var source = this.wire.registry[wire.source];
                var target = this.wire.registry[wire.target];

                g.setEdge(source.getNode().getId(), target.getNode().getId());
            }, this);

            dagre.layout(g);

            this.nodes.forEach(function(node) {
                var rect = g.node(node.getId());

                node.render({'x': rect.x, 'y': rect.y});
            }, this);
        } else {
            // render without using dagre
            this.nodes.forEach(function(node) {
                node.render();
            });
        }

        // render wires
        this.wires.forEach(function(wire) {
            this.wire.addWire(wire.source, wire.target);
        }, this);

        //
        paper.view.draw();
    }

    return diagram;
})();
<|endoftext|>