<commit_before>"use strict";

let express = require("express");
let fs = require("fs");
let pathutil = require("path");
let util = require("util");
let ViewData = require("../view").ViewData;

class ThemeManager {
  /**
   * @param paths Array of search paths.
   *
   * NOTE: Paths must be relative to the project root.
   */
  constructor(paths) {
    this.paths = [];
    this.themes = new Map;
    paths.forEach(path => this.discover(path));
  }

  get(name) {
    if (this.themes.has(name)) {
      return this.themes.get(name);
    }
    throw new Error(util.format("Theme '%s' does not exist", name));
  }

  discover(root) {
    if (this.paths.indexOf(root) != -1) {
      return;
    }
    this.paths.push(root);
    fs.readdirSync(root).forEach(name => {
      if (this.themes.has(name)) {
        throw new Error(util.format("Cannot register theme '%s' as it already exists", name));
      }
      let path = util.format("%s/%s", root, name);
      this.themes.set(name, new Theme(name, path));
    });
  }
}

class Theme {
  constructor(name, path) {
    this.name = name;
    this.path = path;
  }
}

exports.configure = services => {
  let app = services.get("app");
  let config = services.get("config");
  let theme = config.get("theme.name");
  let manager = new ThemeManager(["themes", pathutil.relative(app.rootPath, app.colibreRoot + "/themes")]);
  services.register("theme.manager", manager);

  for (let theme of manager.themes.values()) {
    app.baseApp.use("/" + theme.path, express.static(theme.path));
  }

  services.get("view").templates.addPath("layout", util.format("themes/%s/views/layout", theme));
  services.get("event.manager").on("view.render", event => {
    if (event.data instanceof ViewData) {
      let site_name = config.get("site_name");
      let page_title = event.data.variables.page_title || null;
      let head_title = (page_title ? util.format("%s –", page_title) : "") + site_name;
      event.data = new ViewData("layout", {
        head_title: head_title,
        page_title: page_title,
        identity: event.identity,
        content: event.data,
      });
    }
  });
};
<commit_msg>Add a space between dash and site name in title<commit_after>"use strict";

let express = require("express");
let fs = require("fs");
let pathutil = require("path");
let util = require("util");
let ViewData = require("../view").ViewData;

class ThemeManager {
  /**
   * @param paths Array of search paths.
   *
   * NOTE: Paths must be relative to the project root.
   */
  constructor(paths) {
    this.paths = [];
    this.themes = new Map;
    paths.forEach(path => this.discover(path));
  }

  get(name) {
    if (this.themes.has(name)) {
      return this.themes.get(name);
    }
    throw new Error(util.format("Theme '%s' does not exist", name));
  }

  discover(root) {
    if (this.paths.indexOf(root) != -1) {
      return;
    }
    this.paths.push(root);
    fs.readdirSync(root).forEach(name => {
      if (this.themes.has(name)) {
        throw new Error(util.format("Cannot register theme '%s' as it already exists", name));
      }
      let path = util.format("%s/%s", root, name);
      this.themes.set(name, new Theme(name, path));
    });
  }
}

class Theme {
  constructor(name, path) {
    this.name = name;
    this.path = path;
  }
}

exports.configure = services => {
  let app = services.get("app");
  let config = services.get("config");
  let theme = config.get("theme.name");
  let manager = new ThemeManager(["themes", pathutil.relative(app.rootPath, app.colibreRoot + "/themes")]);
  services.register("theme.manager", manager);

  for (let theme of manager.themes.values()) {
    app.baseApp.use("/" + theme.path, express.static(theme.path));
  }

  services.get("view").templates.addPath("layout", util.format("themes/%s/views/layout", theme));
  services.get("event.manager").on("view.render", event => {
    if (event.data instanceof ViewData) {
      let site_name = config.get("site_name");
      let page_title = event.data.variables.page_title || null;
      let head_title = (page_title ? util.format("%s – ", page_title) : "") + site_name;
      event.data = new ViewData("layout", {
        head_title: head_title,
        page_title: page_title,
        identity: event.identity,
        content: event.data,
      });
    }
  });
};
<|endoftext|>


<commit_before>/*
 * JavaScript tracker for Snowplow: Snowplow.js
 *
 * Significant portions copyright 2010 Anthon Pang. Remainder copyright
 * 2012-2014 Snowplow Analytics Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of Anthon Pang nor Snowplow Analytics Ltd nor the
 *   names of their contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
;(function () {

	var
		lodash = require('../lib_managed/lodash'),
		cookie = require('browser-cookie-lite'),

		object = typeof exports !== 'undefined' ? exports : this; // For eventual node.js environment support

	/**
	 * Cleans up the page title
	 */
	object.fixupTitle = function (title) {
		if (!lodash.isString(title)) {
			title = title.text || '';

			var tmp = document.getElementsByTagName('title');
			if (tmp && !lodash.isUndefined(tmp[0])) {
				title = tmp[0].text;
			}
		}
		return title;
	};

	/*
	 * Extract hostname from URL
	 */
	object.getHostName = function (url) {
		// scheme : // [username [: password] @] hostname [: port] [/ [path] [? query] [# fragment]]
		var e = new RegExp('^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)'),
			matches = e.exec(url);

		return matches ? matches[1] : url;
	};

	/*
	 * Fix-up domain
	 */
	object.fixupDomain = function (domain) {
		var dl = domain.length;

		// remove trailing '.'
		if (domain.charAt(--dl) === '.') {
			domain = domain.slice(0, dl);
		}
		// remove leading '*'
		if (domain.slice(0, 2) === '*.') {
			domain = domain.slice(1);
		}
		return domain;
	};

	/**
	 * Get page referrer. In the case of a single-page app,
	 * if the URL changes without the page reloading, pass
	 * in the old URL. It will be returned unless overriden
	 * by a "refer(r)er" parameter in the querystring.
	 *
	 * @param string oldLocation Optional.
	 * @return string The referrer
	 */
	object.getReferrer = function (oldLocation) {

		var referrer = '';

		var fromQs = object.fromQuerystring('referrer', window.location.href) ||
		object.fromQuerystring('referer', window.location.href);

		// Short-circuit
		if (fromQs) {
			return fromQs;
		}

		// In the case of a single-page app, return the old URL
		if (oldLocation) {
			return oldLocation;
		}

		try {
			referrer = window.top.document.referrer;
		} catch (e) {
			if (window.parent) {
				try {
					referrer = window.parent.document.referrer;
				} catch (e2) {
					referrer = '';
				}
			}
		}
		if (referrer === '') {
			referrer = document.referrer;
		}
		return referrer;
	};

	/*
	 * Cross-browser helper function to add event handler
	 */
	object.addEventListener = function (element, eventType, eventHandler, useCapture) {
		if (element.addEventListener) {
			element.addEventListener(eventType, eventHandler, useCapture);
			return true;
		}
		if (element.attachEvent) {
			return element.attachEvent('on' + eventType, eventHandler);
		}
		element['on' + eventType] = eventHandler;
	};

	/*
	 * Return value from name-value pair in querystring
	 */
	object.fromQuerystring = function (field, url) {
		var match = new RegExp('^[^#]*[?&]' + field + '=([^&#]*)').exec(url);
		if (!match) {
			return null;
		}
		return decodeURIComponent(match[1].replace(/\+/g, ' '));
	};

	/*
	 * Only log deprecation warnings if they won't cause an error
	 */
	object.warn = function(message) {
		if (typeof console !== 'undefined') {
			console.warn('Snowplow: ' + message);
		}
	};

	/**
	 * List the classes of a DOM element without using elt.classList (for compatibility with IE 9)
	 */
	object.getCssClasses = function (elt) {
		return elt.className.match(/\S+/g);
	};

	/*
	 * Check whether an element has at least one class from a given list
	 */
	function checkClass(elt, classList) {
		var classes = object.getCssClasses(elt),
			i;

		for (i = 0; i < classes.length; i++) {
			if (classList[classes[i]]) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Convert a criterion object to a filter function
	 *
	 * @param object criterion Either {whitelist: [array of allowable strings]}
	 *                             or {blacklist: [array of allowable strings]}
	 *                             or {filter: function (elt) {return whether to track the element}}
	 * @param boolean byClass Whether to whitelist/blacklist based on an element's classes (for forms)
	 *                        or name attribute (for fields)
	 */
	object.getFilter = function (criterion, byClass) {

		// If the criterion argument is not an object, add listeners to all elements
		if (lodash.isArray(criterion) || !lodash.isObject(criterion)) {
			return function (elt) {
				return true;
			};
		}

		if (criterion.hasOwnProperty('filter')) {
			return criterion.filter;
		} else {
			var inclusive = criterion.hasOwnProperty('whitelist');
			var specifiedClasses = criterion.whitelist || criterion.blacklist;
			if (!lodash.isArray(specifiedClasses)) {
				specifiedClasses = [specifiedClasses];
			}

			// Convert the array of classes to an object of the form {class1: true, class2: true, ...}
			var specifiedClassesSet = {};
			for (var i=0; i<specifiedClasses.length; i++) {
				specifiedClassesSet[specifiedClasses[i]] = true;
			}

			if (byClass) {
				return function (elt) {
					return checkClass(elt, specifiedClassesSet) === inclusive;
				};
			} else {
				return function (elt) {
					return elt.name in specifiedClassesSet === inclusive;
				};
			}
		}
	}

	/**
	 * Add a name-value pair to the querystring of a URL
	 *
	 * @param string url URL to decorate
	 * @param string name Name of the querystring pair
	 * @param string value Value of the querystring pair
	 */
	object.decorateQuerystring = function (url, name, value) {
		var initialQsParams = name + '=' + value;
		var hashSplit = url.split('#');
		var qsSplit = hashSplit[0].split('?');
		var beforeQuerystring = qsSplit.shift();
		// Necessary because a querystring may contain multiple question marks
		var querystring = qsSplit.join('?');
		if (!querystring) {
			querystring = initialQsParams;
		} else {
			// Whether this is the first time the link has been decorated
			var initialDecoration = true;
			var qsFields = querystring.split('&');
			for (var i=0; i<qsFields.length; i++) {
				if (qsFields[i].substr(0, name.length + 1) === name + '=') {
					initialDecoration = false;
					qsFields[i] = initialQsParams;
					querystring = qsFields.join('&');
					break;
				}
			}
			if (initialDecoration) {
				querystring = initialQsParams + '&' + querystring;
			}
		}
		hashSplit[0] = beforeQuerystring + '?' + querystring;
		return hashSplit.join('#');
	};

	/**
	 * Attempt to get a value from localStorage
	 *
	 * @param string key
	 * @return string The value obtained from localStorage, or
	 *                undefined if localStorage is inaccessible
	 */
	object.attemptGetLocalStorage = function (key) {
		try {
			return localStorage.getItem(key);
		} catch(e) {}
	};

	/**
	 * Attempt to write a value to localStorage
	 *
	 * @param string key
	 * @param string value
	 * @return boolean Whether the operation succeeded
	 */
	object.attemptWriteLocalStorage = function (key, value) {
		try {
			localStorage.setItem(key, value);
			return true;
		} catch(e) {
			return false;
		}
	};

	/**
	 * Finds the root domain
	 */
	object.findRootDomain = function () {
		var cookiePrefix = '_sp_root_domain_test_';
		var cookieName = cookiePrefix + new Date().getTime();
		var cookieValue = '_test_value_' + new Date().getTime();

		var split = window.location.hostname.split('.');
		var position = split.length - 1;
		while (position >= 0) {
			var currentDomain = split.slice(position, split.length).join('.');
			cookie.cookie(cookieName, cookieValue, 0, '/', currentDomain);
			if (cookie.cookie(cookieName) === cookieValue) {

				// Clean up created cookie(s)
				object.deleteCookie(cookieName, currentDomain);
				var cookieNames = object.getCookiesWithPrefix(cookiePrefix);
				for (var i = 0; i < cookieNames.length; i++) {
					object.deleteCookie(cookieNames[i], currentDomain);
				}

				return currentDomain;
			}
			position -= 1;
		}

		// Cookies cannot be read
		return window.location.hostname;
	};

	/**
	 * Checks whether a value is present within an array
	 *
	 * @param val The value to check for
	 * @param array The array to check within
	 * @return boolean Whether it exists
	 */
	object.isValueInArray = function (val, array) {
		for (var i = 0; i < array.length; i++) {
			if (array[i] === val) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Deletes an arbitrary cookie by setting the expiration date to the past
	 *
	 * @param cookieName The name of the cookie to delete
	 * @param domainName The domain the cookie is in
	 */
	object.deleteCookie = function (cookieName, domainName) {
		cookie.cookie(cookieName, '', -1, '/', domainName);
	};

	/**
	 * Fetches the name of all cookies beginning with a certain prefix
	 *
	 * @param cookiePrefix The prefix to check for
	 * @return array The cookies that begin with the prefix
	 */
	object.getCookiesWithPrefix = function (cookiePrefix) {
		var cookies = document.cookie.split("; ");
		var cookieNames = [];
		for (var i = 0; i < cookies.length; i++) {
			if (cookies[i].startsWith(cookiePrefix)) {
				cookieNames.push(cookies[i]);
			}
		}
		return cookieNames;
	};

	/**
	 * Parses an object and returns either the
	 * integer or undefined.
	 *
	 * @param obj The object to parse
	 * @return the result of the parse operation
	 */
	object.parseInt = function (obj) {
		var result = parseInt(obj);
		return isNaN(result) ? undefined : result;
	}

	/**
	 * Parses an object and returns either the
	 * number or undefined.
	 *
	 * @param obj The object to parse
	 * @return the result of the parse operation
	 */
	object.parseFloat = function (obj) {
		var result = parseFloat(obj);
		return isNaN(result) ? undefined : result;
	}

}());
<commit_msg>Prevent CSS class listing from failing for elements with no CSS classes (fixes #473)<commit_after>/*
 * JavaScript tracker for Snowplow: Snowplow.js
 *
 * Significant portions copyright 2010 Anthon Pang. Remainder copyright
 * 2012-2014 Snowplow Analytics Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of Anthon Pang nor Snowplow Analytics Ltd nor the
 *   names of their contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
;(function () {

	var
		lodash = require('../lib_managed/lodash'),
		cookie = require('browser-cookie-lite'),

		object = typeof exports !== 'undefined' ? exports : this; // For eventual node.js environment support

	/**
	 * Cleans up the page title
	 */
	object.fixupTitle = function (title) {
		if (!lodash.isString(title)) {
			title = title.text || '';

			var tmp = document.getElementsByTagName('title');
			if (tmp && !lodash.isUndefined(tmp[0])) {
				title = tmp[0].text;
			}
		}
		return title;
	};

	/*
	 * Extract hostname from URL
	 */
	object.getHostName = function (url) {
		// scheme : // [username [: password] @] hostname [: port] [/ [path] [? query] [# fragment]]
		var e = new RegExp('^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)'),
			matches = e.exec(url);

		return matches ? matches[1] : url;
	};

	/*
	 * Fix-up domain
	 */
	object.fixupDomain = function (domain) {
		var dl = domain.length;

		// remove trailing '.'
		if (domain.charAt(--dl) === '.') {
			domain = domain.slice(0, dl);
		}
		// remove leading '*'
		if (domain.slice(0, 2) === '*.') {
			domain = domain.slice(1);
		}
		return domain;
	};

	/**
	 * Get page referrer. In the case of a single-page app,
	 * if the URL changes without the page reloading, pass
	 * in the old URL. It will be returned unless overriden
	 * by a "refer(r)er" parameter in the querystring.
	 *
	 * @param string oldLocation Optional.
	 * @return string The referrer
	 */
	object.getReferrer = function (oldLocation) {

		var referrer = '';

		var fromQs = object.fromQuerystring('referrer', window.location.href) ||
		object.fromQuerystring('referer', window.location.href);

		// Short-circuit
		if (fromQs) {
			return fromQs;
		}

		// In the case of a single-page app, return the old URL
		if (oldLocation) {
			return oldLocation;
		}

		try {
			referrer = window.top.document.referrer;
		} catch (e) {
			if (window.parent) {
				try {
					referrer = window.parent.document.referrer;
				} catch (e2) {
					referrer = '';
				}
			}
		}
		if (referrer === '') {
			referrer = document.referrer;
		}
		return referrer;
	};

	/*
	 * Cross-browser helper function to add event handler
	 */
	object.addEventListener = function (element, eventType, eventHandler, useCapture) {
		if (element.addEventListener) {
			element.addEventListener(eventType, eventHandler, useCapture);
			return true;
		}
		if (element.attachEvent) {
			return element.attachEvent('on' + eventType, eventHandler);
		}
		element['on' + eventType] = eventHandler;
	};

	/*
	 * Return value from name-value pair in querystring
	 */
	object.fromQuerystring = function (field, url) {
		var match = new RegExp('^[^#]*[?&]' + field + '=([^&#]*)').exec(url);
		if (!match) {
			return null;
		}
		return decodeURIComponent(match[1].replace(/\+/g, ' '));
	};

	/*
	 * Only log deprecation warnings if they won't cause an error
	 */
	object.warn = function(message) {
		if (typeof console !== 'undefined') {
			console.warn('Snowplow: ' + message);
		}
	};

	/**
	 * List the classes of a DOM element without using elt.classList (for compatibility with IE 9)
	 */
	object.getCssClasses = function (elt) {
		return elt.className.match(/\S+/g) || [];
	};

	/*
	 * Check whether an element has at least one class from a given list
	 */
	function checkClass(elt, classList) {
		var classes = object.getCssClasses(elt),
			i;

		for (i = 0; i < classes.length; i++) {
			if (classList[classes[i]]) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Convert a criterion object to a filter function
	 *
	 * @param object criterion Either {whitelist: [array of allowable strings]}
	 *                             or {blacklist: [array of allowable strings]}
	 *                             or {filter: function (elt) {return whether to track the element}}
	 * @param boolean byClass Whether to whitelist/blacklist based on an element's classes (for forms)
	 *                        or name attribute (for fields)
	 */
	object.getFilter = function (criterion, byClass) {

		// If the criterion argument is not an object, add listeners to all elements
		if (lodash.isArray(criterion) || !lodash.isObject(criterion)) {
			return function (elt) {
				return true;
			};
		}

		if (criterion.hasOwnProperty('filter')) {
			return criterion.filter;
		} else {
			var inclusive = criterion.hasOwnProperty('whitelist');
			var specifiedClasses = criterion.whitelist || criterion.blacklist;
			if (!lodash.isArray(specifiedClasses)) {
				specifiedClasses = [specifiedClasses];
			}

			// Convert the array of classes to an object of the form {class1: true, class2: true, ...}
			var specifiedClassesSet = {};
			for (var i=0; i<specifiedClasses.length; i++) {
				specifiedClassesSet[specifiedClasses[i]] = true;
			}

			if (byClass) {
				return function (elt) {
					return checkClass(elt, specifiedClassesSet) === inclusive;
				};
			} else {
				return function (elt) {
					return elt.name in specifiedClassesSet === inclusive;
				};
			}
		}
	}

	/**
	 * Add a name-value pair to the querystring of a URL
	 *
	 * @param string url URL to decorate
	 * @param string name Name of the querystring pair
	 * @param string value Value of the querystring pair
	 */
	object.decorateQuerystring = function (url, name, value) {
		var initialQsParams = name + '=' + value;
		var hashSplit = url.split('#');
		var qsSplit = hashSplit[0].split('?');
		var beforeQuerystring = qsSplit.shift();
		// Necessary because a querystring may contain multiple question marks
		var querystring = qsSplit.join('?');
		if (!querystring) {
			querystring = initialQsParams;
		} else {
			// Whether this is the first time the link has been decorated
			var initialDecoration = true;
			var qsFields = querystring.split('&');
			for (var i=0; i<qsFields.length; i++) {
				if (qsFields[i].substr(0, name.length + 1) === name + '=') {
					initialDecoration = false;
					qsFields[i] = initialQsParams;
					querystring = qsFields.join('&');
					break;
				}
			}
			if (initialDecoration) {
				querystring = initialQsParams + '&' + querystring;
			}
		}
		hashSplit[0] = beforeQuerystring + '?' + querystring;
		return hashSplit.join('#');
	};

	/**
	 * Attempt to get a value from localStorage
	 *
	 * @param string key
	 * @return string The value obtained from localStorage, or
	 *                undefined if localStorage is inaccessible
	 */
	object.attemptGetLocalStorage = function (key) {
		try {
			return localStorage.getItem(key);
		} catch(e) {}
	};

	/**
	 * Attempt to write a value to localStorage
	 *
	 * @param string key
	 * @param string value
	 * @return boolean Whether the operation succeeded
	 */
	object.attemptWriteLocalStorage = function (key, value) {
		try {
			localStorage.setItem(key, value);
			return true;
		} catch(e) {
			return false;
		}
	};

	/**
	 * Finds the root domain
	 */
	object.findRootDomain = function () {
		var cookiePrefix = '_sp_root_domain_test_';
		var cookieName = cookiePrefix + new Date().getTime();
		var cookieValue = '_test_value_' + new Date().getTime();

		var split = window.location.hostname.split('.');
		var position = split.length - 1;
		while (position >= 0) {
			var currentDomain = split.slice(position, split.length).join('.');
			cookie.cookie(cookieName, cookieValue, 0, '/', currentDomain);
			if (cookie.cookie(cookieName) === cookieValue) {

				// Clean up created cookie(s)
				object.deleteCookie(cookieName, currentDomain);
				var cookieNames = object.getCookiesWithPrefix(cookiePrefix);
				for (var i = 0; i < cookieNames.length; i++) {
					object.deleteCookie(cookieNames[i], currentDomain);
				}

				return currentDomain;
			}
			position -= 1;
		}

		// Cookies cannot be read
		return window.location.hostname;
	};

	/**
	 * Checks whether a value is present within an array
	 *
	 * @param val The value to check for
	 * @param array The array to check within
	 * @return boolean Whether it exists
	 */
	object.isValueInArray = function (val, array) {
		for (var i = 0; i < array.length; i++) {
			if (array[i] === val) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Deletes an arbitrary cookie by setting the expiration date to the past
	 *
	 * @param cookieName The name of the cookie to delete
	 * @param domainName The domain the cookie is in
	 */
	object.deleteCookie = function (cookieName, domainName) {
		cookie.cookie(cookieName, '', -1, '/', domainName);
	};

	/**
	 * Fetches the name of all cookies beginning with a certain prefix
	 *
	 * @param cookiePrefix The prefix to check for
	 * @return array The cookies that begin with the prefix
	 */
	object.getCookiesWithPrefix = function (cookiePrefix) {
		var cookies = document.cookie.split("; ");
		var cookieNames = [];
		for (var i = 0; i < cookies.length; i++) {
			if (cookies[i].startsWith(cookiePrefix)) {
				cookieNames.push(cookies[i]);
			}
		}
		return cookieNames;
	};

	/**
	 * Parses an object and returns either the
	 * integer or undefined.
	 *
	 * @param obj The object to parse
	 * @return the result of the parse operation
	 */
	object.parseInt = function (obj) {
		var result = parseInt(obj);
		return isNaN(result) ? undefined : result;
	}

	/**
	 * Parses an object and returns either the
	 * number or undefined.
	 *
	 * @param obj The object to parse
	 * @return the result of the parse operation
	 */
	object.parseFloat = function (obj) {
		var result = parseFloat(obj);
		return isNaN(result) ? undefined : result;
	}

}());
<|endoftext|>


<commit_before>'use strict';

/* jshint undef:true */

/**
 * Local Schema constructor
 * @public
 * @param {Object} schema
 */
var LocalSchema = function(name, schema, core, options) {
  this.schema = schema;
  this.name = name;
  this.options = options;
  this.core = core;
  this.keys = Object.keys(schema);
  this.keys.push('_id');
};

/**
 * Adds a property to the schema
 * @public
 * @param {Object} property
 */
LocalSchema.prototype.addToSchema = function(property) {
  var newKeys = Object.keys(property);
  var total = newKeys.length;
  for (var i = 0; i < total; i++) {
    this.schema[newKeys[i]] = property[newKeys[i]];
  }
  this.keys = Object.keys(this.schema);
  this.keys.push('_id');
};

/**
 * Create a new data object for this schema
 * @public
 * @param {Object} data
 * @returns {}
 */
LocalSchema.prototype.create = function(data) {
  var newEntry = {};
  newEntry._id = generateUUID();
  var total = this.keys.length;
  for (var i = 0; i < total; i++) {
    var key = this.keys[i];
    if (key === '_id') {
      continue;
    }

    var value = data[key];
    if (!value && this.schema[key].default) {
      value = this.schema[key].default;
    }
    newEntry[key] = value;
  }

  // Save to localstorage
  // At some point if there is an index, it can be added the the key for speed
  var index = getKey(this.name, newEntry._id);
  this.options.storage.setItem(index, JSON.stringify(newEntry));
  addIndex(this.name, index, this.options);

  // Clear indices
  this.indices = null;
  return new LocalDocument(newEntry, this);
};

/**
 * Returns all entries in storage
 * @public
 * @returns {Array} all entries
 */
LocalSchema.prototype.all = function() {
  var _this = this;
  this.indices = this.indices || getIndices(this.name, this.options);
  var results = [];

  // Check if the collection is empty
  if (!this.indices) {
    return results;
  }

  var total = this.indices.length;
  for (var i = 0; i < total; i++) {
    var index = this.indices[i];
    results.push(this.options.storage.getItem(index));
  }
  results = JSON.parse('[' + results.join(',') + ']');
  results = results.map(function(result) {
    return new LocalDocument(result, _this);
  });

  return results;
};

/**
 * Find an entry by ID
 * @public
 * @param {String} id
 * @returns {Object} the object or null
 */
LocalSchema.prototype.findById = function(id) {
  this.indices = this.indices || getIndices(this.name, this.options);
  var match = getIndex(this.indices, id);
  if (!match) {
    return null;
  }
  return new LocalDocument(
    JSON.parse(
      this.options.storage.getItem(match)
    ), this);
};

/**
 * Looks through the schema for an entry
 * @private
 * @param {Object} entry - the parsed entry
 * @param {Object} query
 * @returns {Array} an array of matches
 */
LocalSchema.prototype.checkEntry = function(entry, query) {
  var total = this.keys.length;
  var matches = [];

  for (var q = 0; q < total; q++) {
    var key = this.keys[q];
    var queryItem = query[key];
    if (typeof queryItem === 'undefined') {
      continue;
    }

    var isRegex = queryItem instanceof RegExp;
    var checkEmpty = typeof queryItem === 'object' && isEmpty(queryItem);
    if (!isRegex && (queryItem === '' || checkEmpty)) {
      continue;
    }

    if (entry[key]) {
      matches.push(matchQuery(
        LocalDocument.convert(key, entry[key], this.schema),
        queryItem
      ));
    }
  }
  return matches;
};

/**
 * Find entries matching a query
 * @public
 * @param {Object} query
 * @param {Boolean} isCount - return a count when true
 * @returns {Array/Number} an array of matches or
 * a number if isCount = true
 */
LocalSchema.prototype.find = function(query, isCount) {
  this.indices = this.indices || getIndices(this.name, this.options);
  if (!query || isEmpty(query)) {
    return isCount ? this.indices.length : this.all();
  }

  var results = isCount ? 0 : [];

  // Check if the collection is empty
  if (!this.indices) {
    return results;
  }

  for (var i = 0; i < this.indices.length; i++) {
    var entry = this.options.storage.getItem(this.indices[i]);
    var parsed = JSON.parse(entry);
    var matches = this.checkEntry(parsed, query);

    if (matches.length > 0 && !containsFalse(matches)) {
      if (!isCount) {
        results.push(new LocalDocument(parsed, this));
      } else {
        results++;
      }
    }
  }

  this.options.debug.log(results.length + ' results found');

  return results;
};

/**
 * Remove entries utilising the find query
 * @public
 * @param {Object} query
 * @returns {Number} the number of items removed
 */
LocalSchema.prototype.remove = function(query) {
  var entries = this.find(query);

  // Remove each entry individually
  for (var i = 0; i < entries.length; i++ ) {
    entries[i].remove();
  }

  return entries.length;
};

/**
 * Helper to return a count of results
 * @public
 * @param {Object} query
 * @returns {Number} the count of results
 */
LocalSchema.prototype.count = function(query) {
  return this.find(query, true);
};

/**
 * A batch updater
 * @public
 * @param {Object} query - to find entries to update
 * @param {Object} values - the values to update
 * @returns {Number} the number of entries changed
 */
LocalSchema.prototype.update = function(query, values) {
  var entries = this.find(query);
  var totalEntries = entries.length;
  if (totalEntries < 1) {
    return 0;
  }
  for (var i = 0; i < totalEntries; i++) {
    var entry = entries[i];
    var total = this.keys.length;
    for (var s = 0; s < total; s++) {
      var key = this.keys[s];
      if (typeof values[key] !== 'undefined') {
        entry[key] = values[key];
      }
    }
    entry.save();
  }
  return entries.length;
};

/**
 * Finds and the populates the results
 * @public
 * @param {Object} query
 * @param {String} names - space seperated
 * @param {Object} options
 * @returns {Array} of populated results
 */
LocalSchema.prototype.findAndPopulate = function(query, names, options) {
  var entries = this.find(query);
  var totalEntries = entries.length;
  if (totalEntries < 1) {
    return [];
  }
  var populated = [];
  for (var i = 0; i < totalEntries; i++) {
    populated.push(entries[i].populate(names, options));
  }
  return populated;
};

/**
 * LocalSchema Schema Types
 * For use in validation and return
 * @public
 */
LocalSchema.SchemaTypes = {
  String: 'string',
  Number: 'number',
  Boolean: 'boolean',
  Mixed: 'mixed',
  Date: 'date'
};
<commit_msg>Added initial plan<commit_after>'use strict';

/* jshint undef:true */

/**
 * Local Schema constructor
 * @public
 * @param {Object} schema
 */
var LocalSchema = function(name, schema, core, options) {
  this.schema = schema;
  this.name = name;
  this.options = options;
  this.core = core;
  this.keys = Object.keys(schema);
  this.keys.push('_id');
};

/**
 * Adds a property to the schema
 * @public
 * @param {Object} property
 */
LocalSchema.prototype.addToSchema = function(property) {
  var newKeys = Object.keys(property);
  var total = newKeys.length;
  for (var i = 0; i < total; i++) {
    this.schema[newKeys[i]] = property[newKeys[i]];
  }
  this.keys = Object.keys(this.schema);
  this.keys.push('_id');
};

/**
 * Create a new data object for this schema
 * @public
 * @param {Object} data
 * @returns {}
 */
LocalSchema.prototype.create = function(data) {
  var newEntry = {};
  newEntry._id = generateUUID();
  var total = this.keys.length;
  for (var i = 0; i < total; i++) {
    var key = this.keys[i];
    if (key === '_id') {
      continue;
    }

    var value = data[key];
    if (!value && this.schema[key].default) {
      value = this.schema[key].default;
    }
    newEntry[key] = value;
  }

  // Save to localstorage
  // At some point if there is an index, it can be added the the key for speed
  var index = getKey(this.name, newEntry._id);
  this.options.storage.setItem(index, JSON.stringify(newEntry));
  addIndex(this.name, index, this.options);

  // Clear indices
  this.indices = null;
  return new LocalDocument(newEntry, this);
};

/**
 * Returns all entries in storage
 * @public
 * @returns {Array} all entries
 */
LocalSchema.prototype.all = function() {
  var _this = this;
  this.indices = this.indices || getIndices(this.name, this.options);
  var results = [];

  // Check if the collection is empty
  if (!this.indices) {
    return results;
  }

  var total = this.indices.length;
  for (var i = 0; i < total; i++) {
    var index = this.indices[i];
    results.push(this.options.storage.getItem(index));
  }
  results = JSON.parse('[' + results.join(',') + ']');
  results = results.map(function(result) {
    return new LocalDocument(result, _this);
  });

  return results;
};

/**
 * Find an entry by ID
 * @public
 * @param {String} id
 * @returns {Object} the object or null
 */
LocalSchema.prototype.findById = function(id) {
  this.indices = this.indices || getIndices(this.name, this.options);
  var match = getIndex(this.indices, id);
  if (!match) {
    return null;
  }
  return new LocalDocument(
    JSON.parse(
      this.options.storage.getItem(match)
    ), this);
};

/**
 * Looks through the schema for an entry
 * @private
 * @param {Object} entry - the parsed entry
 * @param {Object} query
 * @returns {Array} an array of matches
 */
LocalSchema.prototype.checkEntry = function(entry, query) {
  var total = this.keys.length;
  var matches = [];

  for (var q = 0; q < total; q++) {
    var key = this.keys[q];
    var queryItem = query[key];
    if (typeof queryItem === 'undefined') {
      continue;
    }

    var isRegex = queryItem instanceof RegExp;
    var checkEmpty = typeof queryItem === 'object' && isEmpty(queryItem);
    if (!isRegex && (queryItem === '' || checkEmpty)) {
      continue;
    }

    if (entry[key]) {
      matches.push(matchQuery(
        LocalDocument.convert(key, entry[key], this.schema),
        queryItem
      ));
    }
  }
  return matches;
};

/**
 * Find entries matching a query
 * @public
 * @param {Object} query
 * @param {Boolean} isCount - return a count when true
 * @returns {Array/Number} an array of matches or
 * a number if isCount = true
 */
LocalSchema.prototype.find = function(query, isCount) {
  this.indices = this.indices || getIndices(this.name, this.options);
  if (!query || isEmpty(query)) {
    return isCount ? this.indices.length : this.all();
  }

  var results = isCount ? 0 : [];

  // Check if the collection is empty
  if (!this.indices) {
    return results;
  }

  for (var i = 0; i < this.indices.length; i++) {
    var entry = this.options.storage.getItem(this.indices[i]);
    var parsed = JSON.parse(entry);
    var matches = this.checkEntry(parsed, query);

    if (matches.length > 0 && !containsFalse(matches)) {
      if (!isCount) {
        results.push(new LocalDocument(parsed, this));
      } else {
        results++;
      }
    }
  }

  this.options.debug.log(results.length + ' results found');

  return results;
};

/**
 * Remove entries utilising the find query
 * @public
 * @param {Object} query
 * @returns {Number} the number of items removed
 */
LocalSchema.prototype.remove = function(query) {
  var entries = this.find(query);

  // Remove each entry individually
  for (var i = 0; i < entries.length; i++ ) {
    entries[i].remove();
  }

  return entries.length;
};

/**
 * Helper to return a count of results
 * @public
 * @param {Object} query
 * @returns {Number} the count of results
 */
LocalSchema.prototype.count = function(query) {
  return this.find(query, true);
};

/**
 * A batch updater
 * @public
 * @param {Object} query - to find entries to update
 * @param {Object} values - the values to update
 * @returns {Number} the number of entries changed
 */
LocalSchema.prototype.update = function(query, values) {
  var entries = this.find(query);
  var totalEntries = entries.length;
  if (totalEntries < 1) {
    return 0;
  }
  for (var i = 0; i < totalEntries; i++) {
    var entry = entries[i];
    var total = this.keys.length;
    for (var s = 0; s < total; s++) {
      var key = this.keys[s];
      if (typeof values[key] !== 'undefined') {
        entry[key] = values[key];
      }
    }
    entry.save();
  }
  return entries.length;
};

/**
 * Finds and the populates the results
 * @public
 * @param {Object} query
 * @param {String} names - space seperated
 * @param {Object} options
 * @returns {Array} of populated results
 */
LocalSchema.prototype.findAndPopulate = function(query, names, options) {
  var entries = this.find(query);
  var totalEntries = entries.length;
  if (totalEntries < 1) {
    return [];
  }
  var populated = [];
  for (var i = 0; i < totalEntries; i++) {
    populated.push(entries[i].populate(names, options));
  }
  return populated;
};

/**
 * contains the ability to match and group documents
 * @param {Array} pipeline - an array of queries
 * @returns {Array} data reduced by the pipeline
 */
LocalSchema.prototype.aggregate = function(pipeline) {
  var data = this.all();
  var total = pipeline.length;
  for (var i = 0; i < total; i++) {
    var query = pipeline[i];
    if (query.$match) {
      // Do a filter on data
    } else if(query.$group) {
      // add an _id
      // add properties (i.e. total)
      // add $sum ability
      // http://docs.mongodb.org/manual/reference/operator/aggregation/group/#pipe._S_group
    } else if(query.$sort) {
      // Sort the array
    } else if(query.$limit) {
      // limit the array
    } else {
      console.error('Aggregate currently only supports ' +
        '$match, $group, $sort & $limit query types');
      return;
    }

  }
};

/**
 * LocalSchema Schema Types
 * For use in validation and return
 * @public
 */
LocalSchema.SchemaTypes = {
  String: 'string',
  Number: 'number',
  Boolean: 'boolean',
  Mixed: 'mixed',
  Date: 'date'
};
<|endoftext|>


<commit_before>var fs = require('fs');

module.exports = Game;

function Game(){
    this.game = loadGame();
    this.unlockNextRow();
}

Game.prototype.getQuestion = function(){
    var available = getAvailable(this.game);

    if(available.length === 0){
        this.unlockNextRow();
        available = getAvailable(this.game);
    }

    var fields = Math.random() > 0.5 ? ['sound', 'kana'] : ['kana', 'sound'],
        questionField = fields[0], answerField = fields[1];

    var questionKana = pickQuestion(available);

    question = {
        kana: questionKana,
        question: questionKana[questionField],
        answer: questionKana[answerField]
    };

    var choices = _.chain(available)
                   .filter(function(kana){
                       return kana[answerField] !== question.answer &&
                              kana[questionField] !== question.question;
                    })
                   .sample(3)
                   .pluck(answerField)
                   .value();

    choices.push(question.answer);

    question.choices = _.shuffle(choices);

    return question;
};

Game.prototype.updateScore = function(kana, correct){
    kana.score[correct ? 'correct' : 'incorrect'] += 1;
};

Game.prototype.unlockNextRow = function(){
    var nextToUnlock = _.find(this.game, function(row){
        return !row[0].score && !row[0].locked;
    });

    var allLearned = _.every(getAvailable(this.game), function(kana){
            return kana.score.correct >= 3;
        });

    if (nextToUnlock && allLearned){
        initRow(nextToUnlock);
    }

    saveGame(this.game);
};

function loadGame(){
    var kana = JSON.parse(
        fs.readFileSync(__dirname + '/kana.json', 'utf8')
    );
    return (localStorage && JSON.parse(localStorage.getItem("game"))) ||
           kana.hiragana.concat(kana.katakana);
}

function initRow(row){
    _.forEach(row, function(q){
        q.score = {correct: 0, incorrect: 0};
    });
}

function pickQuestion(options){
    var byAttempts = _.sortBy(options, function(kana){
        return kana.score.correct + kana.score.incorrect;
    }),
        pivot = Math.ceil(byAttempts.length * 0.2),
        recent = _.initial(byAttempts, pivot),
        old = _.last(byAttempts, byAttempts.length - pivot),
        picked = undefined;

    if(Math.random() > 0.2){
        picked = _.sample(recent);
    }
    else{
        picked = _.sample(old);
    }

    return picked;
}

function getAvailable(rows){
    return _.filter(_.flatten(rows), function(kana){
        return kana.score && !kana.locked;
    });
}

function saveGame(game){
    return localStorage && localStorage.setItem("game", JSON.stringify(game));
}
<commit_msg>Linting fix<commit_after>var fs = require('fs');

module.exports = Game;

function Game(){
    this.game = loadGame();
    this.unlockNextRow();
}

Game.prototype.getQuestion = function(){
    var available = getAvailable(this.game);

    if(available.length === 0){
        this.unlockNextRow();
        available = getAvailable(this.game);
    }

    var fields = Math.random() > 0.5 ? ['sound', 'kana'] : ['kana', 'sound'],
        questionField = fields[0], answerField = fields[1];

    var questionKana = pickQuestion(available);

    var question = {
        kana: questionKana,
        question: questionKana[questionField],
        answer: questionKana[answerField]
    };

    var choices = _.chain(available)
                   .filter(function(kana){
                       return kana[answerField] !== question.answer &&
                              kana[questionField] !== question.question;
                    })
                   .sample(3)
                   .pluck(answerField)
                   .value();

    choices.push(question.answer);

    question.choices = _.shuffle(choices);

    return question;
};

Game.prototype.updateScore = function(kana, correct){
    kana.score[correct ? 'correct' : 'incorrect'] += 1;
};

Game.prototype.unlockNextRow = function(){
    var nextToUnlock = _.find(this.game, function(row){
        return !row[0].score && !row[0].locked;
    });

    var allLearned = _.every(getAvailable(this.game), function(kana){
            return kana.score.correct >= 3;
        });

    if (nextToUnlock && allLearned){
        initRow(nextToUnlock);
    }

    saveGame(this.game);
};

function loadGame(){
    var kana = JSON.parse(
        fs.readFileSync(__dirname + '/kana.json', 'utf8')
    );
    return (localStorage && JSON.parse(localStorage.getItem("game"))) ||
           kana.hiragana.concat(kana.katakana);
}

function initRow(row){
    _.forEach(row, function(q){
        q.score = {correct: 0, incorrect: 0};
    });
}

function pickQuestion(options){
    var byAttempts = _.sortBy(options, function(kana){
        return kana.score.correct + kana.score.incorrect;
    }),
        pivot = Math.ceil(byAttempts.length * 0.2),
        recent = _.initial(byAttempts, pivot),
        old = _.last(byAttempts, byAttempts.length - pivot),
        picked = undefined;

    if(Math.random() > 0.2){
        picked = _.sample(recent);
    }
    else{
        picked = _.sample(old);
    }

    return picked;
}

function getAvailable(rows){
    return _.filter(_.flatten(rows), function(kana){
        return kana.score && !kana.locked;
    });
}

function saveGame(game){
    return localStorage && localStorage.setItem("game", JSON.stringify(game));
}
<|endoftext|>


<commit_before>const m = require('mithril');
const specData = require('spec-data');
const singlePart = require('./components/part');
const infoLinks = require('./components/info-links');
const hashMan = require('./hash');
const pure = require('./pure');
const rowData = require('./row-data');
const seo = require('./seo');

// localStorage section display module
const sectionsLs = {
	initialized: localStorage.getItem(`table-section-display-${rowData.sections[0].name}`) !== null,
	get: name => localStorage.getItem(`table-section-display-${name}`) === 'yes',
	set: (name, val) => localStorage.setItem(`table-section-display-${name}`, val ? 'yes' : 'no'),
	toggle: name => sectionsLs.set(name, !sectionsLs.get(name)),
	// returns 1 for all on, -1 for all off, 0 for mixed
	// yeah, I know this isn't java and there's a better way, but this is how I'm gonna do it
	checkAll: () => {
		const firstSectionState = sectionsLs.get(rowData.sections[0].name)
		return rowData.sections.every(c => sectionsLs.get(c.name) === firstSectionState) ? firstSectionState * 2 - 1 /* #winning */: 0;
	},
	setAll: setTo => rowData.sections.map(c => sectionsLs.set(c.name, setTo)),
};

// If unused before, set the section display to the defaults.
if(!sectionsLs.initialized) {
	rowData.sections.forEach(curSection => {
		sectionsLs.set(curSection.name, curSection.display);
	});
}

module.exports = {
	identicalRows: true,
	uncomparableRows: true,
	oncreate: seo.update,
	onupdate: seo.update,
	view: vnode => {
		const partNames = hashMan.getList();
		const partData = partNames.map(c => specData[c]);
		const sections = pure.getTableData(partData, rowData.sections, {
			showIdenticalRows: vnode.state.identicalRows,
			showUncomparableRows: vnode.state.uncomparableRows,
		});
		// filter out advanced rows if necessary
		return [
			(partNames.length === 0 ? [
				m('#nothing-selected', 'No Parts Selected'),
				m('.flex-vertical-spacer'),
				m(infoLinks),
			] : [
				m('h2.centered.top.mt1', 'SELECTED COMPONENTS:'),
				m('#selected-parts-list.flex-wrapper.justify-center', partNames.map(curPartName => m(singlePart, {
					name: curPartName,
					canSelect: false,
				}))),
				m('.hr'),
				m('h2.centered', 'SPECIFICATIONS:'),
				// table options, e.g hide identical rows, advanced rows
				m('.flex-wrapper.justify-center', [
					m('.table-option', {
						class: vnode.state.identicalRows && 'red-selected',
						onclick: () => vnode.state.identicalRows = !vnode.state.identicalRows,
					}, 'Show Identical Rows'),
					m('.table-option', {
						class: vnode.state.uncomparableRows && 'red-selected',
						onclick: () => vnode.state.uncomparableRows = !vnode.state.uncomparableRows,
					}, 'Show Incomplete Rows'),
				]),
				m('.spec-tab-wrapper',
					m('table.spec-tab', [
						// header with part names
						m('tr', [
							m('td.left-corner'),
							partData.map(c => m('th', c.humanName)),
							m('td.table-section-hidden.table-collapse-button', [
								m('.a', { onclick: () => {
									if(sectionsLs.checkAll() === 1) {
										// everything is already shown, hide things
										sectionsLs.setAll(false);
									} else {
										// some or everything is hidden, show everything
										sectionsLs.setAll(true);
									}
								}}, m.trust(`${sectionsLs.checkAll() === 1 ? '-' : '+'} All`)),
							]),
						]),
						// now for real data
						sections.map(curSection => {
							// if we don't have any data for this section, exit now
							if(curSection.rows.length === 0) {
								return;
							}
							return sectionsLs.get(curSection.name) ?
								// section is displayed
								curSection.rows.map((curRow, i) =>
									m('tr', [
										m('td.row-header', curRow.name),
										curRow.cells.map(curCell =>
											m('td', {
												class: curCell.winner ? 'winner' : '',
											// if it's an array, then do line-break separated values, otherwise do just the value
											}, curCell.value instanceof Array ?
												[
													curCell.value[0],
													curCell.value.slice(1).map(c => [ m('br'), c ]),
												]
												: curCell.value
											)
										),
										// include bracket if this is the top row
										i === 0 &&
											m(`td.table-section-hidden.not-that-hidden`, {
												rowspan: curSection.rows.length,
												onclick: () => sectionsLs.toggle(curSection.name),
											},
												[
													m('.a.table-section-label', curSection.name),
													m('.table-section-bracket', [
														m('.bracket-upper-end.bracket-curve'),
														m('.bracket-upper-rect.bracket-rect'),
														m('.bracket-upper-join.bracket-curve'),
														m('.bracket-lower-join.bracket-curve'),
														m('.bracket-lower-rect.bracket-rect'),
														m('.bracket-lower-end.bracket-curve'),
													]),
												]
											),
									])
								) :
								// section is collapsed
								m('tr', { onclick: () => sectionsLs.toggle(curSection.name) }, [
									m('td.table-section-collapsed', {
										// +1 to account for row header thing
										colspan: curSection.rows[0].cells.length + 1,
									},
										m('a', curSection.name)
									),
									m('td.table-section-hidden'),
								])
						}),
					]),
				),
				m(infoLinks),
			]),
		];
	},
}
<commit_msg>Refactor spec-viewer.js<commit_after>const m = require('mithril');
const specData = require('spec-data');
const singlePart = require('./components/part');
const infoLinks = require('./components/info-links');
const hashMan = require('./hash');
const pure = require('./pure');
const rowData = require('./row-data');
const seo = require('./seo');

// localStorage section display module
const sectionsLs = {
	initialized: localStorage.getItem(`table-section-display-${rowData.sections[0].name}`) !== null,
	get: name => localStorage.getItem(`table-section-display-${name}`) === 'yes',
	set: (name, val) => localStorage.setItem(`table-section-display-${name}`, val ? 'yes' : 'no'),
	toggle: name => sectionsLs.set(name, !sectionsLs.get(name)),
	// returns 1 for all on, -1 for all off, 0 for mixed
	// yeah, I know this isn't java and there's a better way, but this is how I'm gonna do it
	checkAll: () => {
		const firstSectionState = sectionsLs.get(rowData.sections[0].name)
		return rowData.sections.every(c => sectionsLs.get(c.name) === firstSectionState) ? firstSectionState * 2 - 1 /* #winning */: 0;
	},
	setAll: setTo => rowData.sections.map(c => sectionsLs.set(c.name, setTo)),
};

// If unused before, set the section display to the defaults.
if(!sectionsLs.initialized) {
	rowData.sections.forEach(curSection => {
		sectionsLs.set(curSection.name, curSection.display);
	});
}

const bracket = {
	view: vnode => {
		const curSection = vnode.attrs.section;
		return m(`td.table-section-hidden.not-that-hidden`, {
			rowspan: curSection.rows.length,
			onclick: () => sectionsLs.toggle(curSection.name),
		},
			[
				m('.a.table-section-label', curSection.name),
				m('.table-section-bracket', [
					m('.bracket-upper-end.bracket-curve'),
					m('.bracket-upper-rect.bracket-rect'),
					m('.bracket-upper-join.bracket-curve'),
					m('.bracket-lower-join.bracket-curve'),
					m('.bracket-lower-rect.bracket-rect'),
					m('.bracket-lower-end.bracket-curve'),
				]),
			]
		);
	},
};

const openRow = {
	view: vnode =>
		m('tr', [
			m('td.row-header', vnode.attrs.row.name),
			vnode.attrs.row.cells.map(curCell =>
				m('td', {
					class: curCell.winner ? 'winner' : '',
				// if it's an array, then do line-break separated values, otherwise do just the value
				}, curCell.value instanceof Array ?
					[
						curCell.value[0],
						curCell.value.slice(1).map(c => [ m('br'), c ]),
					]
					: curCell.value
				)
			),
			// include bracket if this is the top row
			vnode.attrs.isTop && m(bracket, { section: vnode.attrs.section }),
		]),
};

const closedRow = {
	view: vnode => {
		const curSection = vnode.attrs.section;
		return m('tr', { onclick: () => sectionsLs.toggle(curSection.name) }, [
			m('td.table-section-collapsed', {
				// +1 to account for row header thing
				colspan: curSection.rows[0].cells.length + 1,
			},
				m('a', curSection.name)
			),
			m('td.table-section-hidden'),
		]);
	},
}

const section = {
	view: vnode => {
		const curSection = vnode.attrs.section;
		// if we don't have any data for this section, exit now
		if(curSection.rows.length === 0) {
			return;
		}
		return sectionsLs.get(curSection.name) ?
			curSection.rows.map((curRow, i) =>
				m(openRow, { section: curSection, row: curRow, isTop: i === 0 })
			) : m(closedRow, { section: curSection });
	},
};

const toggleAllSectionsButton = {
	view: vnode =>
		m('.a', { onclick: () => {
			if(sectionsLs.checkAll() === 1) {
				// everything is already shown, hide things
				sectionsLs.setAll(false);
			} else {
				// some or everything is hidden, show everything
				sectionsLs.setAll(true);
			}
		}}, m.trust(`${sectionsLs.checkAll() === 1 ? '-' : '+'} All`)),
};

const specTable = {
	view: vnode =>
		m('.spec-tab-wrapper',
			m('table.spec-tab', [
				// header with part names
				m('tr', [
					m('td.left-corner'),
					vnode.attrs.humanNames.map(c => m('th', c)),
					m('td.table-section-hidden.table-collapse-button', m(toggleAllSectionsButton)),
				]),
				// now for real data
				vnode.attrs.sections.map(s => m(section, { section: s })),
			])
		),

};

module.exports = {
	identicalRows: true,
	uncomparableRows: true,
	oncreate: seo.update,
	onupdate: seo.update,
	view: vnode => {
		const partNames = hashMan.getList();
		const partData = partNames.map(c => specData[c]);
		const partHumanNames = partData.map(c => c.humanName);
		const sections = pure.getTableData(partData, rowData.sections, {
			showIdenticalRows: vnode.state.identicalRows,
			showUncomparableRows: vnode.state.uncomparableRows,
		});
		// filter out advanced rows if necessary
		return [
			(partNames.length === 0 ? [
				m('#nothing-selected', 'No Parts Selected'),
				m('.flex-vertical-spacer'),
				m(infoLinks),
			] : [
				m('h2.centered.top.mt1', 'SELECTED COMPONENTS:'),
				m('#selected-parts-list.flex-wrapper.justify-center', partNames.map(curPartName => m(singlePart, {
					name: curPartName,
					canSelect: false,
				}))),
				m('.hr'),
				m('h2.centered', 'SPECIFICATIONS:'),
				// table options, e.g hide identical rows, advanced rows
				m('.flex-wrapper.justify-center', [
					m('.table-option', {
						class: vnode.state.identicalRows && 'red-selected',
						onclick: () => vnode.state.identicalRows = !vnode.state.identicalRows,
					}, 'Show Identical Rows'),
					m('.table-option', {
						class: vnode.state.uncomparableRows && 'red-selected',
						onclick: () => vnode.state.uncomparableRows = !vnode.state.uncomparableRows,
					}, 'Show Incomplete Rows'),
				]),
				m(specTable, { sections, humanNames: partHumanNames }),
				m(infoLinks),
			]),
		];
	},
}
<|endoftext|>


<commit_before>
import Expression, { SubQuery } from '../expression'
import { isArray, isEmpty } from 'lodash'
import Criterion from './base'

/**
 * @class IsInCriterion
 */
export default class IsIn extends Criterion {

  /**
   *
   * @param {Expression} column
   * @param {Array|SubQuery} values
   * @constructor
   */
  constructor(expr, values) {
    super()

    if (!
      (expr instanceof Expression &&
      (isArray(values) || values instanceof SubQuery))
    )
      throw new TypeError("Invalid `in` condition")

    this.op = 'in'
    this.operand = expr
    this.values = values
  }

  /**
   *
   * @returns {IsIn}
   */
  negate() {
    this.op = (this.op === 'in') ? 'not in' : 'in'
    return this
  }

  /**
   *
   * @param {Compiler} compiler
   * @returns {String}
   */
  compile(compiler) {
    if ( isArray(this.values) && isEmpty(this.values) )
      return `1 = ${(this.op === 'in') ? 0 : 1}`

    var operand = this.operand.compile(compiler)
    var values = compiler.parameterize(this.values)

    // wrap the array values with parentheses
    if ( isArray(this.values) ) values = `(${values})`

    return `${this.bool} ${operand} ${this.op} ${values}`
  }

}<commit_msg>Update criterion/is-in.js<commit_after>
import Expression, { SubQuery } from '../expression'
import { isArray, isEmpty } from 'lodash'
import Criterion from './basic'

/**
 * @class IsInCriterion
 */
export default class IsIn extends Criterion {

  /**
   *
   * @param {Expression} expr
   * @param {Array|SubQuery} values
   * @constructor
   */
  constructor(expr, values) {
    if (!
      (expr instanceof Expression &&
      (isArray(values) || values instanceof SubQuery))
    )
      throw new TypeError("Invalid `in` condition")

    super(expr, 'in', values)
  }

  /**
   *
   * @returns {IsIn}
   */
  negate() {
    this.op = (this.op === 'in') ? 'not in' : 'in'
    return this
  }

  /**
   *
   * @param {Compiler} compiler
   * @returns {String}
   */
  compile(compiler) {
    if ( isArray(this.values) && isEmpty(this.values) )
      return `1 = ${(this.op === 'in') ? 0 : 1}`

    return super.compile(compiler)
  }

}<|endoftext|>


<commit_before>var c, gl;

var color = [];
var indices = [];
var max = 100000
var ext;

function initWebGL() {
    c = document.getElementById("c");
    gl = c.getContext("experimental-webgl");
    ext = gl.getExtension("OES_element_index_uint");
    if (!ext) alert('no! OES_element_index_uint')
    console.log(ext)
    ext = gl.getExtension('ANGLE_instanced_arrays');
    if (!ext) alert('no! ANGLE_instanced_arrays')
    gl.viewport(0, 0, 1280, 800)
}
var p;
function initShaders() {
    p = gl.createProgram();
    var v = document.getElementById("vs").textContent;
    var f = document.getElementById("fs").textContent;
    var vs = gl.createShader(gl.VERTEX_SHADER);
    var fs = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(vs, v);
    gl.shaderSource(fs, f);
    gl.compileShader(vs);
    gl.compileShader(fs);
    gl.attachShader(p, vs);
    gl.attachShader(p, fs);
    gl.linkProgram(p);
    gl.useProgram(p);
    p.aVertexPosition = gl.getAttribLocation(p, "aVertexPosition");

    p.instancePosition = gl.getAttribLocation(p, "instancePosition");
    p.instanceRotation = gl.getAttribLocation(p, "instanceRotation");

    p.color = gl.getAttribLocation(p, "color");

    p.pixelMatrix = gl.getUniformLocation(p, 'pixelMatrix');
    p.uScale = gl.getUniformLocation(p, 'uScale');

    gl.enableVertexAttribArray(p.aVertexPosition);

    gl.enableVertexAttribArray(p.instancePosition);
    gl.enableVertexAttribArray(p.instanceRotation);
    gl.enableVertexAttribArray(p.color);

}
var instancePositions = [];
var instanceRotations = [];
var instanceColors = [];
var offsetPosition = 3;
var offsetColor = 4
function initBuffers() {

    var data = [
        -0.5, -0.5, 0.0,
        -0.5, 0.5, 0.0,
        0.5, -0.5, 0.0,
        0.5, 0.5, 0.0
    ];

    indices = [
        0, 1, 2,
        1, 2, 3
    ];

    var pos = 0;

    for (var i = 0; i < max; i++) {
        instancePositions[pos * offsetPosition] = 0;
        instancePositions[pos * offsetPosition + 1] = 0
        instancePositions[pos * offsetPosition + 2] = Math.random() * 10000 + 2500

        instanceRotations[pos * offsetPosition] = Math.random()
        instanceRotations[pos * offsetPosition+ 1] = Math.random()
        instanceRotations[pos * offsetPosition+ 2] = Math.random()

        instanceColors[pos * offsetColor] = Math.random()
        instanceColors[pos * offsetColor + 1] = Math.random()
        // 2D일경우에는 그냥..곱해버릴까 -_-;;
        instanceColors[pos * offsetColor + 2] = Math.random()
        instanceColors[pos * offsetColor + 3] = 0.3;

        pos++;
    }

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
    gl.vertexAttribPointer(p.aVertexPosition, 3, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(instancePositions), gl.STATIC_DRAW);
    gl.vertexAttribPointer(p.instancePosition, 3, gl.FLOAT, false, 0, 0);
    ext.vertexAttribDivisorANGLE(p.instancePosition, 1)

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(instanceRotations), gl.STATIC_DRAW);
    gl.vertexAttribPointer(p.instanceRotation, 3, gl.FLOAT, false, 0, 0);
    ext.vertexAttribDivisorANGLE(p.instanceRotation, 1)

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(instanceColors), gl.STATIC_DRAW);
    gl.vertexAttribPointer(p.color, 4, gl.FLOAT, false, 0, 0);
    ext.vertexAttribDivisorANGLE(p.color, 1)

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indices), gl.STATIC_DRAW);
}
//
function animate() {
    render();
    requestAnimationFrame(animate);
}

var pos = 0;
var time = 0
function render() {
    gl.clearColor(1, 0.5, 0.5, 1)
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    gl.enable(gl.BLEND), gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

    time += 0.003
    for (var i = 0; i < max; i++) {
        instancePositions[i * offsetPosition] =  i;
        instancePositions[i * offsetPosition + 1] = i * time *0.1
    }

    var mtx
    var fieldOfViewY = 45 * Math.PI / 180
    var aspectRatio = 1280 / 800
    var zNear = 1
    var zFar = 10000000
    var yScale = 1.0 / Math.tan(fieldOfViewY / 2.0);
    var xScale = yScale / aspectRatio;
    mtx = [
        xScale, 0, 0, 0,
        0, -yScale, 0, 0,
        0, 0, zFar / (zFar - zNear), 1,
        0, 0, (zNear * zFar) / (zNear - zFar), 1
    ]

    gl.uniform3fv(p.uScale, [32, 32, 1])
    gl.uniformMatrix4fv(p.pixelMatrix, false, mtx)

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(instancePositions), gl.DYNAMIC_DRAW);
    gl.vertexAttribPointer(p.instancePosition, 3, gl.FLOAT, false, 0, 0);
    ext.vertexAttribDivisorANGLE(p.instancePosition, 1)
    ext.drawElementsInstancedANGLE(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0, max);
    //gl.flush();
}

initWebGL();
initShaders();
initBuffers();
animate();
<commit_msg>먼가그러고보니 버벅거리네;;;<commit_after>var c, gl;

var color = [];
var indices = [];
var max = 100000
var ext;

function initWebGL() {
    c = document.getElementById("c");
    gl = c.getContext("experimental-webgl");
    ext = gl.getExtension("OES_element_index_uint");
    if (!ext) alert('no! OES_element_index_uint')
    console.log(ext)
    ext = gl.getExtension('ANGLE_instanced_arrays');
    if (!ext) alert('no! ANGLE_instanced_arrays')
    gl.viewport(0, 0, 1280, 800)
}
var p;
function initShaders() {
    p = gl.createProgram();
    var v = document.getElementById("vs").textContent;
    var f = document.getElementById("fs").textContent;
    var vs = gl.createShader(gl.VERTEX_SHADER);
    var fs = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(vs, v);
    gl.shaderSource(fs, f);
    gl.compileShader(vs);
    gl.compileShader(fs);
    gl.attachShader(p, vs);
    gl.attachShader(p, fs);
    gl.linkProgram(p);
    gl.useProgram(p);
    p.aVertexPosition = gl.getAttribLocation(p, "aVertexPosition");

    p.instancePosition = gl.getAttribLocation(p, "instancePosition");
    p.instanceRotation = gl.getAttribLocation(p, "instanceRotation");

    p.color = gl.getAttribLocation(p, "color");

    p.pixelMatrix = gl.getUniformLocation(p, 'pixelMatrix');
    p.uScale = gl.getUniformLocation(p, 'uScale');

    gl.enableVertexAttribArray(p.aVertexPosition);

    gl.enableVertexAttribArray(p.instancePosition);
    gl.enableVertexAttribArray(p.instanceRotation);
    gl.enableVertexAttribArray(p.color);

}
var instancePositions = [];
var instanceRotations = [];
var instanceColors = [];
var offsetPosition = 3;
var offsetColor = 4

var positionBuffer
function initBuffers() {

    var data = [
        -0.5, -0.5, 0.0,
        -0.5, 0.5, 0.0,
        0.5, -0.5, 0.0,
        0.5, 0.5, 0.0
    ];

    indices = [
        0, 1, 2,
        1, 2, 3
    ];

    var pos = 0;

    for (var i = 0; i < max; i++) {
        instancePositions[pos * offsetPosition] = 0;
        instancePositions[pos * offsetPosition + 1] = 0
        instancePositions[pos * offsetPosition + 2] = Math.random() * 10000 + 2500

        instanceRotations[pos * offsetPosition] = Math.random()
        instanceRotations[pos * offsetPosition+ 1] = Math.random()
        instanceRotations[pos * offsetPosition+ 2] = Math.random()

        instanceColors[pos * offsetColor] = Math.random()
        instanceColors[pos * offsetColor + 1] = Math.random()
        // 2D일경우에는 그냥..곱해버릴까 -_-;;
        instanceColors[pos * offsetColor + 2] = Math.random()
        instanceColors[pos * offsetColor + 3] = 0.3;

        pos++;
    }

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
    gl.vertexAttribPointer(p.aVertexPosition, 3, gl.FLOAT, false, 0, 0);

    positionBuffer =  gl.createBuffer()
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(instancePositions), gl.STATIC_DRAW);
    gl.vertexAttribPointer(p.instancePosition, 3, gl.FLOAT, false, 0, 0);
    ext.vertexAttribDivisorANGLE(p.instancePosition, 1)

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(instanceRotations), gl.STATIC_DRAW);
    gl.vertexAttribPointer(p.instanceRotation, 3, gl.FLOAT, false, 0, 0);
    ext.vertexAttribDivisorANGLE(p.instanceRotation, 1)

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(instanceColors), gl.STATIC_DRAW);
    gl.vertexAttribPointer(p.color, 4, gl.FLOAT, false, 0, 0);
    ext.vertexAttribDivisorANGLE(p.color, 1)

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indices), gl.STATIC_DRAW);
}
//
function animate() {
    render();
    requestAnimationFrame(animate);
}

var pos = 0;
var time = 0
function render() {
    gl.clearColor(0, 0, 0, 1)
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    gl.enable(gl.BLEND), gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

    time += 0.003
    for (var i = 0; i < max; i++) {
        instancePositions[i * offsetPosition] =  i;
        instancePositions[i * offsetPosition + 1] = i * time *0.1
    }

    var mtx
    var fieldOfViewY = 55 * Math.PI / 180
    var aspectRatio = 1280 / 800
    var zNear = 1
    var zFar = 10000000
    var yScale = 1.0 / Math.tan(fieldOfViewY / 2.0);
    var xScale = yScale / aspectRatio;
    mtx = [
        xScale, 0, 0, 0,
        0, -yScale, 0, 0,
        0, 0, zFar / (zFar - zNear), 1,
        0, 0, (zNear * zFar) / (zNear - zFar), 1
    ]

    gl.uniform3fv(p.uScale, [100, 100, 1])
    gl.uniformMatrix4fv(p.pixelMatrix, false, mtx)

    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(instancePositions), gl.DYNAMIC_DRAW);
    gl.vertexAttribPointer(p.instancePosition, 3, gl.FLOAT, false, 0, 0);
    ext.vertexAttribDivisorANGLE(p.instancePosition, 1)

    ext.drawElementsInstancedANGLE(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0, max);
    //gl.flush();
}

initWebGL();
initShaders();
initBuffers();
animate();
<|endoftext|>


<commit_before>
var AppWindow = function(startUrl, settings) {
    this.startUrl = startUrl;
    this.win_= null;
    this.webview = null;
    this.settings = settings;

    this.createWindow();
};

// Create a new window. Defer navigating the webview until the DOM is loaded.
AppWindow.prototype.createWindow = function() {
    chrome.app.window.create('window.html', {
        bounds: {
            width: this.settings.width,
            height: this.settings.height
        },
        frame: 'chrome'
    }, function(win) {
        this.win = win;
        this.win.contentWindow.addEventListener('DOMContentLoaded', this.onLoad.bind(this));
        this.win.onBoundsChanged.addListener(this.onBoundsChanged.bind(this));
    }.bind(this));
}

// Resize the window's webview to the window's size and load the start URL.
AppWindow.prototype.onLoad = function() {
    this.webview = this.win.contentWindow.document.getElementById('webview');
    this.webview.addEventListener('newwindow', this.onNewWindow.bind(this));

    this.onBoundsChanged();
    this.loadUrl(this.startUrl);
}

// Update this window's cached bounds and, if the window has been resized as
// opposed to just moved, also update the global app's default window size.
AppWindow.prototype.onBoundsChanged = function() {
    var bounds = this.win.getBounds(), change = false;

    if (this.settings.minWidth && bounds.width < this.settings.minWidth) {
        change = true;
        bounds.width = this.settings.minWidth;
    }
    if (this.settings.minHeight && bounds.height < this.settings.minHeight) {
        change = true;
        bounds.height = this.settings.minHeight;
    }
    if (this.settings.maxWidth && bounds.width > this.settings.maxWidth) {
        change = true;
        bounds.width = this.settings.maxWidth;
    }
    if (this.settings.maxHeight && bounds.height > this.settings.maxHeight) {
        change = true;
        bounds.height = this.settings.maxHeight;
    }

    if (change) this.win.setBounds(bounds);

    this.webview.style.height = bounds.height + 'px';
    this.webview.style.width = bounds.width + 'px';
}

// Fired when the guest page attempts to open a new browser window.
AppWindow.prototype.onNewWindow = function(e) {
    if (e.name == "popup") {
        var newwin = new AppWindow(e.targetUrl, {
            width: 1000,
            height: 600
        });
    } else {
        window.open(e.targetUrl);
    }
};

// Navigate the window's webview to an  URL.
AppWindow.prototype.loadUrl = function(url) {
    this.webview.partition = this.settings.partition || "codebox";
    this.webview.src = url;
};

chrome.app.runtime.onLaunched.addListener(function(launchData) {
    var mainWin = new AppWindow("https://www.codebox.io/app/", {
        width: 400,
        height: 500,
        maxWidth: 400,
        minWidth: 400,
        minHeight: 500
    })
});<commit_msg>Fix auth in chrome app<commit_after>
var AppWindow = function(startUrl, settings) {
    this.startUrl = startUrl;
    this.win_= null;
    this.webview = null;
    this.settings = settings;

    this.createWindow();
};

// Create a new window. Defer navigating the webview until the DOM is loaded.
AppWindow.prototype.createWindow = function() {
    chrome.app.window.create('window.html', {
        bounds: {
            width: this.settings.width,
            height: this.settings.height
        },
        frame: 'chrome'
    }, function(win) {
        this.win = win;
        this.win.contentWindow.addEventListener('DOMContentLoaded', this.onLoad.bind(this));
        this.win.onBoundsChanged.addListener(this.onBoundsChanged.bind(this));
    }.bind(this));
}

// Resize the window's webview to the window's size and load the start URL.
AppWindow.prototype.onLoad = function() {
    this.webview = this.win.contentWindow.document.getElementById('webview');
    this.webview.addEventListener('newwindow', this.onNewWindow.bind(this));

    this.onBoundsChanged();
    this.loadUrl(this.startUrl);
}

// Update this window's cached bounds and, if the window has been resized as
// opposed to just moved, also update the global app's default window size.
AppWindow.prototype.onBoundsChanged = function() {
    var bounds = this.win.getBounds(), change = false;

    if (this.settings.minWidth && bounds.width < this.settings.minWidth) {
        change = true;
        bounds.width = this.settings.minWidth;
    }
    if (this.settings.minHeight && bounds.height < this.settings.minHeight) {
        change = true;
        bounds.height = this.settings.minHeight;
    }
    if (this.settings.maxWidth && bounds.width > this.settings.maxWidth) {
        change = true;
        bounds.width = this.settings.maxWidth;
    }
    if (this.settings.maxHeight && bounds.height > this.settings.maxHeight) {
        change = true;
        bounds.height = this.settings.maxHeight;
    }

    if (change) this.win.setBounds(bounds);

    this.webview.style.height = bounds.height + 'px';
    this.webview.style.width = bounds.width + 'px';
}

// Fired when the guest page attempts to open a new browser window.
AppWindow.prototype.onNewWindow = function(e) {
    if (e.name == "popup") {
        var newwin = new AppWindow(e.targetUrl, {
            width: 1000,
            height: 600
        });
    } else {
        window.open(e.targetUrl);
    }
};

// Navigate the window's webview to an  URL.
AppWindow.prototype.loadUrl = function(url) {
    this.webview.partition = this.settings.partition || "persist:codebox";
    this.webview.src = url;
};

chrome.app.runtime.onLaunched.addListener(function(launchData) {
    var mainWin = new AppWindow("https://www.codebox.io/app/", {
        width: 400,
        height: 500,
        maxWidth: 400,
        minWidth: 400,
        minHeight: 500
    })
});<|endoftext|>


<commit_before>/*
 jQuery.WordWrapper.js

 https://github.com/Ohar/jQuery.WordWrapper.js

 Copyright 2015, Pavel Lysenko

 Released under the MIT license.
 http://www.opensource.org/licenses/mit-license.php

 Version: v 0.1
 Date: Thu Apr 16, 2015
 */

(function ($) {
	$.fn.wordwrapper = function (options) {

		var vowels = ['а', 'е', 'ё', 'и', 'о', 'у', 'ы', 'э', 'ю', 'я'],
			consonants = ['б', 'в', 'г', 'д', 'ж', 'з', 'й', 'к', 'л', 'м', 'н', 'п',
				'р', 'с', 'т', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ь'],
			SOFT_HYPHEN = "\u00AD";

		options = $.extend({}, options);

		return this.each(function () {
			var $this = $(this);

			wordwrapper($this);

			function wordwrapper(e) {
				var text = e.text(),
					words = text.split(' ');

				words.forEach(function(word, i, words_arr){
					var subwords = word.split('-');
					subwords.forEach(function(subword, j, subwords_arr){
						var letters = subword.split('');
						letters.forEach(function(letter, k, letters_arr){
							if (vowels.indexOf(letter) + 1) {
								letters_arr[k] = letter + SOFT_HYPHEN;
							}
						});
						subword = letters.join('');
						subwords_arr[j] = subword;
					});
					word = subwords.join('-');
					words_arr[i] = word;
				});
				text = words.join('');
				e.text(text);
			}
		});

	};
})(jQuery);<commit_msg>Fix error: missing space in word joining<commit_after>/*
 jQuery.WordWrapper.js

 https://github.com/Ohar/jQuery.WordWrapper.js

 Copyright 2015, Pavel Lysenko

 Released under the MIT license.
 http://www.opensource.org/licenses/mit-license.php

 Version: v 0.1
 Date: Thu Apr 16, 2015
 */

(function ($) {
	$.fn.wordwrapper = function (options) {

		var vowels = ['а', 'е', 'ё', 'и', 'о', 'у', 'ы', 'э', 'ю', 'я'],
			consonants = ['б', 'в', 'г', 'д', 'ж', 'з', 'й', 'к', 'л', 'м', 'н', 'п',
				'р', 'с', 'т', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ь'],
			SOFT_HYPHEN = "\u00AD";

		options = $.extend({}, options);

		return this.each(function () {
			var $this = $(this);

			wordwrapper($this);

			function wordwrapper(e) {
				var text = e.text(),
					words = text.split(' ');

				words.forEach(function(word, i, words_arr){
					var subwords = word.split('-');
					subwords.forEach(function(subword, j, subwords_arr){
						var letters = subword.split('');
						letters.forEach(function(letter, k, letters_arr){
							if (vowels.indexOf(letter) + 1) {
								letters_arr[k] = letter + SOFT_HYPHEN;
							}
						});
						subword = letters.join('');
						subwords_arr[j] = subword;
					});
					word = subwords.join('-');
					words_arr[i] = word;
				});
				text = words.join(' ');
				e.text(text);
			}
		});

	};
})(jQuery);<|endoftext|>


<commit_before>/*

Media Query - jQuery Plugin

Copyright (c) 2011 Brothers Roloff
http://www.brothersroloff.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

(function($) {
	$.mediaquery = function(options) {
		if(!options) options = [];
		var head = $('head');
		var link = link = head.append('<link rel="stylesheet" rel="text/css" media="all" href="#">').children().last();
		$(window).resize(function() {
			var viewport = {
				width: $('body').outerWidth(),
				height: $('body').height()
			};
			$.each(options, function(index, value) {
				if(viewport.width+17 >= value.minWidth && viewport.width+17 <= value.maxWidth) {
					link.attr('href', value.href);
				}
			});
		});
		$(window).resize();
	}
})(jQuery);<commit_msg>media query to work with less css files<commit_after>less.env ='development';
(function(a){
    a.mediaquery=function(b){
        b||(b=[]);
        var d=d=a("head").append('<link rel="stylesheet/less" type="text/css" media="all" href="#">').children().last();
        var actual = {
            href: d.attr('href')
        }
        a(window).resize(function(){
            var old = {
                href: $("head link[type='text/css']:last").attr('href')
            }
            var e={
                width:a("body").outerWidth(),
                height:a("body").height()
            };
            a.each(b,function(f,c){
                if (e.width+17>=c.minWidth && e.width+17<=c.maxWidth) {
                    actual = c;
                }
            })
            if (actual.href!=old.href) {
                d.attr("href",actual.href);
                if (less.env == 'development') {
                    console.log('changing_stylesheet to ' + actual.href);
                }
                less.sheets.push($('link[href="'+actual.href+'"]')[0]);
                less.refresh();

            }
        });
        a(window).resize()

    }
})(jQuery);

/*

Less Modification by elod@moldva.hu

Media Query - jQuery Plugin

Copyright (c) 2011 Brothers Roloff
http://www.brothersroloff.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/<|endoftext|>


<commit_before>/*
    Library by Daniel Cohen Gindi (danielgindi@gmail.com)
    MIT License!
*/
(function(b){function m(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}var e=(1.1).toLocaleString().match(/\d(.*?)\d/)[1],n=new RegExp("\\"+m(e),"g");b.fn.numeric=function(a){this.each(function(f,d){var c="INPUT"===d.tagName;"boolean"===typeof a&&(a={decimal:a});a=a||{};"undefined"===typeof a.negative&&(a.negative=c?d.getAttribute("min")?0>parseFloat(d.getAttribute("min")):!0:!0);var g=!1===a.decimal?"":a.decimal||e,k=!!a.negative;g&&"INPUT"===this.tagName&&"number"===this.type&&
"valueAsNumber"in this&&(g=e);"undefined"===typeof a.decimal&&c&&(c=d.getAttribute("step"))&&"any"!==c&&-1===c.indexOf(".")&&(g="");return b(d).data("numeric.decimal",g).data("numeric.negative",k).on("keypress.numericValue",b.fn.numeric.a).on("keyup.numericValue",b.fn.numeric.a).on("blur.numericValue",b.fn.numeric.a).on("input.numericValue",b.fn.numeric.a)});return this};b.fn.valueAsNumber=function(){if(!this.length)return null;var a=arguments;if(a.length)this.each(function(){"INPUT"===this.tagName&&
"number"===this.type&&"valueAsNumber"in this?this.valueAsNumber=a[0]:this.value=a[0].toString().replace(/\./,b.data(this,"numeric.decimal")||e)});else{if("INPUT"===this[0].tagName&&"number"===this[0].type&&"valueAsNumber"in this[0])return this[0].valueAsNumber;var f=this.data("numeric.decimal"),f=f?new RegExp(m(f),"g"):n;return""===this[0].value?null:parseFloat(this[0].value.replace(f,"."))}};b.fn.numeric.a=function(){var a=b.data(this,"numeric.decimal");var f=b.data(this,"numeric.negative"),d=this.value,
c="",g=!1;if("number"!==this.type)var k=this.selectionStart,e=this.selectionEnd;a&&m(a);a&&m(a);for(var h=0,l;h<d.length;h++)l=d.charAt(h),"0"<=l&&"9">=l||f&&"-"===l&&!c.length?c+=l:a&&d.substr(h,a.length)===a&&!g?(g=!0,c+=a,h+=a.length-1):(h<=k&&k--,h<=e&&e--);d!==c&&(this.value=c,"number"!==this.type&&(this.selectionStart=k,this.selectionEnd=e))};b.fn.removeNumeric=function(){return this.removeData("numeric.decimal").removeData("numeric.negative").off(".numericValue")}})(jQuery);
<commit_msg>Correctly return `this` from each<commit_after>/*
    Library by Daniel Cohen Gindi (danielgindi@gmail.com)
    MIT License!
*/
(function(b){function m(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}var e=(1.1).toLocaleString().match(/\d(.*?)\d/)[1],n=new RegExp("\\"+m(e),"g");b.fn.numeric=function(a){this.each(function(f,d){var c="INPUT"===d.tagName;"boolean"===typeof a&&(a={decimal:a});a=a||{};"undefined"===typeof a.negative&&(a.negative=c?d.getAttribute("min")?0>parseFloat(d.getAttribute("min")):!0:!0);var g=!1===a.decimal?"":a.decimal||e,k=!!a.negative;g&&"INPUT"===this.tagName&&"number"===this.type&&
"valueAsNumber"in this&&(g=e);"undefined"===typeof a.decimal&&c&&(c=d.getAttribute("step"))&&"any"!==c&&-1===c.indexOf(".")&&(g="");return b(d).data("numeric.decimal",g).data("numeric.negative",k).on("keypress.numericValue",b.fn.numeric.a).on("keyup.numericValue",b.fn.numeric.a).on("blur.numericValue",b.fn.numeric.a).on("input.numericValue",b.fn.numeric.a)});return this};b.fn.valueAsNumber=function(){if(!this.length)return null;var a=arguments;if(a.length)return this.each(function(){"INPUT"===this.tagName&&
"number"===this.type&&"valueAsNumber"in this?this.valueAsNumber=a[0]:this.value=a[0].toString().replace(/\./,b.data(this,"numeric.decimal")||e)});else{if("INPUT"===this[0].tagName&&"number"===this[0].type&&"valueAsNumber"in this[0])return this[0].valueAsNumber;var f=this.data("numeric.decimal"),f=f?new RegExp(m(f),"g"):n;return""===this[0].value?null:parseFloat(this[0].value.replace(f,"."))}};b.fn.numeric.a=function(){var a=b.data(this,"numeric.decimal");var f=b.data(this,"numeric.negative"),d=this.value,
c="",g=!1;if("number"!==this.type)var k=this.selectionStart,e=this.selectionEnd;a&&m(a);a&&m(a);for(var h=0,l;h<d.length;h++)l=d.charAt(h),"0"<=l&&"9">=l||f&&"-"===l&&!c.length?c+=l:a&&d.substr(h,a.length)===a&&!g?(g=!0,c+=a,h+=a.length-1):(h<=k&&k--,h<=e&&e--);d!==c&&(this.value=c,"number"!==this.type&&(this.selectionStart=k,this.selectionEnd=e))};b.fn.removeNumeric=function(){return this.removeData("numeric.decimal").removeData("numeric.negative").off(".numericValue")}})(jQuery);
<|endoftext|>


<commit_before>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

import Snakeskin from '../core';

Snakeskin.addDirective(
	'break',

	{
		deferInit: true
	},

	function (command) {
		const combo = this.getGroup('cycle', 'async');
		combo['proto'] = true;

		const
			cycles = this.getGroup('cycle'),
			async = this.getGroup('async');

		const
			inside = this.has(combo),
			insideCallback = this.has(this.getGroup('callback')),
			insideProto = inside === 'proto' || this.proto;

		if (!cycles[inside] && !async[inside] && !insideProto) {
			return this.error(`the directive "${this.name}" can be used only with a cycles, protos or an async series`);
		}

		this.startInlineDir();
		if (!this.tolerateWhitespace) {
			this.skipSpace = true;
		}

		if (this.isReady()) {
			if (command === 'proto') {
				if (!insideProto) {
					return this.error('the proto is not defined');
				}

				if (insideCallback) {
					return this.error('can\'t break the proto inside a callback');
				}

				this.append(this.out('break __I_PROTO__;', {sys: true}));
				return;
			}

			if (cycles[inside]) {
				if (inside === insideCallback) {
					this.append('return false;');

				} else {
					this.append('break;');
				}

			} else if (async[inside]) {
				const
					val = command ? this.out(command, {sys: true}) : 'false';

				if (inside === 'waterfall') {
					this.append(`return arguments[arguments.length - 1](${val});`);

				} else {
					this.append(ws`
						if (typeof arguments[0] === 'function') {
							return arguments[0](${val});
						}

						return false;
					`);
				}

			} else {
				this.append(this.out('break __I_PROTO__;', {sys: true}));
			}
		}
	}
);

Snakeskin.addDirective(
	'continue',

	{
		deferInit: true
	},

	function (command) {
		const combo = this.getGroup('cycle', 'async');
		combo['proto'] = true;

		const
			cycles = this.getGroup('cycle'),
			async = this.getGroup('async');

		const
			inside = this.has(combo),
			insideCallback = this.has(this.getGroup('callback')),
			insideProto = inside === 'proto' || this.proto;

		if (!cycles[inside] && !async[inside] && !insideProto) {
			return this.error(`the directive "${this.name}" can be used only with a cycles, protos or an async series`);
		}

		this.startInlineDir();
		if (!this.tolerateWhitespace) {
			this.skipSpace = true;
		}

		if (this.isReady()) {
			if (command === 'proto') {
				if (!insideProto) {
					return this.error(`the proto is not defined`);
				}

				if (insideCallback) {
					return this.error('can\'t continue the proto inside a callback');
				}

				this.append(this.out('continue __I_PROTO__;', {sys: true}));
				return;
			}

			if (cycles[inside]) {
				if (inside === insideCallback) {
					this.append('return;');

				} else {
					this.append('continue;');
				}

			} else if (async[inside]) {
				const
					val = command ? `undefined,${this.out(command, {sys: true})}` : '';

				if (inside === 'waterfall') {
					this.append(`return arguments[arguments.length - 1](${val});`);

				} else {
					this.append(ws`
						if (typeof arguments[0] === 'function') {
							return arguments[0](${val});
						}

						return;
					`);
				}

			} else {
				this.append(this.out('continue __I_PROTO__;', {sys: true}));
			}
		}
	}
);
<commit_msg>Added group control<commit_after>'use strict';

/*!
 * Snakeskin
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 */

import Snakeskin from '../core';

Snakeskin.addDirective(
	'break',

	{
		deferInit: true,
		group: 'control'
	},

	function (command) {
		const combo = this.getGroup('cycle', 'async');
		combo['proto'] = true;

		const
			cycles = this.getGroup('cycle'),
			async = this.getGroup('async');

		const
			inside = this.has(combo),
			insideCallback = this.has(this.getGroup('callback')),
			insideProto = inside === 'proto' || this.proto;

		if (!cycles[inside] && !async[inside] && !insideProto) {
			return this.error(`the directive "${this.name}" can be used only with a cycles, protos or an async series`);
		}

		this.startInlineDir();
		if (!this.tolerateWhitespace) {
			this.skipSpace = true;
		}

		if (this.isReady()) {
			if (command === 'proto') {
				if (!insideProto) {
					return this.error('the proto is not defined');
				}

				if (insideCallback) {
					return this.error('can\'t break the proto inside a callback');
				}

				this.append(this.out('break __I_PROTO__;', {sys: true}));
				return;
			}

			if (cycles[inside]) {
				if (inside === insideCallback) {
					this.append('return false;');

				} else {
					this.append('break;');
				}

			} else if (async[inside]) {
				const
					val = command ? this.out(command, {sys: true}) : 'false';

				if (inside === 'waterfall') {
					this.append(`return arguments[arguments.length - 1](${val});`);

				} else {
					this.append(ws`
						if (typeof arguments[0] === 'function') {
							return arguments[0](${val});
						}

						return false;
					`);
				}

			} else {
				this.append(this.out('break __I_PROTO__;', {sys: true}));
			}
		}
	}
);

Snakeskin.addDirective(
	'continue',

	{
		deferInit: true,
		group: 'control'
	},

	function (command) {
		const combo = this.getGroup('cycle', 'async');
		combo['proto'] = true;

		const
			cycles = this.getGroup('cycle'),
			async = this.getGroup('async');

		const
			inside = this.has(combo),
			insideCallback = this.has(this.getGroup('callback')),
			insideProto = inside === 'proto' || this.proto;

		if (!cycles[inside] && !async[inside] && !insideProto) {
			return this.error(`the directive "${this.name}" can be used only with a cycles, protos or an async series`);
		}

		this.startInlineDir();
		if (!this.tolerateWhitespace) {
			this.skipSpace = true;
		}

		if (this.isReady()) {
			if (command === 'proto') {
				if (!insideProto) {
					return this.error(`the proto is not defined`);
				}

				if (insideCallback) {
					return this.error('can\'t continue the proto inside a callback');
				}

				this.append(this.out('continue __I_PROTO__;', {sys: true}));
				return;
			}

			if (cycles[inside]) {
				if (inside === insideCallback) {
					this.append('return;');

				} else {
					this.append('continue;');
				}

			} else if (async[inside]) {
				const
					val = command ? `undefined,${this.out(command, {sys: true})}` : '';

				if (inside === 'waterfall') {
					this.append(`return arguments[arguments.length - 1](${val});`);

				} else {
					this.append(ws`
						if (typeof arguments[0] === 'function') {
							return arguments[0](${val});
						}

						return;
					`);
				}

			} else {
				this.append(this.out('continue __I_PROTO__;', {sys: true}));
			}
		}
	}
);
<|endoftext|>