<commit_before>/* global require, module */

var os = require('os');
var _ = require('lodash');
var mdd = require('mddata');
var textTransformation = require('gulp-text-simple');

var __debug = function (msg) {
	// console.log('# ' + msg);
};

var isText = _.isString;
var isNode = function (x) { return x && isText(x.name); };
var isArray = _.isArray;
var isContainer = function (x) { return x && isArray(x.children); };
var isNodeWithChildren = function (x) { return x && isNode(x) && isContainer(x); };

var formatData = function (data) {
	'use strict';
	if (isText(data)) {
		return 'Text(' + data + ')';
	} else if (isArray(data)) {
		return 'Array (' + _.size(data) + ')';
	} else if (isNodeWithChildren(data)) {
		return 'Node(' + data.name + ', ' + _.size(data) + ')';
	} else if (isNode(data)) {
		return 'Node(' + data.name + ')';
	} else {
		return 'Unknown';
	}
};

var parsePath = function (path) {
	'use strict';
	if (typeof(path) === 'string') {
		return path.split('/');
	} else {
		return path;
	}
};

var buildCriterium = function (pathPart) {
	'use strict';
	var typTest = function (t) { return true; };
	var separator = pathPart.indexOf(':');
	if (separator > -1) {
		var typ = pathPart.slice(0, separator);
		typTest = function (t) { return typ === '' || t === typ; };
		pathPart = pathPart.slice(separator + 1);
	}

	var regex = new RegExp(
		'^' + _.escapeRegExp(pathPart).
		replace(/\\\*/g, '.*').replace(/\\\?/g, '.') + '$');
	return function (x) {
		if (isText(x)) {
			return regex.test(x);
		} else if (isNode(x)) {
			return typTest(x.typ) && regex.test(x.name);
		} else {
			return false;
		}
	};
};

var formatResult = function (selection) {
	'use strict';
	if (!selection) {
		return null;
	}
	var path = selection[0];
	var node = selection[1];
	if (isText(node)) {
		return { name: node, path: path };
	} else if (isNode(node)) {
		var result = _.omit(node, 'children');
		result.path = path;
		return result;
	} else {
		return null;
	}
};

var extendResultList = function (resultList, additionals) {
	'use strict';
	function arraysEqual(a, b) {
		if (a === b) { return true; }
		if (a === null || b === null) {	return false; }
		if (a.length !== b.length) { return false; }
		for (var i = 0; i < a.length; ++i) {
			if (a[i] !== b[i]) { return false; }
		}
		return true;
	}
	_.forEach(additionals, function (x) {
		if (_.every(resultList, function (y) {
			return !arraysEqual(x.path, y.path);
		})) {
			resultList.push(x);
		}
	});
};

var resolvePath = function (coll, path) {
	'use strict';
	if (_.size(path) === 0) {
		return null;
	}
	var n = { children: coll };
	_.forEach(path, function (i) {
		n = n.children[i];
	});
	if (isText(n)) {
		n = { name: n };
	}
	return n;
};

var globPath = function globPath(coll, queryPath, absolute, path, result) {
	'use strict';
	if (isNodeWithChildren(coll)) {
		coll = coll.children;
	}
	if (!isArray(coll)) {
		__debug('GLOB ERROR Invalid input: ' + coll);
		return null;
	}
	__debug('GLOB ' + formatData(coll));
	__debug('GLOB Path (absolute=' + absolute + '): ' + queryPath);
	if (queryPath[0] === '**') {
		return globPath(coll, _.drop(queryPath), false, path, result);
	}
	path = path || [];
	result = result || [];
	var criterium = buildCriterium(queryPath[0]);
	var selections = [];
	_.forEach(coll, function (n, i) {
		if (criterium(n)) {
			__debug('GLOB Select ' + i + ': ' + formatData(n));
			selections.push([path.concat([i]), n]);
		}
	});
	if (_.size(queryPath) == 1) {
		extendResultList(result, _.map(selections, formatResult));
	} else if (_.size(queryPath) > 1) {
		_.forEach(selections, function (s) {
			var p = s[0];
			var e = s[1];
			if (e.children) {
				result = globPath(e.children, _.drop(queryPath),
					true, p, result);
			}
		});
	}
	if (!absolute) {
		_.forEach(coll, function (n, i) {
			if (isNodeWithChildren(n)) {
				result = globPath(n.children, queryPath, false,
					path.concat([i]), result);
			}
		});
	}
	return result;
};

var findNodes = function (coll, selector) {
	'use strict';
	var queryPath = parsePath(selector);
	if (queryPath.size === 0) {
		return null;
	}
	var absolute = queryPath[0] === '';
	if (absolute) {
		queryPath = _.drop(queryPath, 1);
	}
	return globPath(coll, queryPath, absolute);
};

var firstMatchingChild = function (node, criterium) {
	'use strict';
	var nodesWithIndex = _.map(
		node.children,
		function (n, i) { return [i, n]; });
	var matchingNodesWithIndex = _.filter(
		nodesWithIndex,
		function (r) { return criterium(r[1]); });
	return _.first(matchingNodesWithIndex);
};

var findRelativeNode = function (coll, refPath, selector) {
	'use strict';
	var queryPath = parsePath(selector);
	refPath = _.clone(refPath);
	var n = resolvePath(coll, refPath);
	__debug('FIND RELATIVE ROOT ' + formatData(n));
	_.forEach(queryPath, function (q) {
		__debug('FIND RELATIVE POS ' + refPath + ': ' + q);
		if (q === '' || q === '.') {
			__debug('FIND RELATIVE skip');
			return;
		} else if (q === '..') {
			__debug('FIND RELATIVE parent');
			refPath = _.dropRight(refPath);
			n = resolvePath(coll, refPath);
		} else {
			var r = firstMatchingChild(n, buildCriterium(q));
			if (r) {
				__debug('FIND RELATIVE children: ' + q + ' -> ' + r[1].name);
				n = r[1];
				refPath.push(r[0]);
			} else {
				__debug('FIND RELATIVE children: ' + q + ' NOT FOUND');
			}
		}
	});
	return n;
};

var cellFromNode = function(node, attribute) {
	'use strict';
	if (!node) {
		return null;
	}
	if (attribute === 'name') {
		var cell = { text: node.name };
		if (node.anchor) {
			cell.href = '#' + node.anchor;
		}
		return cell;
	}
	if (attribute === 'value') {
		return { text: node.value ? node.value : null };
	}
	return { text: 'unknown attribute: ' + attribute };
};

var table = function (data, query) {
	'use strict';
	var nodes = findNodes(data, query.selector);
	if (isArray(query.columns)) {
		return {
			columns: _.map(query.columns, function (col) {
				return { text: col.name };
			}),
			rows: _.map(nodes, function (node) {
				return _.map(query.columns, function (col) {
					var cellNode = findRelativeNode(
						data, node.path, col.selector);
					return cellFromNode(cellNode, col.attribute);
				});
			})
		};
	} else {
		if (_.every(nodes, function (n) { return n.value === undefined; })) {
			return {
				columns: [{ text: 'Name' }],
				rows: _.map(nodes, function (n) {
					var cell = { text: n.name };
					if (n.anchor) {
						cell.href = '#' + n.anchor;
					}
					return [cell];
				})
			};
		} else {
			return {
				columns: [
					{ text: 'Name' },
					{ text: 'Value' }
				],
				rows: _.map(nodes, function (n) {
					var nameCell = { text: n.name };
					if (n.anchor) {
						nameCell.href = '#' + n.anchor;
					}
					var valueCell = { text: n.value ? n.value : null };
					return [nameCell, valueCell];
				})
			};
		}
	}
};

var formatMarkdownCell = function (cell) {
	'use strict';
	if (!cell) {
		return '';
	}
	if (cell.href) {
		return '[' + cell.text + '](' + cell.href + ')';
	}
	return cell.text;
};

var formatMarkdownList = function (table) {
	'use strict';
	var md = '';
	var rowCnt = _.size(table.rows);
	if (_.size(table.columns) === 1) {
		_.forEach(table.rows, function (row, i) {
			md += '* ' + formatMarkdownCell(row[0]);
			if (i < rowCnt - 1) {
				md += os.EOL;
			}
		});
	} else if (_.size(table.columns) > 1) {
		_.forEach(table.rows, function (row, i) {
			md += '* ' + formatMarkdownCell(row[0]);
			var value = formatMarkdownCell(row[1]);
			if (value) {
				md += ': ' + value;
			}
			if (i < rowCnt - 1) {
				md += os.EOL;
			}
		});
	}
	return md;
};

var formatMarkdownTable = function (table) {
	'use strict';
	var md = '| ' +
		_.map(table.columns,
			function (column) {
				return column.text;
			})
			.join(' | ') + ' |' + os.EOL;
	md += '|' +
		_.map(table.columns,
			function (column) {
				return _.repeat('-', _.size(column.text) + 2);
			})
			.join('|') + '|' + os.EOL;
	var rowCnt = _.size(table.rows);
	_.forEach(table.rows, function (cells, i) {
		md += '| ' +
			_.map(cells, formatMarkdownCell)
				.join(' | ') + ' |';
		if (i < rowCnt - 1) {
			md += os.EOL;
		}
	});
	return md;
};

var transformText = function (text) {
	'use strict';
	var parseColumns = function (spec) {
		var lines = _.map(spec.split('\n'),
			function (l) { return _.trim(l); });
		var colRegex = /^#column\s+([^:]+?)\s*:\s+([^\s\(]+)\((.*?)\)$/;
		var columns = _.map(lines, function (l) {
			var m = colRegex.exec(l);
			if (m) {
				return { name: m[1], attribute: m[2], selector: m[3] };
			} else {
				return null;
			}
		});
		return _.filter(columns);
	};
	var data = mdd(text);
	text = text.replace(
		/<!--\s+#data-list\s+([^\n]*?)\s+-->/gm,
		function (m, s) {
			return formatMarkdownList(table(data, { selector: s }));
		});
	text = text.replace(
		/<!--\s+#data-table\s+([^\n]*?)\s+-->/gm,
		function (m, s) {
			return formatMarkdownTable(table(data, { selector: s }));
		});
	text = text.replace(
		/<!--\s+#data-table\s+([^\n]*?)\s*\n\s*([\s\S]*?)\s+-->/gm,
		function (m, s, c) {
			return formatMarkdownTable(table(data,
				{ selector: s, columns: parseColumns(c) }));
		});
	text = text.replace(
		/<!--\s+#data-list\s+([^\n]*?)\s*\n\s*([\s\S]*?)\s+-->/gm,
		function (m, s, c) {
			return formatMarkdownList(table(data,
				{ selector: s, columns: parseColumns(c) }));
		});
	return text;
};

module.exports = {
	select: findNodes,
	query: table,
	formatMarkdownTable: formatMarkdownTable,
	formatMarkdownList: formatMarkdownList,
	transform: textTransformation(transformText),
};
<commit_msg>code cleanup<commit_after>/* global require, module */

var os = require('os');
var _ = require('lodash');
var mdd = require('mddata');
var textTransformation = require('gulp-text-simple');

var __debug = function (msg) {
	// console.log('# ' + msg);
};

var isText = _.isString;
var isNode = function (x) { return x && isText(x.name); };
var isArray = _.isArray;
var isContainer = function (x) { return x && isArray(x.children); };
var isNodeWithChildren = function (x) { return x && isNode(x) && isContainer(x); };

var formatData = function (data) {
	'use strict';
	if (isText(data)) {
		return 'Text(' + data + ')';
	} else if (isArray(data)) {
		return 'Array (' + _.size(data) + ')';
	} else if (isNodeWithChildren(data)) {
		return 'Node(' + data.name + ', ' + _.size(data) + ')';
	} else if (isNode(data)) {
		return 'Node(' + data.name + ')';
	} else {
		return 'Unknown';
	}
};

var parsePath = function (path) {
	'use strict';
	if (typeof(path) === 'string') {
		return path.split('/');
	} else {
		return path;
	}
};

var buildCriterium = function (pathPart) {
	'use strict';
	var typTest = function (t) { return true; };
	var separator = pathPart.indexOf(':');
	if (separator > -1) {
		var typ = pathPart.slice(0, separator);
		typTest = function (t) { return typ === '' || t === typ; };
		pathPart = pathPart.slice(separator + 1);
	}

	var regex = new RegExp(
		'^' + _.escapeRegExp(pathPart).
		replace(/\\\*/g, '.*').replace(/\\\?/g, '.') + '$');
	return function (x) {
		if (isText(x)) {
			return regex.test(x);
		} else if (isNode(x)) {
			return typTest(x.typ) && regex.test(x.name);
		} else {
			return false;
		}
	};
};

var formatResult = function (selection) {
	'use strict';
	if (!selection) {
		return null;
	}
	var path = selection[0];
	var node = selection[1];
	if (isText(node)) {
		return { name: node, path: path };
	} else if (isNode(node)) {
		var result = _.omit(node, 'children');
		result.path = path;
		return result;
	} else {
		return null;
	}
};

var extendResultList = function (resultList, additionals) {
	'use strict';
	function arraysEqual(a, b) {
		if (a === b) { return true; }
		if (a === null || b === null) {	return false; }
		if (a.length !== b.length) { return false; }
		for (var i = 0; i < a.length; ++i) {
			if (a[i] !== b[i]) { return false; }
		}
		return true;
	}
	_.forEach(additionals, function (x) {
		if (_.every(resultList, function (y) {
			return !arraysEqual(x.path, y.path);
		})) {
			resultList.push(x);
		}
	});
};

var resolvePath = function (coll, path) {
	'use strict';
	if (_.size(path) === 0) {
		return null;
	}
	var n = { children: coll };
	_.forEach(path, function (i) {
		n = n.children[i];
	});
	if (isText(n)) {
		n = { name: n };
	}
	return n;
};

var globPath = function globPath(coll, queryPath, absolute, path, result) {
	'use strict';
	if (isNodeWithChildren(coll)) {
		coll = coll.children;
	}
	if (!isArray(coll)) {
		__debug('GLOB ERROR Invalid input: ' + coll);
		return null;
	}
	__debug('GLOB ' + formatData(coll));
	__debug('GLOB Path (absolute=' + absolute + '): ' + queryPath);
	if (queryPath[0] === '**') {
		return globPath(coll, _.drop(queryPath), false, path, result);
	}
	path = path || [];
	result = result || [];
	var criterium = buildCriterium(queryPath[0]);
	var selections = [];
	_.forEach(coll, function (n, i) {
		if (criterium(n)) {
			__debug('GLOB Select ' + i + ': ' + formatData(n));
			selections.push([path.concat([i]), n]);
		}
	});
	if (_.size(queryPath) === 1) {
		extendResultList(result, _.map(selections, formatResult));
	} else if (_.size(queryPath) > 1) {
		_.forEach(selections, function (s) {
			var p = s[0];
			var e = s[1];
			if (e.children) {
				result = globPath(e.children, _.drop(queryPath),
					true, p, result);
			}
		});
	}
	if (!absolute) {
		_.forEach(coll, function (n, i) {
			if (isNodeWithChildren(n)) {
				result = globPath(n.children, queryPath, false,
					path.concat([i]), result);
			}
		});
	}
	return result;
};

var findNodes = function (coll, selector) {
	'use strict';
	var queryPath = parsePath(selector);
	if (queryPath.size === 0) {
		return null;
	}
	var absolute = queryPath[0] === '';
	if (absolute) {
		queryPath = _.drop(queryPath, 1);
	}
	return globPath(coll, queryPath, absolute);
};

var firstMatchingChild = function (node, criterium) {
	'use strict';
	var nodesWithIndex = _.map(
		node.children,
		function (n, i) { return [i, n]; });
	var matchingNodesWithIndex = _.filter(
		nodesWithIndex,
		function (r) { return criterium(r[1]); });
	return _.first(matchingNodesWithIndex);
};

var findRelativeNode = function (coll, refPath, selector) {
	'use strict';
	var queryPath = parsePath(selector);
	refPath = _.clone(refPath);
	var n = resolvePath(coll, refPath);
	__debug('FIND RELATIVE ROOT ' + formatData(n));
	_.forEach(queryPath, function (q) {
		__debug('FIND RELATIVE POS ' + refPath + ': ' + q);
		if (q === '' || q === '.') {
			__debug('FIND RELATIVE skip');
			return;
		} else if (q === '..') {
			__debug('FIND RELATIVE parent');
			refPath = _.dropRight(refPath);
			n = resolvePath(coll, refPath);
		} else {
			var r = firstMatchingChild(n, buildCriterium(q));
			if (r) {
				__debug('FIND RELATIVE children: ' + q + ' -> ' + r[1].name);
				n = r[1];
				refPath.push(r[0]);
			} else {
				__debug('FIND RELATIVE children: ' + q + ' NOT FOUND');
			}
		}
	});
	return n;
};

var cellFromNode = function(node, attribute) {
	'use strict';
	if (!node) {
		return null;
	}
	if (attribute === 'name') {
		var cell = { text: node.name };
		if (node.anchor) {
			cell.href = '#' + node.anchor;
		}
		return cell;
	}
	if (attribute === 'value') {
		return { text: node.value ? node.value : null };
	}
	return { text: 'unknown attribute: ' + attribute };
};

var table = function (data, query) {
	'use strict';
	var nodes = findNodes(data, query.selector);
	if (isArray(query.columns)) {
		return {
			columns: _.map(query.columns, function (col) {
				return { text: col.name };
			}),
			rows: _.map(nodes, function (node) {
				return _.map(query.columns, function (col) {
					var cellNode = findRelativeNode(
						data, node.path, col.selector);
					return cellFromNode(cellNode, col.attribute);
				});
			})
		};
	} else {
		if (_.every(nodes, function (n) { return n.value === undefined; })) {
			return {
				columns: [{ text: 'Name' }],
				rows: _.map(nodes, function (n) {
					var cell = { text: n.name };
					if (n.anchor) {
						cell.href = '#' + n.anchor;
					}
					return [cell];
				})
			};
		} else {
			return {
				columns: [
					{ text: 'Name' },
					{ text: 'Value' }
				],
				rows: _.map(nodes, function (n) {
					var nameCell = { text: n.name };
					if (n.anchor) {
						nameCell.href = '#' + n.anchor;
					}
					var valueCell = { text: n.value ? n.value : null };
					return [nameCell, valueCell];
				})
			};
		}
	}
};

var formatMarkdownCell = function (cell) {
	'use strict';
	if (!cell) {
		return '';
	}
	if (cell.href) {
		return '[' + cell.text + '](' + cell.href + ')';
	}
	return cell.text;
};

var formatMarkdownList = function (table) {
	'use strict';
	var md = '';
	var rowCnt = _.size(table.rows);
	if (_.size(table.columns) === 1) {
		_.forEach(table.rows, function (row, i) {
			md += '* ' + formatMarkdownCell(row[0]);
			if (i < rowCnt - 1) {
				md += os.EOL;
			}
		});
	} else if (_.size(table.columns) > 1) {
		_.forEach(table.rows, function (row, i) {
			md += '* ' + formatMarkdownCell(row[0]);
			var value = formatMarkdownCell(row[1]);
			if (value) {
				md += ': ' + value;
			}
			if (i < rowCnt - 1) {
				md += os.EOL;
			}
		});
	}
	return md;
};

var formatMarkdownTable = function (table) {
	'use strict';
	var md = '| ' +
		_.map(table.columns,
			function (column) {
				return column.text;
			})
			.join(' | ') + ' |' + os.EOL;
	md += '|' +
		_.map(table.columns,
			function (column) {
				return _.repeat('-', _.size(column.text) + 2);
			})
			.join('|') + '|' + os.EOL;
	var rowCnt = _.size(table.rows);
	_.forEach(table.rows, function (cells, i) {
		md += '| ' +
			_.map(cells, formatMarkdownCell)
				.join(' | ') + ' |';
		if (i < rowCnt - 1) {
			md += os.EOL;
		}
	});
	return md;
};

var transformText = function (text) {
	'use strict';
	var parseColumns = function (spec) {
		var lines = _.map(spec.split('\n'),
			function (l) { return _.trim(l); });
		var colRegex = /^#column\s+([^:]+?)\s*:\s+([^\s\(]+)\((.*?)\)$/;
		var columns = _.map(lines, function (l) {
			var m = colRegex.exec(l);
			if (m) {
				return { name: m[1], attribute: m[2], selector: m[3] };
			} else {
				return null;
			}
		});
		return _.filter(columns);
	};
	var data = mdd(text);
	text = text.replace(
		/<!--\s+#data-list\s+([^\n]*?)\s+-->/gm,
		function (m, s) {
			return formatMarkdownList(table(data, { selector: s }));
		});
	text = text.replace(
		/<!--\s+#data-table\s+([^\n]*?)\s+-->/gm,
		function (m, s) {
			return formatMarkdownTable(table(data, { selector: s }));
		});
	text = text.replace(
		/<!--\s+#data-table\s+([^\n]*?)\s*\n\s*([\s\S]*?)\s+-->/gm,
		function (m, s, c) {
			return formatMarkdownTable(table(data,
				{ selector: s, columns: parseColumns(c) }));
		});
	text = text.replace(
		/<!--\s+#data-list\s+([^\n]*?)\s*\n\s*([\s\S]*?)\s+-->/gm,
		function (m, s, c) {
			return formatMarkdownList(table(data,
				{ selector: s, columns: parseColumns(c) }));
		});
	return text;
};

module.exports = {
	select: findNodes,
	query: table,
	formatMarkdownTable: formatMarkdownTable,
	formatMarkdownList: formatMarkdownList,
	transform: textTransformation(transformText),
};
<|endoftext|>


<commit_before>var registerComponent = require('../core/component').registerComponent;
var THREE = require('../lib/three');

var scaleDummy = new THREE.Vector3();

/**
 * Raycaster component.
 *
 * Pass options to three.js Raycaster including which objects to test.
 * Poll for intersections.
 * Emit event on origin entity and on target entity on intersect.
 *
 * @member {array} intersectedEls - List of currently intersected entities.
 * @member {array} objects - Cached list of meshes to intersect.
 * @member {number} prevCheckTime - Previous time intersection was checked. To help interval.
 * @member {object} raycaster - three.js Raycaster.
 */
module.exports.Component = registerComponent('raycaster', {
  schema: {
    far: {default: Infinity}, // Infinity.
    interval: {default: 100},
    near: {default: 0},
    objects: {default: ''},
    recursive: {default: true}
  },

  init: function () {
    this.direction = new THREE.Vector3();
    this.intersectedEls = [];
    this.objects = null;
    this.prevCheckTime = undefined;
    this.raycaster = new THREE.Raycaster();
    this.updateOriginDirection();
  },

  /**
   * Create or update raycaster object.
   */
  update: function () {
    var data = this.data;
    var raycaster = this.raycaster;

    // Set raycaster properties.
    raycaster.far = data.far;
    raycaster.near = data.near;

    this.refreshObjects();
  },

  /**
   * Update list of objects to test for intersection.
   */
  refreshObjects: function () {
    var data = this.data;
    var i;
    var objectEls;

    // Push meshes onto list of objects to intersect.
    if (data.objects) {
      objectEls = this.el.sceneEl.querySelectorAll(data.objects);
      this.objects = [];
      for (i = 0; i < objectEls.length; i++) {
        this.objects.push(objectEls[i].object3D);
      }
      return;
    }

    // If objects not defined, intersect with everything.
    this.objects = this.el.sceneEl.object3D.children;
  },

  /**
   * Check for intersections and cleared intersections on an interval.
   */
  tick: function (time) {
    var el = this.el;
    var data = this.data;
    var prevIntersectedEls = this.intersectedEls.slice();
    var intersectedEls = this.intersectedEls = [];  // Reset intersectedEls.
    var intersections;
    var prevCheckTime = this.prevCheckTime;

    // Only check for intersection if interval time has passed.
    if (prevCheckTime && (time - prevCheckTime < data.interval)) { return; }

    // Raycast.
    this.updateOriginDirection();
    intersections = this.raycaster.intersectObjects(this.objects, data.recursive);

    // Update intersectedEls object first in case event handlers try to inspect it.
    intersections.forEach(function emitEvents (intersection) {
      intersectedEls.push(intersection.object.el);
    });

    // Emit intersected on intersected entity per intersected entity.
    intersections.forEach(function emitEvents (intersection) {
      var intersectedEl = intersection.object.el;
      if (!intersectedEl) { return; }
      intersectedEl.emit('raycaster-intersected', {el: el, intersection: intersection});
    });

    // Emit all intersections at once on raycasting entity.
    if (intersections.length) {
      el.emit('raycaster-intersection', {
        els: intersections.map(function getEl (intersection) {
          return intersection.object.el;
        }),
        intersections: intersections
      });
    }

    // Emit intersection cleared on both entities per formerly intersected entity.
    prevIntersectedEls.forEach(function checkStillIntersected (intersectedEl) {
      if (intersectedEls.indexOf(intersectedEl) !== -1) { return; }
      el.emit('raycaster-intersection-cleared', {el: intersectedEl});
      intersectedEl.emit('raycaster-intersected-cleared', {el: el});
    });
  },

  /**
   * Set origin and direction of raycaster using entity position and rotation.
   */
  updateOriginDirection: (function () {
    var directionHelper = new THREE.Quaternion();
    var originVec3 = new THREE.Vector3();

    // Closure to make quaternion/vector3 objects private.
    return function updateOriginDirection () {
      var el = this.el;
      var object3D = el.object3D;

      // Update matrix world.
      object3D.updateMatrixWorld();
      // Grab the position and rotation.
      object3D.matrixWorld.decompose(originVec3, directionHelper, scaleDummy);
      // Apply rotation to a 0, 0, -1 vector.
      this.direction.set(0, 0, -1);
      this.direction.applyQuaternion(directionHelper);

      this.raycaster.set(originVec3, this.direction);
    };
  })()
});
<commit_msg>filter out intersection objects that have no ref to entity (#1698)<commit_after>var registerComponent = require('../core/component').registerComponent;
var THREE = require('../lib/three');

var scaleDummy = new THREE.Vector3();

/**
 * Raycaster component.
 *
 * Pass options to three.js Raycaster including which objects to test.
 * Poll for intersections.
 * Emit event on origin entity and on target entity on intersect.
 *
 * @member {array} intersectedEls - List of currently intersected entities.
 * @member {array} objects - Cached list of meshes to intersect.
 * @member {number} prevCheckTime - Previous time intersection was checked. To help interval.
 * @member {object} raycaster - three.js Raycaster.
 */
module.exports.Component = registerComponent('raycaster', {
  schema: {
    far: {default: Infinity}, // Infinity.
    interval: {default: 100},
    near: {default: 0},
    objects: {default: ''},
    recursive: {default: true}
  },

  init: function () {
    this.direction = new THREE.Vector3();
    this.intersectedEls = [];
    this.objects = null;
    this.prevCheckTime = undefined;
    this.raycaster = new THREE.Raycaster();
    this.updateOriginDirection();
  },

  /**
   * Create or update raycaster object.
   */
  update: function () {
    var data = this.data;
    var raycaster = this.raycaster;

    // Set raycaster properties.
    raycaster.far = data.far;
    raycaster.near = data.near;

    this.refreshObjects();
  },

  /**
   * Update list of objects to test for intersection.
   */
  refreshObjects: function () {
    var data = this.data;
    var i;
    var objectEls;

    // Push meshes onto list of objects to intersect.
    if (data.objects) {
      objectEls = this.el.sceneEl.querySelectorAll(data.objects);
      this.objects = [];
      for (i = 0; i < objectEls.length; i++) {
        this.objects.push(objectEls[i].object3D);
      }
      return;
    }

    // If objects not defined, intersect with everything.
    this.objects = this.el.sceneEl.object3D.children;
  },

  /**
   * Check for intersections and cleared intersections on an interval.
   */
  tick: function (time) {
    var el = this.el;
    var data = this.data;
    var intersectedEls;
    var intersections;
    var prevCheckTime = this.prevCheckTime;
    var prevIntersectedEls;

    // Only check for intersection if interval time has passed.
    if (prevCheckTime && (time - prevCheckTime < data.interval)) { return; }

    // Store old previously intersected entities.
    prevIntersectedEls = this.intersectedEls.slice();

    // Raycast.
    this.updateOriginDirection();
    intersections = this.raycaster.intersectObjects(this.objects, data.recursive);

    // Only keep intersections against objects that have a reference to an entity.
    intersections = intersections.filter(function hasEl (intersection) {
      return !!intersection.object.el;
    });

    // Update intersectedEls.
    intersectedEls = this.intersectedEls = intersections.map(function getEl (intersection) {
      return intersection.object.el;
    });

    // Emit intersected on intersected entity per intersected entity.
    intersections.forEach(function emitEvents (intersection) {
      var intersectedEl = intersection.object.el;
      intersectedEl.emit('raycaster-intersected', {el: el, intersection: intersection});
    });

    // Emit all intersections at once on raycasting entity.
    if (intersections.length) {
      el.emit('raycaster-intersection', {
        els: intersectedEls,
        intersections: intersections
      });
    }

    // Emit intersection cleared on both entities per formerly intersected entity.
    prevIntersectedEls.forEach(function checkStillIntersected (intersectedEl) {
      if (intersectedEls.indexOf(intersectedEl) !== -1) { return; }
      el.emit('raycaster-intersection-cleared', {el: intersectedEl});
      intersectedEl.emit('raycaster-intersected-cleared', {el: el});
    });
  },

  /**
   * Set origin and direction of raycaster using entity position and rotation.
   */
  updateOriginDirection: (function () {
    var directionHelper = new THREE.Quaternion();
    var originVec3 = new THREE.Vector3();

    // Closure to make quaternion/vector3 objects private.
    return function updateOriginDirection () {
      var el = this.el;
      var object3D = el.object3D;

      // Update matrix world.
      object3D.updateMatrixWorld();
      // Grab the position and rotation.
      object3D.matrixWorld.decompose(originVec3, directionHelper, scaleDummy);
      // Apply rotation to a 0, 0, -1 vector.
      this.direction.set(0, 0, -1);
      this.direction.applyQuaternion(directionHelper);

      this.raycaster.set(originVec3, this.direction);
    };
  })()
});
<|endoftext|>


<commit_before>import assign from 'object-assign';
import pascalCase from 'pascal-case';
import React from 'react';
import ReactDOM from 'react-dom';

const defaults = {
  React,
  ReactDOM,
};

function syncEvent(node, eventName, newEventHandler) {
  const eventNameLc = eventName[0].toLowerCase() + eventName.substring(1);
  const eventStore = node.__events || (node.__events = {});
  const oldEventHandler = eventStore[eventNameLc];

  // Remove old listener so they don't double up.
  if (oldEventHandler) {
    node.removeEventListener(eventNameLc, oldEventHandler);
  }

  // Bind new listener.
  if (newEventHandler) {
    node.addEventListener(eventNameLc, eventStore[eventNameLc] = function handler(e) {
      newEventHandler.call(this, e);
    });
  }
}

export default function (CustomElement, opts) {
  opts = assign(defaults, opts);
  const tagName = (new CustomElement()).tagName;
  const displayName = pascalCase(tagName);
  const { React, ReactDOM } = opts;

  if (!React || !ReactDOM) {
    throw new Error('React and ReactDOM must be dependencies, globally on your `window` object or passed via opts.');
  }

  class ReactComponent extends React.Component {
    static get displayName() {
      return displayName;
    }
    static get propTypes() {
      return {
        children: React.PropTypes.any,
        style: React.PropTypes.any,
      };
    }
    componentDidMount() {
      this.componentWillReceiveProps(this.props);
    }
    componentWillReceiveProps(props) {
      const node = ReactDOM.findDOMNode(this);
      Object.keys(props).forEach(name => {
        if (name === 'children' || name === 'style') {
          return;
        }

        if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
          syncEvent(node, name.substring(2), props[name]);
        } else {
          node[name] = props[name];
        }
      });
    }
    render() {
      return React.createElement(tagName, { style: this.props.style }, this.props.children);
    }
  };

  const proto = CustomElement.prototype
  Object.keys(proto).forEach(prop => {
    if (typeof proto[prop] === 'function') {
      ReactComponent.prototype[prop] = proto[prop].bind(proto);
    }
  });

  return ReactComponent;
}
<commit_msg>refactor: eslint fixes<commit_after>import assign from 'object-assign';
import pascalCase from 'pascal-case';
import React from 'react';
import ReactDOM from 'react-dom';

const defaults = {
  React,
  ReactDOM,
};

function syncEvent(node, eventName, newEventHandler) {
  const eventNameLc = eventName[0].toLowerCase() + eventName.substring(1);
  const eventStore = node.__events || (node.__events = {});
  const oldEventHandler = eventStore[eventNameLc];

  // Remove old listener so they don't double up.
  if (oldEventHandler) {
    node.removeEventListener(eventNameLc, oldEventHandler);
  }

  // Bind new listener.
  if (newEventHandler) {
    node.addEventListener(eventNameLc, eventStore[eventNameLc] = function handler(e) {
      newEventHandler.call(this, e);
    });
  }
}

export default function (CustomElement, opts) {
  opts = assign(defaults, opts);
  const tagName = (new CustomElement()).tagName;
  const displayName = pascalCase(tagName);
  const { React, ReactDOM } = opts;

  if (!React || !ReactDOM) {
    throw new Error('React and ReactDOM must be dependencies, globally on your `window` object or passed via opts.');
  }

  class ReactComponent extends React.Component {
    static get displayName() {
      return displayName;
    }
    static get propTypes() {
      return {
        children: React.PropTypes.any,
        style: React.PropTypes.any,
      };
    }
    componentDidMount() {
      this.componentWillReceiveProps(this.props);
    }
    componentWillReceiveProps(props) {
      const node = ReactDOM.findDOMNode(this);
      Object.keys(props).forEach(name => {
        if (name === 'children' || name === 'style') {
          return;
        }

        if (name.indexOf('on') === 0 && name[2] === name[2].toUpperCase()) {
          syncEvent(node, name.substring(2), props[name]);
        } else {
          node[name] = props[name];
        }
      });
    }
    render() {
      return React.createElement(tagName, { style: this.props.style }, this.props.children);
    }
  }

  const proto = CustomElement.prototype;
  Object.keys(proto).forEach(prop => {
    if (typeof proto[prop] === 'function') {
      ReactComponent.prototype[prop] = proto[prop].bind(proto);
    }
  });

  return ReactComponent;
}
<|endoftext|>


<commit_before>const path = require('path');
const url = require('url');
const Promise = require('bluebird');
const postcss = require('postcss');
const merge = require('merge');
const gulp = require('gulp');
const gulpIconfont = require('gulp-iconfont');

const POSTCSS_PLUGIN_NAME = 'postcss-iconfont';

const ABSOLUTE_URL = /^\//;

function errorLog(msg) {
    console.error(msg);
}
function isFunction(s) {
    return typeof s === 'function';
}
function trimQuotationMarks(val) {
    return val.replace(/^['"]{0,1}/, '').replace(/['"]{0,1}$/, '');
}
function include(arr,obj) {
    return (arr.indexOf(obj) !== -1);
}
function noop () {}

function extractsSVG (root, options) {
    return new Promise(function (resolve, reject) {
        let queueMap = [];

        // for each font face rule
        root.walkAtRules('font-face', function (rule) {
            let item = {};

            rule.walkDecls('src', function (decl) {
                let matchStr = decl.value.match(/^url\(['"]{0,1}(.*\.svg)['"]{0,1}\)/);
                if (matchStr && matchStr[1]) {
                    item.url = matchStr[1];
                }
            });

            if (item.url) {
                // for each font-family declaration
                rule.walkDecls('font-family', function (decl) {
                    item.fontName = trimQuotationMarks(decl.value);
                });

                if (item.fontName) {
                    if (ABSOLUTE_URL.test(item.url)) {
                        item.path = path.resolve(options.basePath + item.url);
                    } else {
                        item.path = path.resolve(path.dirname(root.source.input.file), item.url);
                    }
                    queueMap.push(item);
                }
            }
        });

        resolve([queueMap]);
    });
}

function prepare (queueMap, options) {
    return Promise.reduce(queueMap, function (newQueueMap, queueItem) {
        // filter options for gulp-iconfont options with options
        let iconFontOptions = merge({
            fontName: queueItem.fontName,
            log: noop // disabled print log from svgfont2svgicons
        }, options);
        delete iconFontOptions.basePath;
        delete iconFontOptions.stylesheetPath;
        delete iconFontOptions.publishPath;
        delete iconFontOptions.hooks;

        newQueueMap.push({
            path: queueItem.path,
            url: queueItem.url,
            iconFontOptions: iconFontOptions
        });

        return Promise.resolve(newQueueMap);
    }, []).then(function (newQueueMap) {
        return [newQueueMap];
    });
}

function runGulpIconFont (queueMap) {
    return Promise.map(queueMap, function (queueItem) {
        return new Promise(function (resolve, reject) {
            let glyphsResult;
            gulp.src([queueItem.path])
                .pipe(gulpIconfont(queueItem.iconFontOptions))
                .on('error', function (err) {
                    reject(err);
                }).on('glyphs', function (glyphs, opts) {
                    glyphsResult = {glyphs, opts};
                })
                .pipe(gulp.dest(queueItem.iconFontOptions.outputPath))
                .on('end', function () {
                    if (glyphsResult) {
                        resolve(glyphsResult);
                    } else {
                        reject(new Error('file not found'));
                    }
                })
        });
    }).then(function (results) {
        return [results];
    });
}

function transformPOSIXSeparator(s) {
    return s.split(path.sep).join('/');
}

function createFontSrcDeclarationsWithEOT (stylesheetPath, options, result) {
    let fontUrl = path.relative(stylesheetPath, path.join(result.opts.outputPath, result.opts.fileName));
    fontUrl = url.resolve(options.publishPath, transformPOSIXSeparator(fontUrl));
    return 'url(\'' + fontUrl + '.eot\')';
}

function createFontSrcDeclarations(stylesheetPath, options, result) {
    let fontUrl = path.relative(stylesheetPath, path.join(result.opts.outputPath, result.opts.fileName));
    fontUrl = url.resolve(options.publishPath, transformPOSIXSeparator(fontUrl));
    let formats = result.opts.formats;

    let declarationArr = [];
    if (include(formats, 'eot')) {
        declarationArr.push('url(\'' + fontUrl + '.eot#iefix\') format(\'embedded-opentype\')');
    }
    if (include(formats, 'woff2')) {
        declarationArr.push('url(\'' + fontUrl + '.woff2\') format(\'woff2\')');
    }
    if (include(formats, 'woff')) {
        declarationArr.push('url(\'' + fontUrl + '.woff\') format(\'woff\')');
    }
    if (include(formats, 'svg')) {
        declarationArr.push('url(\'' + fontUrl + '.svg#' + result.opts.fontName + '\') format(\'svg\')');
    }
    return declarationArr.join(', \n       ');
}
function insertFontSrcDeclarations(rule, options, result) {
    const stylesheetPath = options.stylesheetPath || path.dirname(rule.parent.source.input.file);
    rule.walkDecls('src', function (decl) {
        // eot for < ie9
        if (include(result.opts.formats, 'eot')) {
            rule.insertBefore(decl, postcss.decl({
                prop: 'src',
                value: createFontSrcDeclarationsWithEOT(stylesheetPath, options, result)
            }));
        }

        rule.insertBefore(decl, postcss.decl({
            prop: 'src',
            value: createFontSrcDeclarations(stylesheetPath, options, result)
        }));

        decl.remove();
    });
}

/**
 * Insert glyphs css rules
 * @param rule
 * @param result
 */
function insertGlyphsRules(rule, result) {
    // Reverse order
    // make sure to insert the characters in ascending order
    let glyphs = result.glyphs.slice(0);
    let glyphLen = glyphs.length;
    while (glyphLen--) {
        let glyph = glyphs[glyphLen];
        let node = postcss.rule({
            selector: '.' + result.opts.fontName + '-' + glyph.name + ':before'
        });
        node.append(postcss.decl({
            prop: 'content',
            value: '\'\\' +
            glyph.unicode[0]
                .charCodeAt(0)
                .toString(16)
                .toUpperCase()
            + '\''
        }));

        rule.parent.insertAfter(rule, node);
    }

    let node = postcss.rule({
        selector: '[class^="' + result.opts.fontName + '-"], [class*=" ' + result.opts.fontName + '-"]'
    });
    [
        {prop: 'font-family', value: '\'' + result.opts.fontName + '\''},
        {prop: 'speak', value: 'none'},
        {prop: 'font-style', value: 'normal'},
        {prop: 'font-weight', value: 'normal'},
        {prop: 'font-variant', value: 'normal'},
        {prop: 'text-transform', value: 'none'},
        {prop: 'line-height', value: 1},
        {prop: '-webkit-font-smoothing', value: 'antialiased'},
        {prop: '-moz-osx-font-smoothing', value: 'grayscale'}
    ].forEach(function (item) {
        node.append(postcss.decl({
            prop: item.prop,
            value: item.value
        }));
    });

    rule.parent.insertAfter(rule, node);
}

function updateRule(root, options, results) {
    if (Array.isArray(results) && results.length) {
        results.forEach(function (item) {
            root.walkAtRules('font-face', function (rule) {
                let isInsert = false;
                rule.walkDecls('font-family', function (decl) {
                    if (trimQuotationMarks(decl.value) === item.opts.fontName) {
                        isInsert = true;
                    }
                });

                if (isInsert) {
                    insertFontSrcDeclarations(rule, options, item);
                    insertGlyphsRules(rule, item);

                    return false;
                }
            });

            if (isFunction(options.hooks.onUpdateRule)) {
                options.hooks.onUpdateRule(root, options, item);
            }
        });
    }
    return Promise.resolve([root, results]);
}

function iconFontPlugin (options) {
    // merge options
    options = merge({
        basePath: './',
        stylesheetPath: null,
        outputPath: './',
        publishPath: '',

        formats: ['svg', 'ttf', 'eot', 'woff'],
        hooks: {
            onUpdateRule: null
        }
    }, (options || {}));

    return function (root, result) {
        return extractsSVG(root, options)
            .spread(function (queueMap) {
                return prepare(queueMap, options);
            }).spread(function (queueMap) { // run gulp-iconfont generate fonts.
                return runGulpIconFont(queueMap);
            }).spread(function (results) { // update css rule.
                return updateRule(root, options, results);
            }).catch(function (err) {
                errorLog(POSTCSS_PLUGIN_NAME + ': An error occurred while processing files - ' + err.message);
                errorLog(err.stack);
                throw err;
            });
    };
}
module.exports = postcss.plugin(POSTCSS_PLUGIN_NAME, iconFontPlugin);
<commit_msg>fix: make up TTF font type add to css declaration<commit_after>const path = require('path');
const url = require('url');
const Promise = require('bluebird');
const postcss = require('postcss');
const merge = require('merge');
const gulp = require('gulp');
const gulpIconfont = require('gulp-iconfont');

const POSTCSS_PLUGIN_NAME = 'postcss-iconfont';

const ABSOLUTE_URL = /^\//;

function errorLog(msg) {
    console.error(msg);
}
function isFunction(s) {
    return typeof s === 'function';
}
function trimQuotationMarks(val) {
    return val.replace(/^['"]{0,1}/, '').replace(/['"]{0,1}$/, '');
}
function include(arr,obj) {
    return (arr.indexOf(obj) !== -1);
}
function noop () {}

function extractsSVG (root, options) {
    return new Promise(function (resolve, reject) {
        let queueMap = [];

        // for each font face rule
        root.walkAtRules('font-face', function (rule) {
            let item = {};

            rule.walkDecls('src', function (decl) {
                let matchStr = decl.value.match(/^url\(['"]{0,1}(.*\.svg)['"]{0,1}\)/);
                if (matchStr && matchStr[1]) {
                    item.url = matchStr[1];
                }
            });

            if (item.url) {
                // for each font-family declaration
                rule.walkDecls('font-family', function (decl) {
                    item.fontName = trimQuotationMarks(decl.value);
                });

                if (item.fontName) {
                    if (ABSOLUTE_URL.test(item.url)) {
                        item.path = path.resolve(options.basePath + item.url);
                    } else {
                        item.path = path.resolve(path.dirname(root.source.input.file), item.url);
                    }
                    queueMap.push(item);
                }
            }
        });

        resolve([queueMap]);
    });
}

function prepare (queueMap, options) {
    return Promise.reduce(queueMap, function (newQueueMap, queueItem) {
        // filter options for gulp-iconfont options with options
        let iconFontOptions = merge({
            fontName: queueItem.fontName,
            log: noop // disabled print log from svgfont2svgicons
        }, options);
        delete iconFontOptions.basePath;
        delete iconFontOptions.stylesheetPath;
        delete iconFontOptions.publishPath;
        delete iconFontOptions.hooks;

        newQueueMap.push({
            path: queueItem.path,
            url: queueItem.url,
            iconFontOptions: iconFontOptions
        });

        return Promise.resolve(newQueueMap);
    }, []).then(function (newQueueMap) {
        return [newQueueMap];
    });
}

function runGulpIconFont (queueMap) {
    return Promise.map(queueMap, function (queueItem) {
        return new Promise(function (resolve, reject) {
            let glyphsResult;
            gulp.src([queueItem.path])
                .pipe(gulpIconfont(queueItem.iconFontOptions))
                .on('error', function (err) {
                    reject(err);
                }).on('glyphs', function (glyphs, opts) {
                    glyphsResult = {glyphs, opts};
                })
                .pipe(gulp.dest(queueItem.iconFontOptions.outputPath))
                .on('end', function () {
                    if (glyphsResult) {
                        resolve(glyphsResult);
                    } else {
                        reject(new Error('file not found'));
                    }
                })
        });
    }).then(function (results) {
        return [results];
    });
}

function transformPOSIXSeparator(s) {
    return s.split(path.sep).join('/');
}

function createFontSrcDeclarationsWithEOT (stylesheetPath, options, result) {
    let fontUrl = path.relative(stylesheetPath, path.join(result.opts.outputPath, result.opts.fileName));
    fontUrl = url.resolve(options.publishPath, transformPOSIXSeparator(fontUrl));
    return 'url(\'' + fontUrl + '.eot\')';
}

function createFontSrcDeclarations(stylesheetPath, options, result) {
    let fontUrl = path.relative(stylesheetPath, path.join(result.opts.outputPath, result.opts.fileName));
    fontUrl = url.resolve(options.publishPath, transformPOSIXSeparator(fontUrl));
    let formats = result.opts.formats;

    let declarationArr = [];
    if (include(formats, 'eot')) {
        declarationArr.push('url(\'' + fontUrl + '.eot#iefix\') format(\'embedded-opentype\')');
    }
    if (include(formats, 'woff2')) {
        declarationArr.push('url(\'' + fontUrl + '.woff2\') format(\'woff2\')');
    }
    if (include(formats, 'woff')) {
        declarationArr.push('url(\'' + fontUrl + '.woff\') format(\'woff\')');
    }
    if (include(formats, 'ttf')) {
        declarationArr.push('url(\'' + fontUrl + '.ttf\') format(\'truetype\')');
    }
    if (include(formats, 'svg')) {
        declarationArr.push('url(\'' + fontUrl + '.svg#' + result.opts.fontName + '\') format(\'svg\')');
    }
    return declarationArr.join(', \n       ');
}
function insertFontSrcDeclarations(rule, options, result) {
    const stylesheetPath = options.stylesheetPath || path.dirname(rule.parent.source.input.file);
    rule.walkDecls('src', function (decl) {
        // eot for < ie9
        if (include(result.opts.formats, 'eot')) {
            rule.insertBefore(decl, postcss.decl({
                prop: 'src',
                value: createFontSrcDeclarationsWithEOT(stylesheetPath, options, result)
            }));
        }

        rule.insertBefore(decl, postcss.decl({
            prop: 'src',
            value: createFontSrcDeclarations(stylesheetPath, options, result)
        }));

        decl.remove();
    });
}

/**
 * Insert glyphs css rules
 * @param rule
 * @param result
 */
function insertGlyphsRules(rule, result) {
    // Reverse order
    // make sure to insert the characters in ascending order
    let glyphs = result.glyphs.slice(0);
    let glyphLen = glyphs.length;
    while (glyphLen--) {
        let glyph = glyphs[glyphLen];
        let node = postcss.rule({
            selector: '.' + result.opts.fontName + '-' + glyph.name + ':before'
        });
        node.append(postcss.decl({
            prop: 'content',
            value: '\'\\' +
            glyph.unicode[0]
                .charCodeAt(0)
                .toString(16)
                .toUpperCase()
            + '\''
        }));

        rule.parent.insertAfter(rule, node);
    }

    let node = postcss.rule({
        selector: '[class^="' + result.opts.fontName + '-"], [class*=" ' + result.opts.fontName + '-"]'
    });
    [
        {prop: 'font-family', value: '\'' + result.opts.fontName + '\''},
        {prop: 'speak', value: 'none'},
        {prop: 'font-style', value: 'normal'},
        {prop: 'font-weight', value: 'normal'},
        {prop: 'font-variant', value: 'normal'},
        {prop: 'text-transform', value: 'none'},
        {prop: 'line-height', value: 1},
        {prop: '-webkit-font-smoothing', value: 'antialiased'},
        {prop: '-moz-osx-font-smoothing', value: 'grayscale'}
    ].forEach(function (item) {
        node.append(postcss.decl({
            prop: item.prop,
            value: item.value
        }));
    });

    rule.parent.insertAfter(rule, node);
}

function updateRule(root, options, results) {
    if (Array.isArray(results) && results.length) {
        results.forEach(function (item) {
            root.walkAtRules('font-face', function (rule) {
                let isInsert = false;
                rule.walkDecls('font-family', function (decl) {
                    if (trimQuotationMarks(decl.value) === item.opts.fontName) {
                        isInsert = true;
                    }
                });

                if (isInsert) {
                    insertFontSrcDeclarations(rule, options, item);
                    insertGlyphsRules(rule, item);

                    return false;
                }
            });

            if (isFunction(options.hooks.onUpdateRule)) {
                options.hooks.onUpdateRule(root, options, item);
            }
        });
    }
    return Promise.resolve([root, results]);
}

function iconFontPlugin (options) {
    // merge options
    options = merge({
        basePath: './',
        stylesheetPath: null,
        outputPath: './',
        publishPath: '',

        formats: ['svg', 'ttf', 'eot', 'woff'],
        hooks: {
            onUpdateRule: null
        }
    }, (options || {}));

    return function (root, result) {
        return extractsSVG(root, options)
            .spread(function (queueMap) {
                return prepare(queueMap, options);
            }).spread(function (queueMap) { // run gulp-iconfont generate fonts.
                return runGulpIconFont(queueMap);
            }).spread(function (results) { // update css rule.
                return updateRule(root, options, results);
            }).catch(function (err) {
                errorLog(POSTCSS_PLUGIN_NAME + ': An error occurred while processing files - ' + err.message);
                errorLog(err.stack);
                throw err;
            });
    };
}
module.exports = postcss.plugin(POSTCSS_PLUGIN_NAME, iconFontPlugin);
<|endoftext|>


<commit_before>"use strict";

class CircleSlider {
  /**
   * Creates an instance of CircleSlider inside the element with the id `targetId`
   * @param {String} targetId  The id of the element to contain the circle slider.
   * @param {Number} startVal  The starting value for the slider.
   * @param {Number} minVal    The minimum value for the slider.
   * @param {Number} maxVal    The maximum value for the slider.
   * @memberof CircleSlider
   */
  constructor(targetId, startVal, minVal, maxVal) {
    this.root = document.getElementById(targetId);

    // validation
    if (!this.root) {
      console.error("CircleSlider: Didn't find any element with id " + targetId);
    }
    if (startVal < minVal || startVal > maxVal) {
      console.error("CircleSlider: startVal is outside the bounds of min and max");
    }
    if (minVal > maxVal) {
      console.error("CircleSlider: min is greater than max!");
    }
    if (maxVal < minVal) {
      console.error("CircleSlider: max is less than min!");
    }

    // create the bg and handle elements
    this.bg = this._createBgElem();
    this.hc = this._createHandleContainerElem();
    this.handle = this._createHandleElem();

    // append them inside the root element
    this.root.appendChild(this.bg);
    this.hc.appendChild(this.handle);
    this.root.appendChild(this.hc);

    this.mouseDown = false;
    this.handle.addEventListener("mousedown", (e) => {
      // prevent text selection
      e.preventDefault();

      if (!this.mouseDown) {
        this.mouseDown = true;
        // add eventListener on document instead of handler so that
        // dragging keeps working when the mouse is outside the root element
        document.addEventListener("mousemove", this._mouseMoveHandler, false);
        // add to document for the same reason as above, the user can
        // release the mouse outside the root element
        document.addEventListener("mouseup", () => {
          this.mouseDown = false;
          document.removeEventListener("mousemove", this._mouseMoveHandler, false);
        })
      }
    })

    // bind functions
    this._mouseMoveHandler = this._mouseMoveHandler.bind(this);
  }

  _mouseMoveHandler(e) {
    e.preventDefault();
    const angle = this.getAngle(e);
    this.hc.style.cssText = `transform: rotate(${angle}deg);`;
  }

  _getCenter(elem) {
    const rect = elem.getBoundingClientRect();
    return {
      x: rect.left + (rect.width / 2),
      y: rect.top + (rect.height / 2),
    };
  }

  _radToDeg(rad) {
    return rad * (180 / Math.PI);
  }

  getAngle(e) {
    const pivot = this._getCenter(this.bg);
    const mouse = {
      x: e.pageX,
      y: e.pageY,
    }
    const offset = -90;
    let angle = this._radToDeg(Math.atan2(mouse.x - pivot.x, -(mouse.y - pivot.y))) + offset;
    if (angle < 0) angle += 360;
    return angle;
  }

  // Uninteresting methods

  _createBgElem() {
    let bg = document.createElement("div");
    bg.className = `cs-bg`;
    bg.style.cssText = "height: 100%; width: 100%; border-radius: 100%;";
    return bg;
  }

  _createHandleContainerElem() {
    let hc = document.createElement("div");
    hc.className = "cs-handle-container";
    return hc;
  }

  _createHandleElem() {
    let h = document.createElement("div");
    h.className = "cs-handle";
    return h;
  }
}<commit_msg>✅ Add touch events<commit_after>"use strict";

class CircleSlider {
  /**
   * Creates an instance of CircleSlider inside the element with the id `targetId`
   * @param {String} targetId  The id of the element to contain the circle slider.
   * @param {Number} startVal  The starting value for the slider.
   * @param {Number} minVal    The minimum value for the slider.
   * @param {Number} maxVal    The maximum value for the slider.
   * @memberof CircleSlider
   */
  constructor(targetId, startVal, minVal, maxVal) {
    this.root = document.getElementById(targetId);

    // validation
    if (!this.root) {
      console.error("CircleSlider: Didn't find any element with id " + targetId);
    }
    if (startVal < minVal || startVal > maxVal) {
      console.error("CircleSlider: startVal is outside the bounds of min and max");
    }
    if (minVal > maxVal) {
      console.error("CircleSlider: min is greater than max!");
    }
    if (maxVal < minVal) {
      console.error("CircleSlider: max is less than min!");
    }

    // create the bg and handle elements
    this.bg = this._createBgElem();
    this.hc = this._createHandleContainerElem();
    this.handle = this._createHandleElem();

    // append them inside the root element
    this.root.appendChild(this.bg);
    this.hc.appendChild(this.handle);
    this.root.appendChild(this.hc);

    // active is true when user is holding down handle
    this.active = false;
    // mouse events
    this._addEventListeners("mousedown", "mousemove", "mouseup");
    // touch events
    this._addEventListeners("touchstart", "touchmove", "touchend");

    // bind functions
    this._mouseMoveHandler = this._mouseMoveHandler.bind(this);
  }

  _addEventListeners(startEvent, moveEvent, endEvent) {
    this.handle.addEventListener(startEvent, (e) => {
      // prevent text selection
      e.preventDefault();

      if (!this.active) {
        this.active = true;
        // add eventListener on document instead of handler so that
        // dragging keeps working when the mouse is outside the root element
        document.addEventListener(moveEvent, this._mouseMoveHandler, false);
        // add to document for the same reason as above, the user can
        // release the mouse outside the root element
        document.addEventListener(endEvent, () => {
          this.active = false;
          document.removeEventListener(moveEvent, this._mouseMoveHandler, false);
        })
      }
    })
  }

  _mouseMoveHandler(e) {
    e.preventDefault();
    const angle = this.getAngle(e);
    this.hc.style.cssText = `transform: rotate(${angle}deg);`;
  }

  _getCenter(elem) {
    const rect = elem.getBoundingClientRect();
    return {
      x: rect.left + (rect.width / 2),
      y: rect.top + (rect.height / 2),
    };
  }

  _radToDeg(rad) {
    return rad * (180 / Math.PI);
  }

  getAngle(e) {
    const pivot = this._getCenter(this.bg);
    const mouse = {
      x: e.pageX,
      y: e.pageY,
    }
    const offset = -90;
    let angle = this._radToDeg(Math.atan2(mouse.x - pivot.x, -(mouse.y - pivot.y))) + offset;
    if (angle < 0) angle += 360;
    return angle;
  }

  // Uninteresting methods

  _createBgElem() {
    let bg = document.createElement("div");
    bg.className = `cs-bg`;
    bg.style.cssText = "height: 100%; width: 100%; border-radius: 100%;";
    return bg;
  }

  _createHandleContainerElem() {
    let hc = document.createElement("div");
    hc.className = "cs-handle-container";
    return hc;
  }

  _createHandleElem() {
    let h = document.createElement("div");
    h.className = "cs-handle";
    return h;
  }
}<|endoftext|>


<commit_before>import {
  error,
  parseToken
} from './util'

const identity = {
  defaults: {
    url: '/api',
    loginUrl: '/login',
    logoutUrl: '/logout',
    refreshUrl: '/refresh',
    accessToken: 'token',
    refreshToken: 'refresh',
    unauthorizedRedirect: null,
    redirect: null,
    scope: null
  },
  install(Vue, opts) {
    if (identity.installed) {
      return
    }
    // setup
    var vm = new Vue({
      data: {
        refreshToken: window.localStorage['vue-identity:refreshToken'],
        accessToken: null,
        expires: null,
        user: null,
        notBefore: null,
        issuedAt: null,
        loading: false,
        checking: false,
        expiryTimer: null
      },
      computed: {
        expiresIn() {
          return this.expires ? (this.expires * 1000) - Date.now() : false
        }
      }
    })

    let self = Vue.prototype.$identity = vm
    let options = Object.assign(identity.defaults, opts)

    let http = Vue.http
    let router = opts.router

    if (!http) {
      console.error('VueIdentity requires vue-resource. Make sure you Vue.use(VueResource) before Vue.use(VueIdentity)')
    }
    Vue.http.interceptors.push(function(request, next) {
      if( self.accessToken ) request.headers.set('Authorization', 'Bearer ' + self.accessToken)
      next()
    })
    if (!router) {
      console.info('To use with vue-router, pass it to me Vue.use(VueIdentity, {router})')
    } else {
      router.beforeEach((to, from, next) => {
        var $identity = options.router.app.$identity
        if (to.meta.identity && $identity.user === null) {
          $identity.authenticate().then(function() {
            next()
          }, function() {
            if (options.unauthorizedRedirect)
              next({
                path: options.unauthorizedRedirect
              })
          })
        } else {
          next()
        }
      })
    }

    // bind events
    window.onfocus = () => {
      self.attemptRefresh()
    }

    // methods
    self.uri = (endpoint) => {
      return options.url + options[endpoint + 'Url']
    }
    self.authenticate = () => {
      if (self.refreshToken) return Promise.resolve()
      var params = {}
      if (options.scope) params.scope = options.scope
      if (options.redirect) params.redirect = options.redirect
      return self.attachRequestQuarterback(http.get(this.uri('login'), {
        credentials: true,
        params: params
      }))
    }
    self.refresh = () => {
      return self.attachRequestQuarterback(http.post(this.uri('refresh'), {
        token: self.refreshToken
      }))
    }
    self.login = (data) => {
      return self.attachRequestQuarterback(http.post(this.uri('login'), data, {
        credentials: true,
        params: params
      }))
    }
    self.logout = () => {
      self.refreshToken = null
      self.accessToken = null
      self.user = null
      self.expires = null
      self.issuedAt = null
      self.notBefore = null
      return Promise.resolve()
    }
    self.attachRequestQuarterback = (promise) => {
      self.loading = true
      return promise.then((r) => {
        self.receivethMightyToken(r.data)
        return Promise.resolve()
      }).catch((e) => {
        if (e.headers.get('X-Authentication-Location'))
          window.location.href = e.headers.get('X-Authentication-Location')
        return error(e)
      }).finally(() => {
        self.loading = false
      })
    }
    self.receivethMightyToken = (tokenIsMightier) => {
      var accessToken = tokenIsMightier[options.accessToken]
      var refreshToken = tokenIsMightier[options.refreshToken]
      if (accessToken == undefined) return error('No token received')
      var user = parseToken(accessToken)
      self.accessToken = accessToken
      self.refreshToken = localStorage['vue-identity:refreshToken'] = refreshToken
      self.user = user
      self.expires = user.exp
      self.issuedAt = user.iat
      self.notBefore = user.nbf
      self.attemptRefreshIn(self.expiresIn - 30000)
    }
    self.attemptRefreshIn = (ms) => {
      clearTimeout(self.expiryTimer)
      self.expiryTimer = setTimeout(() => {
        self.attemptRefresh()
      }, ms)
    }
    self.attemptRefresh = () => {
      var expiresIn = (self.expires * 1000) - Date.now()

      if (self.tokenValid() && expiresIn <= 300000) {
        self.authenticate().catch(() => {
          clearInterval(self.expiryTimer)
        })
      }
    }
    self.isLoggedIn = () => {
      return self.tokenValid()
    }
    self.tokenValid = () => {
      return self.refreshToken && ((self.expires * 1000) - Date.now()) > 0
    }
  }
}
export default identity
<commit_msg>Using let instead of var<commit_after>import {
  error,
  parseToken
} from './util'

const identity = {
  defaults: {
    url: '/api',
    loginUrl: '/login',
    logoutUrl: '/logout',
    refreshUrl: '/refresh',
    accessToken: 'token',
    refreshToken: 'refresh',
    unauthorizedRedirect: null,
    redirect: null,
    scope: null
  },
  install(Vue, opts) {
    if (identity.installed) {
      return
    }
    // setup
    let vm = new Vue({
      data: {
        refreshToken: window.localStorage['vue-identity:refreshToken'],
        accessToken: null,
        expires: null,
        user: null,
        notBefore: null,
        issuedAt: null,
        loading: false,
        checking: false,
        expiryTimer: null
      },
      computed: {
        expiresIn() {
          return this.expires ? (this.expires * 1000) - Date.now() : false
        }
      }
    })

    let self = Vue.prototype.$identity = vm
    let options = Object.assign(identity.defaults, opts)

    let http = Vue.http
    let router = opts.router

    if (!http) {
      console.error('VueIdentity requires vue-resource. Make sure you Vue.use(VueResource) before Vue.use(VueIdentity)')
    }
    Vue.http.interceptors.push(function(request, next) {
      if( self.accessToken ) request.headers.set('Authorization', 'Bearer ' + self.accessToken)
      next()
    })
    if (!router) {
      console.info('To use with vue-router, pass it to me Vue.use(VueIdentity, {router})')
    } else {
      router.beforeEach((to, from, next) => {
        let $identity = options.router.app.$identity
        if (to.meta.identity && $identity.user === null) {
          $identity.authenticate().then(function() {
            next()
          }, function() {
            if (options.unauthorizedRedirect)
              next({
                path: options.unauthorizedRedirect
              })
          })
        } else {
          next()
        }
      })
    }

    // bind events
    window.onfocus = () => {
      self.attemptRefresh()
    }

    // methods
    self.uri = (endpoint) => {
      return options.url + options[endpoint + 'Url']
    }
    self.authenticate = () => {
      if (self.refreshToken) return Promise.resolve()
      let params = {}
      if (options.scope) params.scope = options.scope
      if (options.redirect) params.redirect = options.redirect
      return self.attachRequestQuarterback(http.get(this.uri('login'), {
        credentials: true,
        params: params
      }))
    }
    self.refresh = () => {
      return self.attachRequestQuarterback(http.post(this.uri('refresh'), {
        token: self.refreshToken
      }))
    }
    self.login = (data) => {
      return self.attachRequestQuarterback(http.post(this.uri('login'), data, {
        credentials: true,
        params: params
      }))
    }
    self.logout = () => {
      self.refreshToken = null
      self.accessToken = null
      self.user = null
      self.expires = null
      self.issuedAt = null
      self.notBefore = null
      return Promise.resolve()
    }
    self.attachRequestQuarterback = (promise) => {
      self.loading = true
      return promise.then((r) => {
        self.receivethMightyToken(r.data)
        return Promise.resolve()
      }).catch((e) => {
        if (e.headers.get('X-Authentication-Location'))
          window.location.href = e.headers.get('X-Authentication-Location')
        return error(e)
      }).finally(() => {
        self.loading = false
      })
    }
    self.receivethMightyToken = (tokenIsMightier) => {
      let accessToken = tokenIsMightier[options.accessToken]
      let refreshToken = tokenIsMightier[options.refreshToken]
      if (accessToken == undefined) return error('No token received')
      let user = parseToken(accessToken)
      self.accessToken = accessToken
      self.refreshToken = localStorage['vue-identity:refreshToken'] = refreshToken
      self.user = user
      self.expires = user.exp
      self.issuedAt = user.iat
      self.notBefore = user.nbf
      self.attemptRefreshIn(self.expiresIn - 30000)
    }
    self.attemptRefreshIn = (ms) => {
      clearTimeout(self.expiryTimer)
      self.expiryTimer = setTimeout(() => {
        self.attemptRefresh()
      }, ms)
    }
    self.attemptRefresh = () => {
      let expiresIn = (self.expires * 1000) - Date.now()

      if (self.tokenValid() && expiresIn <= 300000) {
        self.authenticate().catch(() => {
          clearInterval(self.expiryTimer)
        })
      }
    }
    self.isLoggedIn = () => {
      return self.tokenValid()
    }
    self.tokenValid = () => {
      return self.refreshToken && ((self.expires * 1000) - Date.now()) > 0
    }
  }
}
export default identity
<|endoftext|>


<commit_before>'use strict';

const ajax = require('then-request');
const adapterOptions = Symbol();

let endpointState = {};
let endpointResolver = {};
let storage = global.localStorage || {};

class RestAdapter {

    constructor (options = {}, routes = {}) {
        this.routes = routes;
        this[adapterOptions] = options;
    }

    getRoutePath (model) {
        let routeArray = [model.constructor.name];
        let child = model;

        while (child.getParent()) {
            routeArray.push(child.getParent().constructor.name);
            child = child.getParent();
        }

        return routeArray.reverse().join('/');
    }

    parseOverrideRoute (uri, model) {
        let parents = [];
        let child = model;

        while (child.getParent()) {
            parents.push(child.getParent().id);
            child = child.getParent();
        }

        uri = uri.replace(/{id}/g, model.id);

        for (let i = 0; i < parents.length; i++) {
            uri = uri.replace(`{parents[${i}]}`, parents[i]);
        }

        return uri;
    }

    getOverrideRoute (requestType, model) {
        const path = this.getRoutePath(model);

        if (this.routes[path] && this.routes[path][requestType]) {
            return this.parseOverrideRoute(this.routes[path][requestType], model);
        }
    }

    buildNestedUrl (url, child) {
        const parent = child.getParent();

        url = `${child.getName()}/${child.id}/${url}`;

        if (parent) {
            return this.buildNestedUrl(url, parent);

        } else {
            return url;
        }
    }

    create (model, options) {
        const url = this.getCreateRoute(model);
        const requestOptions = this.createOptions(options);
        requestOptions.body = this.getBody(model);

        return ajax('POST', url, requestOptions)
            .then(this.formatFetchResponse.bind(this));
    }

    createOptions (model, options = {}) {
        options.headers = Object.assign({}, this[adapterOptions].headers, options.headers);

        return Object.assign({}, this[adapterOptions], options);
    }

    createOptionsWithEtags (model, url, options) {
        let requestOptions = this.createOptions(model, options);
        let key = `${url}-${JSON.stringify(requestOptions.body)}`;

        requestOptions.headers['If-None-Match'] = storage[`e-${key}`];

        return requestOptions;
    }

    destroy (model, options) {
        const url = this.getDestroyRoute(model);
        const requestOptions = this.createOptions(model, options);
        requestOptions.body = model.toJSON();

        return ajax('DELETE', url, requestOptions)
            .then(this.parseResponse.bind(this));
    }

    fetch (model, options = {}) {
        const url = this.getFetchRoute(model);
        const requestOptions = this.createOptionsWithEtags(model, url, options);

        if (endpointState[url] === 'fetching' || endpointState[url] === 'fetched') {
            return endpointResolver[url];
        }

        endpointState[url] = 'fetching';

        endpointResolver[url] = ajax('GET', url, requestOptions)
            .then(this.formatFetchResponse.bind(this))
            .then((response) => {
                endpointState[url] = 'fetched';
                return response;
            });

        return endpointResolver[url];
    }

    fetchWithin (model, parent, options = {}) {
        const url = this.getFetchWithinRoute(model, parent);
        const requestOptions = this.createOptionsWithEtags(model, url, options);

        if (endpointState[url] === 'fetching' || endpointState[url] === 'fetched') {
            return endpointResolver[url];
        }

        endpointState[url] = 'fetching';

        endpointResolver[url] = ajax('GET', this.getUrl(url), requestOptions)
            .then(this.formatFetchResponse.bind(this))
            .then((response) => {
                endpointState[url] = 'fetched';
                return response;
            });

        return endpointResolver[url];
    }

    findAll (Model, options = {}) {
        const url = this.getFindAllRoute(Model);
        const requestOptions = this.createOptionsWithEtags(null, url, options);

        return ajax('GET', url, requestOptions)
            .then(this.formatFindAllResponse.bind(this));
    }

    findAllWithin (Model, parent, options = {}) {
        const url = this.getFindAllWithinRoute(Model, parent);
        const requestOptions = this.createOptionsWithEtags(parent, url, options);

        return ajax('GET', this.getUrl(url), requestOptions)
            .then(this.formatFindAllResponse.bind(this));
    }

    formatFetchResponse (response) {
        const body = this.parseResponse(response);
        const envelope = this.getOptions().envelope;

        return (envelope) ? body[envelope] : body;
    }

    formatFindAllResponse (response) {
        const body = this.parseResponse(response);
        const envelope = this.getOptions().arrayEnvelope;

        return (envelope) ? body[envelope] : body;
    }

    getBody (model) {
        return JSON.stringify(model.toJSON());
    }

    getCreateRoute (model) {
        return this.getOverrideRoute('create', model) || this.getSingleFetchUrl(model);
    }

    getDestroyRoute (model) {
        return this.getOverrideRoute('destroy', model) || this.getSingleFetchUrl(model);
    }

    getFetchRoute (model) {
        return this.getOverrideRoute('fetch', model) || this.getFetchUrl(model.getName(), model.id);
    }

    getFetchWithinRoute (model, parent) {
        return this.getOverrideRoute('fetch', model) || this.buildNestedUrl(model.getName(), parent);
    }

    getFindAllRoute (Model) {
        return this.getOverrideRoute('findAll', new Model()) || this.getUrl(new Model().getName());
    }

    getFindAllWithinRoute (Model, parent) {
        const overrideModel = new Model();
        overrideModel.getParent = () => {
            return parent;
        };

        return this.getOverrideRoute('findAll', overrideModel) || this.buildNestedUrl(new Model().getName(), parent);
    }

    getUpdateRoute (model) {
        return this.getOverrideRoute('update', model) || this.getSingleFetchUrl(model);
    }

    getUrl (url) {
        const { urlRoot } = this.getOptions();

        return `${urlRoot || ''}${url}`;
    }

    getFetchUrl (url, id) {
        if (id) {
            return `${this.getUrl(url)}/${id}`;
        }

        return this.getUrl(url);
    }

    getSingleFetchUrl (model) {
        const parent = model.getParent();
        const parentUrl = (parent) ? `${parent.getName()}/${parent.id}/` : '';
        return this.getFetchUrl(parentUrl + model.getName(), model.id);
    }

    getOptions () {
        return this[adapterOptions];
    }

    getResponseBody (response) {
        const key = response.url;
        const cachedDate = storage[`cannery-d-${key}`];

        if (response.statusCode === 304 && cachedDate) {
            return cachedDate;
        }

        if (response.headers.etag) {
            storage[`cannery-e-${key}`] = response.headers.etag;
            storage[`cannery-d-${key}`] = response.getBody();
        }

        return response.getBody();
    }

    parseResponse (response) {
        const body = this.getResponseBody(response);

        try {
            return JSON.parse(body);
        } catch (e) {}

    }

    update (model, options = {}) {
        const url = this.getUpdateRoute(model);
        const requestOptions = this.createOptions(model, options);
        requestOptions.body = this.getBody(model);

        return ajax('PUT', url, requestOptions)
            .then(this.formatFetchResponse.bind(this));
    }

}

module.exports = RestAdapter;
<commit_msg>changed etag storage type<commit_after>'use strict';

const ajax = require('then-request');
const adapterOptions = Symbol();

let endpointState = {};
let endpointResolver = {};
let storage = global.sessionStorage || {};

class RestAdapter {

    constructor (options = {}, routes = {}) {
        this.routes = routes;
        this[adapterOptions] = options;
    }

    getRoutePath (model) {
        let routeArray = [model.constructor.name];
        let child = model;

        while (child.getParent()) {
            routeArray.push(child.getParent().constructor.name);
            child = child.getParent();
        }

        return routeArray.reverse().join('/');
    }

    parseOverrideRoute (uri, model) {
        let parents = [];
        let child = model;

        while (child.getParent()) {
            parents.push(child.getParent().id);
            child = child.getParent();
        }

        uri = uri.replace(/{id}/g, model.id);

        for (let i = 0; i < parents.length; i++) {
            uri = uri.replace(`{parents[${i}]}`, parents[i]);
        }

        return uri;
    }

    getOverrideRoute (requestType, model) {
        const path = this.getRoutePath(model);

        if (this.routes[path] && this.routes[path][requestType]) {
            return this.parseOverrideRoute(this.routes[path][requestType], model);
        }
    }

    buildNestedUrl (url, child) {
        const parent = child.getParent();

        url = `${child.getName()}/${child.id}/${url}`;

        if (parent) {
            return this.buildNestedUrl(url, parent);

        } else {
            return url;
        }
    }

    create (model, options) {
        const url = this.getCreateRoute(model);
        const requestOptions = this.createOptions(options);
        requestOptions.body = this.getBody(model);

        return ajax('POST', url, requestOptions)
            .then(this.formatFetchResponse.bind(this));
    }

    createOptions (model, options = {}) {
        options.headers = Object.assign({}, this[adapterOptions].headers, options.headers);

        return Object.assign({}, this[adapterOptions], options);
    }

    createOptionsWithEtags (model, url, options) {
        let requestOptions = this.createOptions(model, options);
        let key = `${url}-${JSON.stringify(requestOptions.body)}`;

        requestOptions.headers['If-None-Match'] = storage[`e-${key}`];

        return requestOptions;
    }

    destroy (model, options) {
        const url = this.getDestroyRoute(model);
        const requestOptions = this.createOptions(model, options);
        requestOptions.body = model.toJSON();

        return ajax('DELETE', url, requestOptions)
            .then(this.parseResponse.bind(this));
    }

    fetch (model, options = {}) {
        const url = this.getFetchRoute(model);
        const requestOptions = this.createOptionsWithEtags(model, url, options);

        if (endpointState[url] === 'fetching' || endpointState[url] === 'fetched') {
            return endpointResolver[url];
        }

        endpointState[url] = 'fetching';

        endpointResolver[url] = ajax('GET', url, requestOptions)
            .then(this.formatFetchResponse.bind(this))
            .then((response) => {
                endpointState[url] = 'fetched';
                return response;
            });

        return endpointResolver[url];
    }

    fetchWithin (model, parent, options = {}) {
        const url = this.getFetchWithinRoute(model, parent);
        const requestOptions = this.createOptionsWithEtags(model, url, options);

        if (endpointState[url] === 'fetching' || endpointState[url] === 'fetched') {
            return endpointResolver[url];
        }

        endpointState[url] = 'fetching';

        endpointResolver[url] = ajax('GET', this.getUrl(url), requestOptions)
            .then(this.formatFetchResponse.bind(this))
            .then((response) => {
                endpointState[url] = 'fetched';
                return response;
            });

        return endpointResolver[url];
    }

    findAll (Model, options = {}) {
        const url = this.getFindAllRoute(Model);
        const requestOptions = this.createOptionsWithEtags(null, url, options);

        return ajax('GET', url, requestOptions)
            .then(this.formatFindAllResponse.bind(this));
    }

    findAllWithin (Model, parent, options = {}) {
        const url = this.getFindAllWithinRoute(Model, parent);
        const requestOptions = this.createOptionsWithEtags(parent, url, options);

        return ajax('GET', this.getUrl(url), requestOptions)
            .then(this.formatFindAllResponse.bind(this));
    }

    formatFetchResponse (response) {
        const body = this.parseResponse(response);
        const envelope = this.getOptions().envelope;

        return (envelope) ? body[envelope] : body;
    }

    formatFindAllResponse (response) {
        const body = this.parseResponse(response);
        const envelope = this.getOptions().arrayEnvelope;

        return (envelope) ? body[envelope] : body;
    }

    getBody (model) {
        return JSON.stringify(model.toJSON());
    }

    getCreateRoute (model) {
        return this.getOverrideRoute('create', model) || this.getSingleFetchUrl(model);
    }

    getDestroyRoute (model) {
        return this.getOverrideRoute('destroy', model) || this.getSingleFetchUrl(model);
    }

    getFetchRoute (model) {
        return this.getOverrideRoute('fetch', model) || this.getFetchUrl(model.getName(), model.id);
    }

    getFetchWithinRoute (model, parent) {
        return this.getOverrideRoute('fetch', model) || this.buildNestedUrl(model.getName(), parent);
    }

    getFindAllRoute (Model) {
        return this.getOverrideRoute('findAll', new Model()) || this.getUrl(new Model().getName());
    }

    getFindAllWithinRoute (Model, parent) {
        const overrideModel = new Model();
        overrideModel.getParent = () => {
            return parent;
        };

        return this.getOverrideRoute('findAll', overrideModel) || this.buildNestedUrl(new Model().getName(), parent);
    }

    getUpdateRoute (model) {
        return this.getOverrideRoute('update', model) || this.getSingleFetchUrl(model);
    }

    getUrl (url) {
        const { urlRoot } = this.getOptions();

        return `${urlRoot || ''}${url}`;
    }

    getFetchUrl (url, id) {
        if (id) {
            return `${this.getUrl(url)}/${id}`;
        }

        return this.getUrl(url);
    }

    getSingleFetchUrl (model) {
        const parent = model.getParent();
        const parentUrl = (parent) ? `${parent.getName()}/${parent.id}/` : '';
        return this.getFetchUrl(parentUrl + model.getName(), model.id);
    }

    getOptions () {
        return this[adapterOptions];
    }

    getResponseBody (response) {
        const key = response.url;
        const cachedDate = storage[`cannery-d-${key}`];

        if (response.statusCode === 304 && cachedDate) {
            return cachedDate;
        }

        if (response.headers.etag) {
            storage[`cannery-e-${key}`] = response.headers.etag;
            storage[`cannery-d-${key}`] = response.getBody();
        }

        return response.getBody();
    }

    parseResponse (response) {
        const body = this.getResponseBody(response);

        try {
            return JSON.parse(body);
        } catch (e) {}

    }

    update (model, options = {}) {
        const url = this.getUpdateRoute(model);
        const requestOptions = this.createOptions(model, options);
        requestOptions.body = this.getBody(model);

        return ajax('PUT', url, requestOptions)
            .then(this.formatFetchResponse.bind(this));
    }

}

module.exports = RestAdapter;
<|endoftext|>


<commit_before>import './typeclasses';
import Microstate from './microstate';

export default Microstate;
export const create = Microstate.create;

export { default as Tree } from './utils/tree';
export { default as analyze } from './structure';
<commit_msg>Destructing create<commit_after>import './typeclasses';
import Microstate from './microstate';

export default Microstate;
export const { create } = Microstate;

export { default as Tree } from './utils/tree';
export { default as analyze } from './structure';
<|endoftext|>