<commit_before>/**
 * @license
 * Quolace v0.1 - By Alasdair North
 * http://quolace.appspot.com
 *
 * Copyright © 2012, Alasdair North
 * 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 Alasdair North nor the
 *       names of other 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 ALASDAIR NORTH OR OTHER 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.
 *
 */

/*jslint devel: true, browser: true, sloppy: true, white: true, maxerr: 50, indent: 4, vars: true, sub: true */
/*global jQuery */

/**
 * Constructor for the Quolace object.
 * @constructor
 */
window["Quolace"] = function Quolace(appId, options) {
    var $ = jQuery;

    /**
     * URL base for contacting the API. Should end in a forward slash.
     * @const
     * @type {string}
     */
    var urlRoot = "https://quolace.appspot.com/";

    /**
     * Maximum length (in characters) of a key.
     * @const
     * @type {number}
     */
    var maxKeyLength = 500;

    /**
     * Maximum length (in characters) of a value.
     * @const
     * @type {number}
     */
    var maxValueLength = 10000;

    /**
     * @const
     * @type {string}
     */
    var tokenStorageKey = "quolace_token_" + appId;

    /**
     * @const
     * @type {string}
     */
    var initialUrlStorageKey = "quolace_initial_url_" + appId;

    /**
     * @const
     * @type {string}
     */
    var userDeclinedStorageKey = "quolace_declined_" + appId;

    /**
     * @type {string}
     */
    var token = localStorage.getItem(tokenStorageKey);

    options = options || {};

    /**
     * @type {boolean}
     */
    var useLocalStorage = options["alwaysUseLocalStorage"] || localStorage.getItem(userDeclinedStorageKey) === "true";

    function redirectToLogin() {
        localStorage.setItem(initialUrlStorageKey, document.location.href);
        document.location = urlRoot + "app/link?" + $.param({"id": appId, "return_url": document.location.href});
    }

    // From http://stackoverflow.com/a/5158301/152347
    function getParameterByName(name) {
        var match = new RegExp("[?&]" + name + "=([^&]*)").exec(window.location.search);
        return match && decodeURIComponent(match[1].replace(/\+/g, " "));
    }

    function getErrorHandler(fn) {
        /*jslint unparam: true*/
        return function(jqXHR, textStatus, errorThrown) {
            console.error("Error in API call - ", textStatus, errorThrown);
            if(fn) { fn(false); }
        };
        /*jslint unparam: false*/
    }

    function buildUrl(appId, key, token) {
        var url = urlRoot + "api/1/apps/" + encodeURIComponent(appId) + "/values";
        if(key) {
            url += "/" + encodeURIComponent(key);
        }
        return url + "?" + $.param({"token": token});
    }

    function resetUrl() {
        history.replaceState({}, "", localStorage.getItem(initialUrlStorageKey));
        localStorage.removeItem(initialUrlStorageKey);
    }

    function init(fn) {
        if(useLocalStorage) {
            // No need to go to the API if we know we'll be using localStorage.
            if(fn) { fn(false); }
        } else if(getParameterByName("success") === "false") {
            console.log("User declined link, falling back to localStorage");
            useLocalStorage = true;
            localStorage.setItem(userDeclinedStorageKey, "true");
            resetUrl();
            if(fn) { fn(false); }
        } else {
            if(getParameterByName("success") === "true") {
                var auth_grant = getParameterByName("auth_grant"),
                    upgradeUrl = urlRoot + "api/1/upgrade?callback=?&" + $.param({"id": appId, "auth_grant": auth_grant});

                $.ajax({
                    url: upgradeUrl,
                    dataType: "json",
                    success: function(data) {
                        if(data.success) {
                            token = data["token"];
                            localStorage.setItem(tokenStorageKey, token);
                            resetUrl();
                            fn(true);
                        } else {
                            if(data["message"]) {
                                console.error("Error in API request - " + data["message"]);
                            }
                            if(fn) { fn(false); }
                        }
                    },
                    error: getErrorHandler(fn)
                });

            } else if(token) {
                localStorage.removeItem(initialUrlStorageKey);
                if(fn) { fn(true); }
            } else {
                redirectToLogin();
            }
        }
    }
    this["init"] = init;

    function set(key, value, fn) {
        if(typeof(key) !== "string") {
            if(fn) { fn(false); }
            console.error("Key is not a string - ", key);
        } else if(typeof(value) !== "string" && value !== null) {
            if(fn) { fn(false); }
            console.error("Value is not a string - ", value);
        } else {
            if(key.length > maxKeyLength) {
                if(fn) { fn(false); }
                console.error("Key is " + key.length + " characters long, the maximum key length is " + maxKeyLength + " - ", key);
            } else if (value !== null && value.length > maxValueLength) {
                if(fn) { fn(false); }
                console.error("Value is " + value.length + " characters long, the maximum value length is " + maxValueLength + " - ", value);
            } else {
                if(useLocalStorage) {
                    if(value && value !== "") {
                        localStorage.setItem(key, value);
                    } else {
                        localStorage.removeItem(key, value);
                    }
                    if(fn) { fn(true); }
                } else {
                    var url = buildUrl(appId, key, token);
                    $.ajax({
                        type: "POST",
                        url: url,
                        data: {"value": value || ""},
                        success: function(data) {
                            if(!data["success"] && data["message"]) {
                                console.error("Error in API request - " + data["message"]);
                            }
                            if(fn) { fn(data["success"]); }
                        },
                        dataType: "json",
                        error: getErrorHandler(fn)
                    });
                }
            }
        }
    }
    this["set"] = set;

    function setObject(key, value, fn) {
        this.set(key, value === null ? "" : JSON.stringify(value), fn);
    }
    this["setObject"] = setObject;

    function get(keyOrKeys, fn) {
        var url = "";
        if(typeof(keyOrKeys) === "string") {
            // This is a single key.
            if(useLocalStorage) {
                if(fn) {
                    fn(true, localStorage.getItem(keyOrKeys));
                }
            } else {
                url = buildUrl(appId, keyOrKeys, token);
            }
        } else {
            // This could be an array of strings (a valid input) or something else entirely. Let's check.
            var isArray = true,
                i = 0;
            if(Object.prototype.toString.call(keyOrKeys) === "[object Array]") {
                for(i = 0; i < keyOrKeys.length; i += 1) {
                    if(typeof(keyOrKeys[i]) !== "string") {
                        isArray = false;
                        break;
                    }
                }
            } else {
                isArray = false;
            }

            if(isArray) {
                if(useLocalStorage) {
                    var returnVal = {};
                    for(i = 0; i < keyOrKeys.length; i += 1) {
                        returnVal[keyOrKeys[i]] = localStorage.getItem(keyOrKeys[i]);
                    }
                    if(fn) {
                        fn(true, returnVal);
                    }
                } else {
                    url = buildUrl(appId, null, token) + "&" + $.param({"keys": JSON.stringify(keyOrKeys)});
                }
            } else {
                console.error("Key is not a string or array of strings - ", keyOrKeys);
                if(fn) { fn(false); }
            }
        }

        if(url !== "") {
            $.ajax({
                url: url,
                dataType: "json",
                success: function(data) {
                    if(!data["success"] && data["message"]) {
                        console.error("Error in API request - " + data["message"]);
                    }
                    if(fn) { fn(data["success"], data["data"]); }
                },
                error: getErrorHandler(fn)
            });
        }
    }
    this["get"] = get;

    function getObject(keyOrKeys, fn) {
        this.get(keyOrKeys, function(success, data) {
            var key;
            if(fn) {
                if(success && data) {
                    if(typeof(data) === "string") {
                        fn(success, JSON.parse(data));
                    } else {
                        for(key in data) {
                            if(data.hasOwnProperty(key) && data[key] !== null) {
                                data[key] = JSON.parse(data[key]);
                            }
                        }
                        fn(success, data);
                    }
                } else {
                    fn(success, data);
                }
            }
        });
    }
    this["getObject"] = getObject;

    function isUsingLocalStorage() {
        return useLocalStorage;
    }
    this["isUsingLocalStorage"] = isUsingLocalStorage;

    function login() {
        localStorage.removeItem(userDeclinedStorageKey);
        redirectToLogin();
    }
    this["login"] = login;
};
<commit_msg>Add JSDoc comments for all functions.<commit_after>/**
 * @license
 * Quolace v0.1 - By Alasdair North
 * http://quolace.appspot.com
 *
 * Copyright © 2012, Alasdair North
 * 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 Alasdair North nor the
 *       names of other 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 ALASDAIR NORTH OR OTHER 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.
 *
 */

/*jslint devel: true, browser: true, sloppy: true, white: true, maxerr: 50, indent: 4, vars: true, sub: true */
/*global jQuery */

/**
 * Constructor for the Quolace object.
 * @param {string} appId The Quolace ID of the app.
 * @param {?Object.<string, (boolean|string|number)>} options Object specifying optional behaviour for the library.
 * @constructor
 */
window["Quolace"] = function Quolace(appId, options) {
    var $ = jQuery;

    /**
     * URL base for contacting the API. Should end in a forward slash.
     * @const
     * @type {string}
     */
    var urlRoot = "https://quolace.appspot.com/";

    /**
     * Maximum length (in characters) of a key.
     * @const
     * @type {number}
     */
    var maxKeyLength = 500;

    /**
     * Maximum length (in characters) of a value.
     * @const
     * @type {number}
     */
    var maxValueLength = 10000;

    /**
     * @const
     * @type {string}
     */
    var tokenStorageKey = "quolace_token_" + appId;

    /**
     * @const
     * @type {string}
     */
    var initialUrlStorageKey = "quolace_initial_url_" + appId;

    /**
     * @const
     * @type {string}
     */
    var userDeclinedStorageKey = "quolace_declined_" + appId;

    /**
     * @type {string}
     */
    var token = localStorage.getItem(tokenStorageKey);

    options = options || {};

    /**
     * @type {boolean}
     */
    var useLocalStorage = options["alwaysUseLocalStorage"] || localStorage.getItem(userDeclinedStorageKey) === "true";

    /**
     * Sets document.location to the page asking the user's permission to store data.
     * If they have already given permission then they will be redirected right back
     * again.
     */
    function redirectToLogin() {
        localStorage.setItem(initialUrlStorageKey, document.location.href);
        document.location = urlRoot + "app/link?" + $.param({"id": appId, "return_url": document.location.href});
    }

    /**
     * Get the value of a parameter from the query string.
     * From http://stackoverflow.com/a/5158301/152347
     * @param {string} name The key to the query string value.
     * @return {string}
     */
    function getParameterByName(name) {
        var match = new RegExp("[?&]" + name + "=([^&]*)").exec(window.location.search);
        return match && decodeURIComponent(match[1].replace(/\+/g, " "));
    }

    /**
     * Get a function to handle errors from ajax requests.
     * @param {function(boolean)} fn The callback function for the get/set call.
     * @return {function(Object, ?string, ?string)}
     */
    function getErrorHandler(fn) {
        /*jslint unparam: true*/
        return function(jqXHR, textStatus, errorThrown) {
            console.error("Error in API call - ", textStatus, errorThrown);
            if(fn) { fn(false); }
        };
        /*jslint unparam: false*/
    }

    /**
     * Build a URL for getting/setting values via the API.
     * @param {string} appId
     * @param {string} key
     * @param {string} token
     * @return {string}
     */
    function buildUrl(appId, key, token) {
        var url = urlRoot + "api/1/apps/" + encodeURIComponent(appId) + "/values";
        if(key) {
            url += "/" + encodeURIComponent(key);
        }
        return url + "?" + $.param({"token": token});
    }

    /**
     * Set the URL in the location bar to what it was when the page was first loaded.
     */
    function resetUrl() {
        history.replaceState({}, "", localStorage.getItem(initialUrlStorageKey));
        localStorage.removeItem(initialUrlStorageKey);
    }

    /**
     * Initialise the Quolace values in localStorage etc. Including redirecting to the Quolace site
     * if necessary. Calls the callback when done. Passing true to the callback indicates a successful
     * set up, passing false indicates failure of some kind.
     * @param {function(boolean)} fn The callback function from the user.
     */
    function init(fn) {
        if(useLocalStorage) {
            // No need to go to the API if we know we'll be using localStorage.
            if(fn) { fn(false); }
        } else if(getParameterByName("success") === "false") {
            console.log("User declined link, falling back to localStorage");
            useLocalStorage = true;
            localStorage.setItem(userDeclinedStorageKey, "true");
            resetUrl();
            if(fn) { fn(false); }
        } else {
            if(getParameterByName("success") === "true") {
                var auth_grant = getParameterByName("auth_grant"),
                    upgradeUrl = urlRoot + "api/1/upgrade?callback=?&" + $.param({"id": appId, "auth_grant": auth_grant});

                $.ajax({
                    url: upgradeUrl,
                    dataType: "json",
                    success: function(data) {
                        if(data.success) {
                            token = data["token"];
                            localStorage.setItem(tokenStorageKey, token);
                            resetUrl();
                            fn(true);
                        } else {
                            if(data["message"]) {
                                console.error("Error in API request - " + data["message"]);
                            }
                            if(fn) { fn(false); }
                        }
                    },
                    error: getErrorHandler(fn)
                });

            } else if(token) {
                localStorage.removeItem(initialUrlStorageKey);
                if(fn) { fn(true); }
            } else {
                redirectToLogin();
            }
        }
    }
    this["init"] = init;

    /**
     * Set a string value via the API.
     * @param {string} key
     * @param {string} value
     * @param {function(boolean)} fn
     */
    function set(key, value, fn) {
        if(typeof(key) !== "string") {
            if(fn) { fn(false); }
            console.error("Key is not a string - ", key);
        } else if(typeof(value) !== "string" && value !== null) {
            if(fn) { fn(false); }
            console.error("Value is not a string - ", value);
        } else {
            if(key.length > maxKeyLength) {
                if(fn) { fn(false); }
                console.error("Key is " + key.length + " characters long, the maximum key length is " + maxKeyLength + " - ", key);
            } else if (value !== null && value.length > maxValueLength) {
                if(fn) { fn(false); }
                console.error("Value is " + value.length + " characters long, the maximum value length is " + maxValueLength + " - ", value);
            } else {
                if(useLocalStorage) {
                    if(value && value !== "") {
                        localStorage.setItem(key, value);
                    } else {
                        localStorage.removeItem(key, value);
                    }
                    if(fn) { fn(true); }
                } else {
                    var url = buildUrl(appId, key, token);
                    $.ajax({
                        type: "POST",
                        url: url,
                        data: {"value": value || ""},
                        success: function(data) {
                            if(!data["success"] && data["message"]) {
                                console.error("Error in API request - " + data["message"]);
                            }
                            if(fn) { fn(data["success"]); }
                        },
                        dataType: "json",
                        error: getErrorHandler(fn)
                    });
                }
            }
        }
    }
    this["set"] = set;

    /**
     * Set an object value via the API.
     * @param {string} key
     * @param {Object} value
     * @param {function(boolean)} fn
     */
    function setObject(key, value, fn) {
        this.set(key, value === null ? "" : JSON.stringify(value), fn);
    }
    this["setObject"] = setObject;

    /**
     * Get a string value via the API.
     * @param {(string|Array.<string>)} keyOrKeys
     * @param {function(boolean, (string|Object.<string, string>))} fn
     */
    function get(keyOrKeys, fn) {
        var url = "";
        if(typeof(keyOrKeys) === "string") {
            // This is a single key.
            if(useLocalStorage) {
                if(fn) {
                    fn(true, localStorage.getItem(keyOrKeys));
                }
            } else {
                url = buildUrl(appId, keyOrKeys, token);
            }
        } else {
            // This could be an array of strings (a valid input) or something else entirely. Let's check.
            var isArray = true,
                i = 0;
            if(Object.prototype.toString.call(keyOrKeys) === "[object Array]") {
                for(i = 0; i < keyOrKeys.length; i += 1) {
                    if(typeof(keyOrKeys[i]) !== "string") {
                        isArray = false;
                        break;
                    }
                }
            } else {
                isArray = false;
            }

            if(isArray) {
                if(useLocalStorage) {
                    var returnVal = {};
                    for(i = 0; i < keyOrKeys.length; i += 1) {
                        returnVal[keyOrKeys[i]] = localStorage.getItem(keyOrKeys[i]);
                    }
                    if(fn) {
                        fn(true, returnVal);
                    }
                } else {
                    url = buildUrl(appId, null, token) + "&" + $.param({"keys": JSON.stringify(keyOrKeys)});
                }
            } else {
                console.error("Key is not a string or array of strings - ", keyOrKeys);
                if(fn) { fn(false); }
            }
        }

        if(url !== "") {
            $.ajax({
                url: url,
                dataType: "json",
                success: function(data) {
                    if(!data["success"] && data["message"]) {
                        console.error("Error in API request - " + data["message"]);
                    }
                    if(fn) { fn(data["success"], data["data"]); }
                },
                error: getErrorHandler(fn)
            });
        }
    }
    this["get"] = get;

    /**
     * Get an object value via the API.
     * @param {(string|Array.<string>)} keyOrKeys
     * @param {function(boolean, (Object|Object.<string, string>))} fn
     */
    function getObject(keyOrKeys, fn) {
        this.get(keyOrKeys, function(success, data) {
            var key;
            if(fn) {
                if(success && data) {
                    if(typeof(data) === "string") {
                        fn(success, JSON.parse(data));
                    } else {
                        for(key in data) {
                            if(data.hasOwnProperty(key) && data[key] !== null) {
                                data[key] = JSON.parse(data[key]);
                            }
                        }
                        fn(success, data);
                    }
                } else {
                    fn(success, data);
                }
            }
        });
    }
    this["getObject"] = getObject;

    /**
     * @return {boolean} True if values are being stored in localStorage, false if values are being stored in Quolace.
     */
    function isUsingLocalStorage() {
        return useLocalStorage;
    }
    this["isUsingLocalStorage"] = isUsingLocalStorage;

    /**
     * Public method to redirect to the login page if needed.
     */
    function login() {
        localStorage.removeItem(userDeclinedStorageKey);
        redirectToLogin();
    }
    this["login"] = login;
};
<|endoftext|>


<commit_before>define(function(require) {

	'use strict';

	//var _       = require('underscore');
	var PIXI    = require('pixi');
	var Vector2 = require('vector2-node');

	var ElementView = require('views/element');
	var Block       = require('models/element/block');
	//var Assets      = require('assets');

	var Constants = require('constants');

	/**
	 * A view that represents a block model
	 */
	var BlockView = ElementView.extend({

		/**
		 *
		 */
		initialize: function(options) {
			ElementView.prototype.initialize.apply(this, arguments);

			this.listenTo(this.model, 'change:position', this.updatePosition);

			this.initGraphics();

			this.updatePosition(this.model, this.model.get('position'));
		},

		initGraphics: function() {

			this.outlineBack  = new PIXI.DisplayObjectContainer();
			this.faces        = new PIXI.DisplayObjectContainer();
			this.outlineFront = new PIXI.DisplayObjectContainer();

			this.displayObject.addChild(this.outlineBack);
			this.displayObject.addChild(this.faces);
			this.displayObject.addChild(this.outlineFront);

			var rect = this.mvt.modelToViewScale(Block.getRawShape());
			var perspectiveEdgeSize = this.mvt.modelToViewDeltaX(this.model.getRect().w * Constants.PERSPECTIVE_EDGE_PROPORTION);

			var blockFaceOffset  = (new Vector2(-perspectiveEdgeSize / 2, 0)).rotate(-Constants.PERSPECTIVE_ANGLE);
			var backCornerOffset = (new Vector2(perspectiveEdgeSize,      0)).rotate(-Constants.PERSPECTIVE_ANGLE);

			// Front face
			var lowerLeftFrontCorner  = (new Vector2(rect.left(),   rect.bottom())).add(blockFaceOffset);
			var lowerRightFrontCorner = (new Vector2(rect.right(),  rect.bottom())).add(blockFaceOffset);
			var upperRightFrontCorner = (new Vector2(rect.right(), -rect.top()   )).add(blockFaceOffset);
			var upperLeftFrontCorner  = (new Vector2(rect.left(),  -rect.top()   )).add(blockFaceOffset);

			var frontFacePoints = [
				lowerLeftFrontCorner,
				lowerRightFrontCorner,
				upperRightFrontCorner,
				upperLeftFrontCorner,
				lowerLeftFrontCorner
			];
			this.faces.addChild(this.createFrontFace(frontFacePoints));

			// Top face
			var upperLeftBackCorner  = upperLeftFrontCorner.clone().add(backCornerOffset);
			var upperRightBackCorner = upperRightFrontCorner.clone().add(backCornerOffset);

			var topFacePoints = [
				upperLeftFrontCorner,
				upperRightFrontCorner,
				upperRightBackCorner,
				upperLeftBackCorner,
				upperLeftFrontCorner
			];
			this.faces.addChild(this.createTopFace(topFacePoints));

			// Side face
			var lowerRightBackCorner = lowerRightFrontCorner.clone().add(backCornerOffset);

			var rightFacePoints = [
				upperRightFrontCorner,
				lowerRightFrontCorner,
				lowerRightBackCorner,
				upperRightBackCorner,
				upperRightFrontCorner
			];
			this.faces.addChild(this.createRightFace(rightFacePoints));

			// Front outline
			var lineStyle = {
				lineWidth: 3,
				strokeStyle: '#444',
				lineJoin: 'round'
			};
			var pointArrays = [frontFacePoints, topFacePoints, rightFacePoints];
			this.outlineFront.addChild(this.createOutlineFromPointArrays(pointArrays, lineStyle));

			// Back outline
			var lowerLeftBackCorner = lowerLeftFrontCorner.clone().add(backCornerOffset);

			pointArrays = [[
				lowerLeftBackCorner,
				lowerRightBackCorner
			],[
				lowerLeftBackCorner,
				lowerLeftFrontCorner
			],[
				lowerLeftBackCorner,
				upperLeftBackCorner
			]];
			this.outlineBack.addChild(this.createOutlineFromPointArrays(pointArrays, lineStyle));

			this.outlineBack.visible = false;

			// Label
			var label = new PIXI.Text(this.labelText, {
				font: this.textFont,
				fill: this.textColor
			});
			label.anchor.x = label.anchor.y = 0.5;
			label.x = blockFaceOffset.x;
			label.y = -(rect.h / 2) + blockFaceOffset.y;
			this.displayObject.addChild(label);

			// Just for debugging
			this.renderTopCenterPoint();
			//this.renderBottomCenterPoint();
		},

		createFrontFace: function(points) {
			return this.createColoredPolygonFromPoints(points, this.fillColor, this.fillAlpha);
		},

		createTopFace: function(points) {
			return this.createColoredPolygonFromPoints(points, this.fillColor, this.fillAlpha);
		},

		createRightFace: function(points) {
			return this.createColoredPolygonFromPoints(points, this.fillColor, this.fillAlpha);
		},

		/**
		 * For debugging purposes
		 */
		renderTopCenterPoint: function() {
			var top = new PIXI.Graphics();
			top.beginFill(0x0000FF, 1);
			top.drawCircle(0, this.mvt.modelToViewDeltaY(this.model.topSurface.yPos - this.model.get('position').y), 3);
			top.endFill();
			this.displayObject.addChild(top);
		},

		/**
		 * For debugging purposes
		 */
		renderBottomCenterPoint: function() {
			var origin = new PIXI.Graphics();
			origin.beginFill(0xFF0000, 1);
			origin.drawCircle(0, 0, 3);
			origin.endFill();
			this.displayObject.addChild(origin);
		},

		updatePosition: function(model, position) {
			var viewPoint = this.mvt.modelToView(position);
			this.displayObject.x = viewPoint.x;
			this.displayObject.y = viewPoint.y;
		},

		showEnergyChunks: function() {
			this.outlineBack.visible = true;
			this.faces.alpha = 0.5;
		},

		hideEnergyChunks: function() {
			this.outlineBack.visible = false;
			this.faces.alpha = 1;
		},

	});

	return BlockView;
});<commit_msg>Label opacity changes<commit_after>define(function(require) {

	'use strict';

	//var _       = require('underscore');
	var PIXI    = require('pixi');
	var Vector2 = require('vector2-node');

	var ElementView = require('views/element');
	var Block       = require('models/element/block');
	//var Assets      = require('assets');

	var Constants = require('constants');

	/**
	 * A view that represents a block model
	 */
	var BlockView = ElementView.extend({

		/**
		 *
		 */
		initialize: function(options) {
			ElementView.prototype.initialize.apply(this, arguments);

			this.listenTo(this.model, 'change:position', this.updatePosition);

			this.initGraphics();

			this.updatePosition(this.model, this.model.get('position'));
		},

		initGraphics: function() {

			this.outlineBack  = new PIXI.DisplayObjectContainer();
			this.faces        = new PIXI.DisplayObjectContainer();
			this.outlineFront = new PIXI.DisplayObjectContainer();

			this.displayObject.addChild(this.outlineBack);
			this.displayObject.addChild(this.faces);
			this.displayObject.addChild(this.outlineFront);

			var rect = this.mvt.modelToViewScale(Block.getRawShape());
			var perspectiveEdgeSize = this.mvt.modelToViewDeltaX(this.model.getRect().w * Constants.PERSPECTIVE_EDGE_PROPORTION);

			var blockFaceOffset  = (new Vector2(-perspectiveEdgeSize / 2, 0)).rotate(-Constants.PERSPECTIVE_ANGLE);
			var backCornerOffset = (new Vector2(perspectiveEdgeSize,      0)).rotate(-Constants.PERSPECTIVE_ANGLE);

			// Front face
			var lowerLeftFrontCorner  = (new Vector2(rect.left(),   rect.bottom())).add(blockFaceOffset);
			var lowerRightFrontCorner = (new Vector2(rect.right(),  rect.bottom())).add(blockFaceOffset);
			var upperRightFrontCorner = (new Vector2(rect.right(), -rect.top()   )).add(blockFaceOffset);
			var upperLeftFrontCorner  = (new Vector2(rect.left(),  -rect.top()   )).add(blockFaceOffset);

			var frontFacePoints = [
				lowerLeftFrontCorner,
				lowerRightFrontCorner,
				upperRightFrontCorner,
				upperLeftFrontCorner,
				lowerLeftFrontCorner
			];
			this.faces.addChild(this.createFrontFace(frontFacePoints));

			// Top face
			var upperLeftBackCorner  = upperLeftFrontCorner.clone().add(backCornerOffset);
			var upperRightBackCorner = upperRightFrontCorner.clone().add(backCornerOffset);

			var topFacePoints = [
				upperLeftFrontCorner,
				upperRightFrontCorner,
				upperRightBackCorner,
				upperLeftBackCorner,
				upperLeftFrontCorner
			];
			this.faces.addChild(this.createTopFace(topFacePoints));

			// Side face
			var lowerRightBackCorner = lowerRightFrontCorner.clone().add(backCornerOffset);

			var rightFacePoints = [
				upperRightFrontCorner,
				lowerRightFrontCorner,
				lowerRightBackCorner,
				upperRightBackCorner,
				upperRightFrontCorner
			];
			this.faces.addChild(this.createRightFace(rightFacePoints));

			// Front outline
			var lineStyle = {
				lineWidth: 3,
				strokeStyle: '#444',
				lineJoin: 'round'
			};
			var pointArrays = [frontFacePoints, topFacePoints, rightFacePoints];
			this.outlineFront.addChild(this.createOutlineFromPointArrays(pointArrays, lineStyle));

			// Back outline
			var lowerLeftBackCorner = lowerLeftFrontCorner.clone().add(backCornerOffset);

			pointArrays = [[
				lowerLeftBackCorner,
				lowerRightBackCorner
			],[
				lowerLeftBackCorner,
				lowerLeftFrontCorner
			],[
				lowerLeftBackCorner,
				upperLeftBackCorner
			]];
			this.outlineBack.addChild(this.createOutlineFromPointArrays(pointArrays, lineStyle));

			this.outlineBack.visible = false;

			// Label
			this.label = new PIXI.Text(this.labelText, {
				font: this.textFont,
				fill: this.textColor
			});
			this.label.anchor.x = this.label.anchor.y = 0.5;
			this.label.x = blockFaceOffset.x;
			this.label.y = -(rect.h / 2) + blockFaceOffset.y;
			this.displayObject.addChild(this.label);

			// Just for debugging
			this.renderTopCenterPoint();
			//this.renderBottomCenterPoint();
		},

		createFrontFace: function(points) {
			return this.createColoredPolygonFromPoints(points, this.fillColor, this.fillAlpha);
		},

		createTopFace: function(points) {
			return this.createColoredPolygonFromPoints(points, this.fillColor, this.fillAlpha);
		},

		createRightFace: function(points) {
			return this.createColoredPolygonFromPoints(points, this.fillColor, this.fillAlpha);
		},

		/**
		 * For debugging purposes
		 */
		renderTopCenterPoint: function() {
			var top = new PIXI.Graphics();
			top.beginFill(0x0000FF, 1);
			top.drawCircle(0, this.mvt.modelToViewDeltaY(this.model.topSurface.yPos - this.model.get('position').y), 3);
			top.endFill();
			this.displayObject.addChild(top);
		},

		/**
		 * For debugging purposes
		 */
		renderBottomCenterPoint: function() {
			var origin = new PIXI.Graphics();
			origin.beginFill(0xFF0000, 1);
			origin.drawCircle(0, 0, 3);
			origin.endFill();
			this.displayObject.addChild(origin);
		},

		updatePosition: function(model, position) {
			var viewPoint = this.mvt.modelToView(position);
			this.displayObject.x = viewPoint.x;
			this.displayObject.y = viewPoint.y;
		},

		showEnergyChunks: function() {
			this.outlineBack.visible = true;
			this.faces.alpha = 0.5;
			this.label.alpha = 0.5;
		},

		hideEnergyChunks: function() {
			this.outlineBack.visible = false;
			this.faces.alpha = 1;
			this.label.alpha = 1;
		},

	});

	return BlockView;
});<|endoftext|>


<commit_before>module.exports = ['$q', function ($q) {
    return {
        restrict: 'E',
        scope: true,
        template: `

            <xp-gridfilter-button xp-grid-options="options" layout="row" layout-align="end center"></xp-gridfilter-button>

`,
        link: function (scope, element, attrs) {

            var sort;
            var mustReload;
            var page = 0;
            var refresh = new Rx.Subject();

            scope.options = scope.$parent.$eval(attrs.xpGridOptions || '{}') || {};

            var options = angular.extend(scope.options, {
                data: [],
                infiniteScrollRowsFromEnd: 100,
                infiniteScrollDown: true,
                onRegisterApi: function (gridApi) {
                    scope.options.gridApi = gridApi;
                    gridApi.core.on.sortChanged(scope, sortChanged);
                    sortChanged(gridApi.grid, [options.columnDefs[1]]);
                    gridApi.infiniteScroll.on.needLoadMoreData(scope, needLoadMoreData);
                }
            });

            scope.$watch('options.columnDefs', columns => scope.filters = computeFilters(columns), true);
            scope.$watch('filters', _ => refresh.onNext(true), true);

            function needLoadMoreData() {
                refresh.onNext(false);
            }

            function sortChanged(grid, sortColumns) {
                sort = computeSortString(sortColumns);
                refresh.onNext(true);
            }

            refresh
                .map(e => mustReload |= e)
                .debounce(1)
                .$apply(scope)
                .flatMapLatest(function (reload) {
                    mustReload = false;

                    if (reload) {
                        page = 1;
                        scope.data = [];
                    }

                    var params = angular.extend({ page, sort, pageSize: 100 }, scope.filters);
                    var result = options.fetch(params);

                    if (!result.subscribe)
                        result = Rx.Observable.fromPromise($q.when(result));

                    return result.catch(_ => Rx.Observable.empty());
                })
                .$apply(scope)
                .tap(function (data) {
                    page++;
                    options.data = options.data.concat(data);
                    scope.options.gridApi.infiniteScroll.dataLoaded(false, data.length >= 100);
                })
                .subscribe();
        }
    }
}];

function computeFilters(columns) {
    var o = {};

    if (columns)
        for (var i = 0; i < columns.length; i++) {
            var column = columns[i];
            var filters = column.filters || [];

            if (filters.length)
                o[column.name] = filters;
        }

    return o;
}

function computeSortString(sortColumns) {
    var s = '';

    for (var i = 0; i < sortColumns.length; i++) {
        if (s) s += ',';

        var col = sortColumns[i];
        if (col.sort)
            s += col.name + ':' + col.sort.direction;
    }

    return s;
}<commit_msg>Fix xp-grid duplicate rows on sort<commit_after>module.exports = ['$q', function ($q) {
    return {
        restrict: 'E',
        scope: true,
        template: `

            <xp-gridfilter-button xp-grid-options="options" layout="row" layout-align="end center"></xp-gridfilter-button>

`,
        link: function (scope, element, attrs) {

            var sort;
            var mustReload;
            var page = 0;
            var refresh = new Rx.Subject();

            scope.options = scope.$parent.$eval(attrs.xpGridOptions || '{}') || {};

            var options = angular.extend(scope.options, {
                data: [],
                infiniteScrollRowsFromEnd: 100,
                infiniteScrollDown: true,
                onRegisterApi: function (gridApi) {
                    scope.options.gridApi = gridApi;
                    gridApi.core.on.sortChanged(scope, sortChanged);
                    sortChanged(gridApi.grid, [options.columnDefs[1]]);
                    gridApi.infiniteScroll.on.needLoadMoreData(scope, needLoadMoreData);
                }
            });

            scope.$watch('options.columnDefs', columns => scope.filters = computeFilters(columns), true);
            scope.$watch('filters', _ => refresh.onNext(true), true);

            function needLoadMoreData() {
                refresh.onNext(false);
            }

            function sortChanged(grid, sortColumns) {
                sort = computeSortString(sortColumns);
                refresh.onNext(true);
            }

            refresh
                .map(e => mustReload |= e)
                .debounce(1)
                .$apply(scope)
                .flatMapLatest(function (reload) {
                    mustReload = false;

                    if (reload) {
                        page = 1;
                        options.data = [];
                    }

                    var params = angular.extend({ page, sort, pageSize: 100 }, scope.filters);
                    var result = options.fetch(params);

                    if (!result.subscribe)
                        result = Rx.Observable.fromPromise($q.when(result));

                    return result.catch(_ => Rx.Observable.empty());
                })
                .$apply(scope)
                .tap(function (data) {
                    page++;
                    options.data = options.data.concat(data);
                    scope.options.gridApi.infiniteScroll.dataLoaded(false, data.length >= 100);
                })
                .subscribe();
        }
    }
}];

function computeFilters(columns) {
    var o = {};

    if (columns)
        for (var i = 0; i < columns.length; i++) {
            var column = columns[i];
            var filters = column.filters || [];

            if (filters.length)
                o[column.name] = filters;
        }

    return o;
}

function computeSortString(sortColumns) {
    var s = '';

    for (var i = 0; i < sortColumns.length; i++) {
        if (s) s += ',';

        var col = sortColumns[i];
        if (col.sort)
            s += col.name + ':' + col.sort.direction;
    }

    return s;
}<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/**
 * Method using a factory pattern variant to get a reducer
 * for a particular page or page style.
 *
 * For a new page - create a constant object with the
 * methods from reducerMethods which are composed to create
 * a reducer. Add to PAGE_REDUCERS.
 *
 * Each key value pair in a reducer object should be in the form
 * action type: reducer function, returning a new state object
 *
 */

import { UIDatabase } from '../../../database/index';

import { refreshRow } from './cellReducers';
import {
  selectRow,
  deselectRow,
  deselectAll,
  selectAll,
  selectRows,
  deleteRecords,
} from './rowReducers';
import {
  hideStockOut,
  addRecord,
  hideOverStocked,
  refreshData,
  filterData,
  sortData,
} from './tableReducers';
import { editName, openModal, closeModal } from './pageReducers';

/**
 * Used for actions that should be in all pages using a data table.
 */
const BASE_TABLE_PAGE_REDUCER = {
  sortData,
};

const customerInvoice = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  selectRow,
  deselectRow,
  deselectAll,
  openModal,
  closeModal,
  addRecord,
  deleteRecords,
  refreshData,
  refreshRow,
};

const customerInvoices = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  selectRow,
  deselectRow,
  deselectAll,
  openModal,
  closeModal,
  deleteRecords,
  refreshData,
};

const supplierInvoice = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  selectRow,
  deselectRow,
  deselectAll,
  closeModal,
  openModal,
  refreshData,
  addRecord,
  deleteRecords,
  refreshRow,
};

const supplierRequisition = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  selectRow,
  deselectRow,
  deselectAll,
  openModal,
  closeModal,
  refreshData,
  addRecord,
  hideOverStocked,
  deleteRecords,
  refreshRow,
};

const programSupplierRequisition = {
  ...BASE_TABLE_PAGE_REDUCER,
  filterData,
  selectRow,
  deselectRow,
  deselectAll,
  openModal,
  closeModal,
  refreshData,
  addRecord,
  hideOverStocked,
  deleteRecords,
  refreshRow,
};

const supplierRequisitions = {
  sortData,
  filterData,
  selectRow,
  deselectRow,
  openModal,
  closeModal,
  refreshData,
  deleteRecords,
};

const stocktakes = {
  openModal,
  closeModal,
  filterData,
  selectRow,
  deselectAll,
  deselectRow,
  sortData,
  deleteRecords,
};

const stocktakeManager = {
  selectRow,
  deselectAll,
  deselectRow,
  sortData,
  filterData,
  selectAll,
  hideStockOut,
  selectRows,
  editName,
  refreshData,
};

const stocktakeEditor = {
  ...BASE_TABLE_PAGE_REDUCER,
  sortData,
  filterData,
  openModal,
  closeModal,
  refreshData,
  refreshRow,
};

const stocktakeEditorReasons = {
  ...stocktakeEditor,
};

const PAGE_REDUCERS = {
  customerInvoice,
  customerInvoices,
  supplierInvoice,
  supplierRequisitions,
  supplierRequisition,
  programSupplierRequisition,
  stocktakes,
  stocktakeManager,
  stocktakeEditor,
  stocktakeEditorReasons,
};

const getReducer = page => {
  let reducer;
  switch (page) {
    case 'stocktakeEditor':
      {
        const usesReasons = UIDatabase.objects('StocktakeReasons').length > 0;
        reducer = usesReasons ? PAGE_REDUCERS.stocktakeEditorReasons : PAGE_REDUCERS[page];
      }
      break;
    default:
      reducer = PAGE_REDUCERS[page];
  }

  return (state, action) => {
    const { type } = action;
    if (!reducer[type]) return state;
    return reducer[type](state, action);
  };
};

export default getReducer;
<commit_msg>Add removal of getReducer file<commit_after><|endoftext|>


<commit_before>const mongoose = require('mongoose');
const express = require('express');
const validate = require('./src/validate');
const makeRandomHex = require('./src/makeRandomHex');
const manipulateDatabase = require('./src/manipulateDatabase');

require('dotenv').config();

const mongoURI = `mongodb://${process.env.MONGO_USERNAME}:${process.env.MONGO_PASSWORD}@${process.env.MONGO_URI}`;
mongoose.connect(mongoURI);
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
  console.log('App connected to database successfully');
  const URLSchema = mongoose.Schema({
    originalURL: String,
    shortURL: String,
  });
  const URL = mongoose.model('URL', URLSchema);

  const app = express();

  app.get('/new/:string(*)', (req, res) => {
    const isValidURL = validate(req.params.string);
    if(isValidURL) {
      manipulateDatabase.fetchShortURLs(URL, (blacklist) => {
        const randomHEX = makeRandomHex(blacklist);
        manipulateDatabase.add(URL, req.params.string, randomHEX);
        res.send({
          original_url: req.params.string,
          short_url: `https://mkermani144fccp-usm.herokuapp.com/${randomHEX}`,
        });
      });
    } else {
      res.send({
        error: 'Not a valid URL.'
      });
    }
  });

  app.get('/:shortURL', (req, res) => {
    manipulateDatabase.findOriginalURL(URL, req.params.shortURL, (data) => {
      if (data.length) {
        res.redirect(data[0].originalURL);
      } else {
        res.send({ error: 'The URL does not exist on the database.'});
      }
    });
  });

  app.listen(process.env.PORT || 8000, () => {
    console.log(`App is running on http://localhost:${process.env.PORT || 8000}`)
  });
});
<commit_msg>Update error message text<commit_after>const mongoose = require('mongoose');
const express = require('express');
const validate = require('./src/validate');
const makeRandomHex = require('./src/makeRandomHex');
const manipulateDatabase = require('./src/manipulateDatabase');

require('dotenv').config();

const mongoURI = `mongodb://${process.env.MONGO_USERNAME}:${process.env.MONGO_PASSWORD}@${process.env.MONGO_URI}`;
mongoose.connect(mongoURI);
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
  console.log('App connected to database successfully');
  const URLSchema = mongoose.Schema({
    originalURL: String,
    shortURL: String,
  });
  const URL = mongoose.model('URL', URLSchema);

  const app = express();

  app.get('/new/:string(*)', (req, res) => {
    const isValidURL = validate(req.params.string);
    if(isValidURL) {
      manipulateDatabase.fetchShortURLs(URL, (blacklist) => {
        const randomHEX = makeRandomHex(blacklist);
        manipulateDatabase.add(URL, req.params.string, randomHEX);
        res.send({
          original_url: req.params.string,
          short_url: `https://mkermani144fccp-usm.herokuapp.com/${randomHEX}`,
        });
      });
    } else {
      res.send({
        error: 'The URL is not valid.'
      });
    }
  });

  app.get('/:shortURL', (req, res) => {
    manipulateDatabase.findOriginalURL(URL, req.params.shortURL, (data) => {
      if (data.length) {
        res.redirect(data[0].originalURL);
      } else {
        res.send({ error: 'The URL does not exist on the database.'});
      }
    });
  });

  app.listen(process.env.PORT || 8000, () => {
    console.log(`App is running on http://localhost:${process.env.PORT || 8000}`)
  });
});
<|endoftext|>


<commit_before>'use strict';

var React = require('react');
var merge = require('lodash.merge');
var Router = require('react-router');
var Link = React.createFactory(Router.Link);
var StyleMixin = require('../../lib/StyleMixin');
var css = require('./index.styl');
var Badge = React.createFactory(require('../Badge'));
var DOM = React.DOM;
var PropTypes = React.PropTypes;

module.exports = React.createClass({
  displayName: 'Navbar',
  mixins: [StyleMixin],
  css: css,

  propTypes: {
    links: PropTypes.arrayOf(PropTypes.object),
    title: PropTypes.string,
    logo: PropTypes.string
  },

  getInitialState: function() {
    return {
      toggleNav: false
    };
  },
  getDefaultProps: function(){
    return {
      links: []
    };
  },
  toggleNav: function() {
    this.setState({toggleNav: !this.state.toggleNav});
  },
  render: function(){
    var props = merge({
      className: 'navbar-component',
      style: this.props.style
    }, this.props);

    var logo = DOM.img({
      className: 'logo',
      src: this.props.logo
    });
    var title = DOM.div({
      className: 'title'
    }, this.props.logo ? logo : this.props.title);

    var button = DOM.div({
      className: 'navbar-button',
      onClick: this.toggleNav
    },
      DOM.div({className: 'bar'}),
      DOM.div({className: 'bar'}),
      DOM.div({className: 'bar'})
    );
    var links = this.props.links.map(function(link) {
      link.key = link.to + '-nav-link';
      link.onClick = this.toggleNav;
      var badge = link.badge != null && link.badge.count != null ? Badge(link.badge) : null;
      return Link(link, link.label, badge);
    }, this);

    var inner = DOM.div({
      className: 'inner',
      style: {
        maxHeight: this.state.toggleNav ? '600' : '0'
      }
    }, links);

    return DOM.nav(props, title, inner, button);
  }
});
<commit_msg>Add support for logo link<commit_after>'use strict';

var React = require('react');
var merge = require('lodash.merge');
var Router = require('react-router');
var Link = React.createFactory(Router.Link);
var StyleMixin = require('../../lib/StyleMixin');
var css = require('./index.styl');
var Badge = React.createFactory(require('../Badge'));
var DOM = React.DOM;
var PropTypes = React.PropTypes;

module.exports = React.createClass({
  displayName: 'Navbar',
  mixins: [StyleMixin],
  css: css,

  propTypes: {
    links: PropTypes.arrayOf(PropTypes.object),
    title: PropTypes.string,
    logoSrc: PropTypes.string,
    logoLink: PropTypes.string
  },

  getInitialState: function() {
    return {
      toggleNav: false
    };
  },
  getDefaultProps: function(){
    return {
      links: []
    };
  },
  toggleNav: function() {
    this.setState({toggleNav: !this.state.toggleNav});
  },
  render: function(){
    var props = merge({
      className: 'navbar-component',
      style: this.props.style
    }, this.props);

    var logo = DOM.a({
      href: this.props.logoLink,
    },
      DOM.img({
        className: 'logo',
        src: this.props.logoSrc
    }));

    var title = DOM.div({
      className: 'title'
    }, this.props.logoSrc ? logo : this.props.title);

    var button = DOM.div({
      className: 'navbar-button',
      onClick: this.toggleNav
    },
      DOM.div({className: 'bar'}),
      DOM.div({className: 'bar'}),
      DOM.div({className: 'bar'})
    );
    var links = this.props.links.map(function(link) {
      link.key = link.to + '-nav-link';
      link.onClick = this.toggleNav;
      var badge = link.badge != null && link.badge.count != null ? Badge(link.badge) : null;
      return Link(link, link.label, badge);
    }, this);

    var inner = DOM.div({
      className: 'inner',
      style: {
        maxHeight: this.state.toggleNav ? '600' : '0'
      }
    }, links);

    return DOM.nav(props, title, inner, button);
  }
});
<|endoftext|>


<commit_before>const expect = require('expect.js');
const fs = require('fs-extra');
const Index = require('../src/Index');

describe('Index', function() {

    let index;

    beforeEach(function() {
        fs.emptyDirSync('test/data');
    });

    afterEach(function() {
        if (index) index.close();
        index = undefined;
    });

    function setupIndexWithEntries(num, indexMapper, options) {
        if (typeof indexMapper === 'object') {
            options = indexMapper;
            indexMapper = undefined;
        }
        index = new Index('test.index', Object.assign({ dataDirectory: 'test/data' }, options));
        for (let i = 1; i <= num; i++) {
            index.add(new Index.Entry(indexMapper && indexMapper(i) || i, i));
        }
        return index;
    }

    it('is opened on instanciation', function() {
        index = setupIndexWithEntries();
        expect(index.isOpen()).to.be(true);
    });

    it('defaults name to ".index"', function() {
        index = new Index({ dataDirectory: 'test/data' });
        expect(index.name).to.be('.index');
    });

    it('recovers metadata on reopening', function() {
        index = new Index('test/data/.index', { metadata: { test: 'valueStays' } });
        expect(index.metadata.test).to.be('valueStays');
        index.close();
        index = new Index('test/data/.index');
        expect(index.metadata.test).to.be('valueStays');
    });

    it('throws on opening an non-index file', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        fs.writeSync(fd, 'foo');
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid file header/);
    });

    it('throws on opening an index file with different version', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        fs.writeSync(fd, 'nesidx00');
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid file version/);
    });

    it('throws on opening an index file with wrong metadata size', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        const metadataBuffer = Buffer.allocUnsafe(8 + 4);
        metadataBuffer.write("nesidx01", 0, 8, 'utf8');
        metadataBuffer.writeUInt32BE(0, 8, true);
        fs.writeSync(fd, metadataBuffer);
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid metadata size/);
    });

    it('throws on opening an index file with too large metadata size', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        const metadataBuffer = Buffer.allocUnsafe(8 + 4 + 3);
        metadataBuffer.write("nesidx01", 0, 8, 'utf8');
        metadataBuffer.writeUInt32BE(255, 8, true);
        metadataBuffer.write("{}\n", 12, 3, 'utf8');
        fs.writeSync(fd, metadataBuffer);
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid index file/);
    });

    it('throws on opening an index file with invalid metadata', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        const metadataBuffer = Buffer.allocUnsafe(8 + 4);
        metadataBuffer.write("nesidx01", 0, 8, 'utf8');
        metadataBuffer.writeUInt32BE(255, 8, true);
        fs.writeSync(fd, metadataBuffer);
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid metadata/);
    });

    it('throws on reopening with altered metadata', function() {
        index = new Index('test/data/.index', { metadata: { test: 'valueStays' } });
        expect(() => index = new Index('test/data/.index', { metadata: { test: 'anotherValue' } })).to.throwError(/Index metadata mismatch/);
    });

    it('throws on opening with altered file', function() {
        index = setupIndexWithEntries(5);
        index.close();
        fs.appendFileSync('test/data/test.index', 'foo');
        expect(() => index = new Index('test/data/test.index')).to.throwError(/Index file is corrupt/);
    });

    describe('Entry', function() {

        it('stores data correctly', function() {
            let entry = new Index.Entry(1, 2, 3, 4);
            expect(entry.number).to.be(1);
            expect(entry.position).to.be(2);
            expect(entry.size).to.be(3);
            expect(entry.partition).to.be(4);
        });

    });

    describe('add', function() {

        it('appends entries sequentially', function() {
            index = setupIndexWithEntries(25);
            index.close();
            index.open();
            let entries = index.all();
            expect(entries.length).to.be(25);
            for (let i = 1; i <= entries.length; i++) {
                expect(entries[i - 1].number).to.be(i);
            }
        });

        it('calls callback eventually', function(done) {
            index = new Index('test/data/.index', { flushDelay: 1 });
            let position = index.add(new Index.Entry(1, 0), (number) => {
                expect(number).to.be(position);
                done();
            });
        });

        it('throws with invalid entry object', function() {
            index = new Index('test/data/.index');
            expect(() => index.add([1,2,3,4])).to.throwError(/Wrong entry object/);
        });

        it('throws with invalid entry size', function() {
            index = new Index('test/data/.index');
            class Entry extends Index.Entry {
                static get size() {
                    return 20;
                }
            }
            expect(() => index.add(new Entry(1, 0))).to.throwError(/Invalid entry size/);
        });

    });

    describe('get', function() {

        it('returns false on out of bounds position', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();
            expect(index.get(0)).to.be(false);
            expect(index.get(index.length+1)).to.be(false);
        });

        it('can read entry from the end', function() {
            setupIndexWithEntries(5);
            index.close();
            index.open();
            let entry = index.get(-1);
            expect(entry.number).to.be(index.length);
        });

        it('returns false on closed index', function() {
            index = setupIndexWithEntries(1);
            index.close();
            expect(index.get(1)).to.be(false);
        });

        it('can random read entries', function() {
            index = setupIndexWithEntries(10);
            index.close();
            index.open();
            let entry = index.get(5);
            expect(entry.number).to.be(5);
        });

        it('can read entries multiple times', function() {
            index = setupIndexWithEntries(10);
            index.close();
            index.open();
            for (let i = 0; i < 5; i++) {
                let entry = index.get(5);
                expect(entry.number).to.be(5);
            }
        });

    });

    describe('range', function() {

        it('returns false on out of bounds range position', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            expect(index.range(0)).to.be(false);
            expect(index.range(51, 55)).to.be(false);
            expect(index.range(1, 51)).to.be(false);
            expect(index.range(15, 10)).to.be(false);
        });

        it('returns false on closed index', function() {
            index = setupIndexWithEntries(1);
            index.close();
            expect(index.range(1)).to.be(false);
        });

        it('can read an arbitrary range of entries', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            let entries = index.range(21, 37);
            for (let i = 0; i < entries.length; i++) {
                expect(entries[i].number).to.be(21 + i);
            }
        });

        it('can read a range of entries from the end', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            let entries = index.range(-15);
            expect(entries.length).to.be(15);
            for (let i = 0; i < entries.length; i++) {
                expect(entries[i].number).to.be(36 + i);
            }
        });

        it('can read a range of entries until a distance from the end', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            let entries = index.range(1, -15);
            expect(entries.length).to.be(36);   // 36 because end is inclusive
            for (let i = 0; i < entries.length; i++) {
                expect(entries[i].number).to.be(1 + i);
            }
        });

        it('can read a single item range of entries', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            let entries = index.range(21, 21);
            expect(entries.length).to.be(1);
            expect(entries[0].number).to.be(21);
        });

        it('returns false with a non-numeric range', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();
            let entries = index.range('foo');
            expect(entries).to.be(false);
        });

    });

    describe('lastEntry', function() {

        it('returns the last entry', function() {
            index = setupIndexWithEntries(5);
            expect(index.lastEntry.number).to.be(5);
        });

        it('returns false on empty index', function() {
            index = setupIndexWithEntries(0);
            expect(index.lastEntry).to.be(false);
        });

    });

    describe('find', function() {

        it('returns 0 if no entry is lower or equal searched number', function() {
            index = setupIndexWithEntries(5, i => 5 + i);
            expect(index.find(index.length)).to.be(0);
        });

        it('returns last entry if all entries are lower searched number', function() {
            index = setupIndexWithEntries(5);
            expect(index.find(index.length+1)).to.be(index.length);
        });

        it('returns the entry number on exact match', function() {
            index = setupIndexWithEntries(5);
            for (let i = 1; i <= 5; i++) {
                expect(index.find(i)).to.be(i);
            }
        });

        it('returns the highest entry number lower than the searched number', function() {
            index = setupIndexWithEntries(50, i => 2*i);
            expect(index.find(25)).to.be(12);
        });

        it('returns the lowest entry number higher than the searched number with min=true', function() {
            index = setupIndexWithEntries(50, i => 2*i);
            expect(index.find(25, true)).to.be(13);
        });

    });

    describe('truncate', function() {

        it('truncates after the given index position', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();

            index.truncate(2);
            expect(index.length).to.be(2);

            index.close();
            index.open();
            expect(index.length).to.be(2);
        });

        it('correctly truncates after unflushed entries', function() {
            index = setupIndexWithEntries(5);

            index.truncate(2);
            expect(index.length).to.be(2);

            index.close();
            index.open();
            expect(index.length).to.be(2);
        });

        it('does nothing if truncating after index length', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();

            index.truncate(6);
            expect(index.length).to.be(5);

            index.close();
            index.open();
            expect(index.length).to.be(5);
        });

        it('truncates whole index if given negative position', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();

            index.truncate(-5);
            expect(index.length).to.be(0);

            index.close();
            index.open();
            expect(index.length).to.be(0);
        });

    });

    describe('validRange', function(){

        it('returns false for out of range from positions', function(){
            index = setupIndexWithEntries(5);
            expect(index.validRange(0, 1)).to.be(false);
            expect(index.validRange(-1, 1)).to.be(false);
            expect(index.validRange(index.length + 1, index.length + 2)).to.be(false);
        });

        it('returns false when from greater until', function(){
            index = setupIndexWithEntries(5);
            expect(index.validRange(2, 1)).to.be(false);
            expect(index.validRange(1, 0)).to.be(false);
        });

        it('returns false for out of range until positions', function(){
            index = setupIndexWithEntries(5);
            expect(index.validRange(1, -1)).to.be(false);
            expect(index.validRange(1, index.length +1)).to.be(false);
        });

        it('returns true for valid range positions', function(){
            index = setupIndexWithEntries(5);
            expect(index.validRange(1, 1)).to.be(true);
            expect(index.validRange(1, index.length)).to.be(true);
            expect(index.validRange(index.length, index.length)).to.be(true);
        });

    });

    describe('destroy', function(){

        it('completely deletes the file', function(){
            index = setupIndexWithEntries(5);
            index.destroy();
            expect(fs.existsSync('test/data/.index')).to.be(false);
        });

    });
});
<commit_msg>Add test for searching an item with minimum number and no matches<commit_after>const expect = require('expect.js');
const fs = require('fs-extra');
const Index = require('../src/Index');

describe('Index', function() {

    let index;

    beforeEach(function() {
        fs.emptyDirSync('test/data');
    });

    afterEach(function() {
        if (index) index.close();
        index = undefined;
    });

    function setupIndexWithEntries(num, indexMapper, options) {
        if (typeof indexMapper === 'object') {
            options = indexMapper;
            indexMapper = undefined;
        }
        index = new Index('test.index', Object.assign({ dataDirectory: 'test/data' }, options));
        for (let i = 1; i <= num; i++) {
            index.add(new Index.Entry(indexMapper && indexMapper(i) || i, i));
        }
        return index;
    }

    it('is opened on instanciation', function() {
        index = setupIndexWithEntries();
        expect(index.isOpen()).to.be(true);
    });

    it('defaults name to ".index"', function() {
        index = new Index({ dataDirectory: 'test/data' });
        expect(index.name).to.be('.index');
    });

    it('recovers metadata on reopening', function() {
        index = new Index('test/data/.index', { metadata: { test: 'valueStays' } });
        expect(index.metadata.test).to.be('valueStays');
        index.close();
        index = new Index('test/data/.index');
        expect(index.metadata.test).to.be('valueStays');
    });

    it('throws on opening an non-index file', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        fs.writeSync(fd, 'foo');
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid file header/);
    });

    it('throws on opening an index file with different version', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        fs.writeSync(fd, 'nesidx00');
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid file version/);
    });

    it('throws on opening an index file with wrong metadata size', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        const metadataBuffer = Buffer.allocUnsafe(8 + 4);
        metadataBuffer.write("nesidx01", 0, 8, 'utf8');
        metadataBuffer.writeUInt32BE(0, 8, true);
        fs.writeSync(fd, metadataBuffer);
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid metadata size/);
    });

    it('throws on opening an index file with too large metadata size', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        const metadataBuffer = Buffer.allocUnsafe(8 + 4 + 3);
        metadataBuffer.write("nesidx01", 0, 8, 'utf8');
        metadataBuffer.writeUInt32BE(255, 8, true);
        metadataBuffer.write("{}\n", 12, 3, 'utf8');
        fs.writeSync(fd, metadataBuffer);
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid index file/);
    });

    it('throws on opening an index file with invalid metadata', function() {
        let fd = fs.openSync('test/data/.index', 'a+');
        const metadataBuffer = Buffer.allocUnsafe(8 + 4);
        metadataBuffer.write("nesidx01", 0, 8, 'utf8');
        metadataBuffer.writeUInt32BE(255, 8, true);
        fs.writeSync(fd, metadataBuffer);
        fs.closeSync(fd);
        expect(() => index = new Index('test/data/.index')).to.throwError(/Invalid metadata/);
    });

    it('throws on reopening with altered metadata', function() {
        index = new Index('test/data/.index', { metadata: { test: 'valueStays' } });
        expect(() => index = new Index('test/data/.index', { metadata: { test: 'anotherValue' } })).to.throwError(/Index metadata mismatch/);
    });

    it('throws on opening with altered file', function() {
        index = setupIndexWithEntries(5);
        index.close();
        fs.appendFileSync('test/data/test.index', 'foo');
        expect(() => index = new Index('test/data/test.index')).to.throwError(/Index file is corrupt/);
    });

    describe('Entry', function() {

        it('stores data correctly', function() {
            let entry = new Index.Entry(1, 2, 3, 4);
            expect(entry.number).to.be(1);
            expect(entry.position).to.be(2);
            expect(entry.size).to.be(3);
            expect(entry.partition).to.be(4);
        });

    });

    describe('add', function() {

        it('appends entries sequentially', function() {
            index = setupIndexWithEntries(25);
            index.close();
            index.open();
            let entries = index.all();
            expect(entries.length).to.be(25);
            for (let i = 1; i <= entries.length; i++) {
                expect(entries[i - 1].number).to.be(i);
            }
        });

        it('calls callback eventually', function(done) {
            index = new Index('test/data/.index', { flushDelay: 1 });
            let position = index.add(new Index.Entry(1, 0), (number) => {
                expect(number).to.be(position);
                done();
            });
        });

        it('throws with invalid entry object', function() {
            index = new Index('test/data/.index');
            expect(() => index.add([1,2,3,4])).to.throwError(/Wrong entry object/);
        });

        it('throws with invalid entry size', function() {
            index = new Index('test/data/.index');
            class Entry extends Index.Entry {
                static get size() {
                    return 20;
                }
            }
            expect(() => index.add(new Entry(1, 0))).to.throwError(/Invalid entry size/);
        });

    });

    describe('get', function() {

        it('returns false on out of bounds position', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();
            expect(index.get(0)).to.be(false);
            expect(index.get(index.length+1)).to.be(false);
        });

        it('can read entry from the end', function() {
            setupIndexWithEntries(5);
            index.close();
            index.open();
            let entry = index.get(-1);
            expect(entry.number).to.be(index.length);
        });

        it('returns false on closed index', function() {
            index = setupIndexWithEntries(1);
            index.close();
            expect(index.get(1)).to.be(false);
        });

        it('can random read entries', function() {
            index = setupIndexWithEntries(10);
            index.close();
            index.open();
            let entry = index.get(5);
            expect(entry.number).to.be(5);
        });

        it('can read entries multiple times', function() {
            index = setupIndexWithEntries(10);
            index.close();
            index.open();
            for (let i = 0; i < 5; i++) {
                let entry = index.get(5);
                expect(entry.number).to.be(5);
            }
        });

    });

    describe('range', function() {

        it('returns false on out of bounds range position', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            expect(index.range(0)).to.be(false);
            expect(index.range(51, 55)).to.be(false);
            expect(index.range(1, 51)).to.be(false);
            expect(index.range(15, 10)).to.be(false);
        });

        it('returns false on closed index', function() {
            index = setupIndexWithEntries(1);
            index.close();
            expect(index.range(1)).to.be(false);
        });

        it('can read an arbitrary range of entries', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            let entries = index.range(21, 37);
            for (let i = 0; i < entries.length; i++) {
                expect(entries[i].number).to.be(21 + i);
            }
        });

        it('can read a range of entries from the end', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            let entries = index.range(-15);
            expect(entries.length).to.be(15);
            for (let i = 0; i < entries.length; i++) {
                expect(entries[i].number).to.be(36 + i);
            }
        });

        it('can read a range of entries until a distance from the end', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            let entries = index.range(1, -15);
            expect(entries.length).to.be(36);   // 36 because end is inclusive
            for (let i = 0; i < entries.length; i++) {
                expect(entries[i].number).to.be(1 + i);
            }
        });

        it('can read a single item range of entries', function() {
            index = setupIndexWithEntries(50);
            index.close();
            index.open();
            let entries = index.range(21, 21);
            expect(entries.length).to.be(1);
            expect(entries[0].number).to.be(21);
        });

        it('returns false with a non-numeric range', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();
            let entries = index.range('foo');
            expect(entries).to.be(false);
        });

    });

    describe('lastEntry', function() {

        it('returns the last entry', function() {
            index = setupIndexWithEntries(5);
            expect(index.lastEntry.number).to.be(5);
        });

        it('returns false on empty index', function() {
            index = setupIndexWithEntries(0);
            expect(index.lastEntry).to.be(false);
        });

    });

    describe('find', function() {

        it('returns 0 if no entry is lower or equal searched number', function() {
            index = setupIndexWithEntries(5, i => 5 + i);
            expect(index.find(index.length)).to.be(0);
        });

        it('returns last entry if all entries are lower searched number', function() {
            index = setupIndexWithEntries(5);
            expect(index.find(index.length+1)).to.be(index.length);
        });

        it('returns 0 if all entries are lower searched number with min=true', function() {
            index = setupIndexWithEntries(5);
            expect(index.find(index.length+1, true)).to.be(0);
        });

        it('returns the entry number on exact match', function() {
            index = setupIndexWithEntries(5);
            for (let i = 1; i <= 5; i++) {
                expect(index.find(i)).to.be(i);
            }
        });

        it('returns the highest entry number lower than the searched number', function() {
            index = setupIndexWithEntries(50, i => 2*i);
            expect(index.find(25)).to.be(12);
        });

        it('returns the lowest entry number higher than the searched number with min=true', function() {
            index = setupIndexWithEntries(50, i => 2*i);
            expect(index.find(25, true)).to.be(13);
        });

    });

    describe('truncate', function() {

        it('truncates after the given index position', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();

            index.truncate(2);
            expect(index.length).to.be(2);

            index.close();
            index.open();
            expect(index.length).to.be(2);
        });

        it('correctly truncates after unflushed entries', function() {
            index = setupIndexWithEntries(5);

            index.truncate(2);
            expect(index.length).to.be(2);

            index.close();
            index.open();
            expect(index.length).to.be(2);
        });

        it('does nothing if truncating after index length', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();

            index.truncate(6);
            expect(index.length).to.be(5);

            index.close();
            index.open();
            expect(index.length).to.be(5);
        });

        it('truncates whole index if given negative position', function() {
            index = setupIndexWithEntries(5);
            index.close();
            index.open();

            index.truncate(-5);
            expect(index.length).to.be(0);

            index.close();
            index.open();
            expect(index.length).to.be(0);
        });

    });

    describe('validRange', function(){

        it('returns false for out of range from positions', function(){
            index = setupIndexWithEntries(5);
            expect(index.validRange(0, 1)).to.be(false);
            expect(index.validRange(-1, 1)).to.be(false);
            expect(index.validRange(index.length + 1, index.length + 2)).to.be(false);
        });

        it('returns false when from greater until', function(){
            index = setupIndexWithEntries(5);
            expect(index.validRange(2, 1)).to.be(false);
            expect(index.validRange(1, 0)).to.be(false);
        });

        it('returns false for out of range until positions', function(){
            index = setupIndexWithEntries(5);
            expect(index.validRange(1, -1)).to.be(false);
            expect(index.validRange(1, index.length +1)).to.be(false);
        });

        it('returns true for valid range positions', function(){
            index = setupIndexWithEntries(5);
            expect(index.validRange(1, 1)).to.be(true);
            expect(index.validRange(1, index.length)).to.be(true);
            expect(index.validRange(index.length, index.length)).to.be(true);
        });

    });

    describe('destroy', function(){

        it('completely deletes the file', function(){
            index = setupIndexWithEntries(5);
            index.destroy();
            expect(fs.existsSync('test/data/.index')).to.be(false);
        });

    });
});
<|endoftext|>


<commit_before>$.easing.doubleSqrt = function(t, millisecondsSince, startValue, endValue, totalDuration) {
    var res = Math.sqrt(Math.sqrt(t));
    return res;
};

(function($) {

    $.tileBlockSlider = function(element, options) {

        // настройки по умолчанию
        var defaults = {
            // период смены картинок
            period: 2000,
            // продолжительность анимации
            duration: 1000,
            // направление анимации (up, down, left, right)
            direction: 'up'
        };
        // объект плагина
        var plugin = this;
        // настройки конкретного объекта
        plugin.settings = {};

        var $element = $(element), // reference to the jQuery version of DOM element
            element = element;    // reference to the actual DOM element

        var blocks, // все картинки
            currentBlockIndex, // индекс текущего блока
            slideInPosition, // стартовое положение блока перед началом появления
            slideOutPosition, // финальное положение блока при скрытии
            tileWidth, // размеры плитки
            tileHeight;

        // инициализируем
        plugin.init = function () {

            plugin.settings = $.extend({}, defaults, options);

            // все блоки
            blocks = $element.children(".tile-content");

            // если блок всего 1, то слайдинг не нужен
            if (blocks.length <= 1) {
                return;
            }

            // индекс активного в данный момент блока
            currentBlockIndex = 0;

            // размеры текущей плитки
            tileWidth = $element.innerWidth();
            tileHeight = $element.innerHeight();
            // положение блоков
            slideInPosition = getSlideInPosition();
            slideOutPosition = getSlideOutPosition();

            // подготавливаем блоки к анимации
            blocks.each(function (index, block) {
                block = $(block);
                // блоки должны быть position:absolute
                // возможно этот параметр задан через класс стилей
                // проверяем, и добавляем если это не так
                if (block.css('position') !== 'absolute') {
                    block.css('position', 'absolute');
                }
                // скрываем все блоки кроме первого
                if (index !== 0) {
                    block.css('left', tileWidth);
                }
            });

            // запускаем интервал для смены блоков
            setInterval(function () {
                slideBlock();
            }, plugin.settings.period);
        };

        // смена блоков
        var slideBlock = function() {

            var slideOutBlock, // блок который надо скрыть
                slideInBlock, // блок который надо показать
                mainPosition = {'left': 0, 'top': 0},
                options;

            slideOutBlock = $(blocks[currentBlockIndex]);

            currentBlockIndex++;
            if (currentBlockIndex >= blocks.length) {
                currentBlockIndex = 0;
            }
            slideInBlock = $(blocks[currentBlockIndex]);

            slideInBlock.css(slideInPosition);

            options = {
                duration: plugin.settings.duration,
                easing: 'doubleSqrt'
            };

            slideOutBlock.animate(slideOutPosition, options);
            slideInBlock.animate(mainPosition, options);
        };

        /**
         * возвращает стартовую позицию для блока который должен появиться {left: xxx, top: yyy}
         */
        var getSlideInPosition = function () {
            var pos;
            if (plugin.settings.direction === 'left') {
                pos = {
                    'left': tileWidth,
                    'top': 0
                }
            } else if (plugin.settings.direction === 'right') {
                pos = {
                    'left': -tileWidth,
                    'top': 0
                }
            } else if (plugin.settings.direction === 'up') {
                pos = {
                    'left': 0,
                    'top': tileHeight
                }
            } else if (plugin.settings.direction === 'down') {
                pos = {
                    'left': 0,
                    'top': -tileHeight
                }
            }
            return pos;
        };

        /**
         * возвращает финальную позицию для блока который должен скрыться {left: xxx, top: yyy}
         */
        var getSlideOutPosition = function () {
            var pos;
            if (plugin.settings.direction === 'left') {
                pos = {
                    'left': -tileWidth,
                    'top': 0
                }
            } else if (plugin.settings.direction === 'right') {
                pos = {
                    'left': tileWidth,
                    'top': 0
                }
            } else if (plugin.settings.direction === 'up') {
                pos = {
                    'left': 0,
                    'top': -tileHeight
                }
            } else if (plugin.settings.direction === 'down') {
                pos = {
                    'left': 0,
                    'top': tileHeight
                }
            }
            return pos;
        };

        plugin.getParams = function() {

            // code goes here

        }

        plugin.init();

    }

    $.fn.tileBlockSlider = function(options) {
        return this.each(function() {
            if (undefined == $(this).data('tileBlockSlider')) {
                var plugin = new $.tileBlockSlider(this, options);
                $(this).data('tileBlockSlider', plugin);
            }
        });
    }

})(jQuery);

$(window).ready(function(){
    var slidedTiles = $('[data-role=tile-slider], .block-slider, .tile-slider');
    slidedTiles.each(function (index, tile) {
        var params = {};
        tile = $(tile);
        params.direction = tile.data('paramDirection');
        params.duration = tile.data('paramDuration');
        params.period = tile.data('paramPeriod');
        tile.tileBlockSlider(params);
    })

});<commit_msg>Enabled $()["TileSlider"]({initAll: true}) support and generally update to make consistent with the other plug-ins<commit_after>/**
 * TileSlider- jQuery plugin for MetroUiCss framework
 */

$.easing.doubleSqrt = function(t, millisecondsSince, startValue, endValue,
		totalDuration) {
	var res = Math.sqrt(Math.sqrt(t));
	return res;
};

(function($) {
	var pluginName = 'TileSlider', initAllSelector = '[data-role=tile-slider], .block-slider, .tile-slider', paramKeys = [
			'Period', 'Duration', 'Direction' ];

	$[pluginName] = function(element, options) {

		if (!element) {
			return $()[pluginName]({
				initAll : true
			});
		}

		// default settings
		// TODO: expose defaults with a $.fn[pluginName].defaults ={} block;
		var defaults = {
			// the period for changing images
			period : 2000,
			// animation length
			duration : 1000,
			// direction of animation (up, down, left, right)
			direction : 'up'
		};
		// object plugin
		var plugin = this;
		// plugin settings
		plugin.settings = {};

		var $element = $(element); // reference to the jQuery version of DOM element

		var blocks, //
		currentBlockIndex, // the index of the current block
		slideInPosition, // the start location of the block before the advent
		slideOutPosition, // the final location of the block when you hide
		tileWidth, // tile dimensions
		tileHeight;

		// initialize
		plugin.init = function() {

			plugin.settings = $.extend({}, defaults, options);

			// all blocks
			blocks = $element.children(".tile-content");

			// Do nothing if only single content block
			if (blocks.length <= 1) {
				return;
			}

			// the index of the currently active block
			currentBlockIndex = 0;

			// current tile dimensions
			tileWidth = $element.innerWidth();
			tileHeight = $element.innerHeight();
			// block position
			slideInPosition = getSlideInPosition();
			slideOutPosition = getSlideOutPosition();

			// prepare the block animation

			blocks.each(function(index, block) {
				block = $(block);
				// blocks should be position:absolute
				// may be specified through style class
				// check and add if it is not
				if (block.css('position') !== 'absolute') {
					block.css('position', 'absolute');
				}
				// hide all blocks except the first
				// TODO: add random start option
				if (index !== 0) {
					block.css('left', tileWidth);
				}
			});

			// Use setInterval to do the period run
			// TODO: preserve return value of setInterval to handle a clean stop.
			setInterval(function() {
				slideBlock();
			}, plugin.settings.period);
		};

		// changing blocks
		var slideBlock = function() {

			var slideOutBlock, // the block that you want to hide
			slideInBlock, // block to show
			mainPosition = {
				'left' : 0,
				'top' : 0
			}, options;

			slideOutBlock = $(blocks[currentBlockIndex]);

			currentBlockIndex++;
			if (currentBlockIndex >= blocks.length) {
				currentBlockIndex = 0;
			}
			slideInBlock = $(blocks[currentBlockIndex]);

			slideInBlock.css(slideInPosition);

			options = {
				duration : plugin.settings.duration,
				easing : 'doubleSqrt'
			};

			slideOutBlock.animate(slideOutPosition, options);
			slideInBlock.animate(mainPosition, options);
		};

		// TODO: Consider refactoring the two functions below into one, given that the return values of
		// each are simply the negative of the twin (given that -0=0)
		// would seem an opportunity to shrink code size - consider good function name and usage to not
		// diminish the self-documentation value of the two functions.

		/**
		 *
		 * Returns the starting position for the block that you want to appear {left: xxx, top: yyy}
		 */
		var getSlideInPosition = function() {
			var pos;
			if (plugin.settings.direction === 'left') {
				pos = {
					'left' : tileWidth,
					'top' : 0
				}
			} else if (plugin.settings.direction === 'right') {
				pos = {
					'left' : -tileWidth,
					'top' : 0
				}
			} else if (plugin.settings.direction === 'up') {
				pos = {
					'left' : 0,
					'top' : tileHeight
				}
			} else if (plugin.settings.direction === 'down') {
				pos = {
					'left' : 0,
					'top' : -tileHeight
				}
			}
			return pos;
		};

		/**
		 *
		 * Returns the final position for the block to escape {left: xxx, top: yyy}
		 */
		var getSlideOutPosition = function() {
			var pos;
			if (plugin.settings.direction === 'left') {
				pos = {
					'left' : -tileWidth,
					'top' : 0
				}
			} else if (plugin.settings.direction === 'right') {
				pos = {
					'left' : tileWidth,
					'top' : 0
				}
			} else if (plugin.settings.direction === 'up') {
				pos = {
					'left' : 0,
					'top' : -tileHeight
				}
			} else if (plugin.settings.direction === 'down') {
				pos = {
					'left' : 0,
					'top' : tileHeight
				}
			}
			return pos;
		};

		plugin.getParams = function() {

			// code goes here

		};

		plugin.init();

	};

	$.fn[pluginName] = function(options) {
		var elements = options && options.initAll ? $(initAllSelector) : this;
		return elements.each(function() {
			var that = $(this), params = {}, plugin;
			if (undefined == that.data(pluginName)) {
				$.each(paramKeys, function(index, key) {
					// TODO: Look into IE10 issue
					// added try/catch block as I was seeing "no such method .toLowerCase()" errors in IE10,
					// although not in Chrome/Win (27.0.1453.94) Safari/Win (5.1.7) or Firefox/Win (21.0)
					// interestingly, catching and ignoring the exeception has tiles in IE10 behave correctly, but
					// throwing an "alert" into the catch will result in the tiles not having specified settings
					// applied - only the defaults.
					try {
						params[key[0].toLowerCase() + key.slice(1)] = that
								.data('param' + key);
					} catch (e) {

					}
				});
				plugin = new $[pluginName](this, params);
				that.data(pluginName, plugin);
			}
		});
	};

	// autoinit
	$(function() {
		$()[pluginName]({
			initAll : true
		});
	});

})(jQuery);
<|endoftext|>


<commit_before>//this is my new blog... isn't it great?
function getBlog() {
    alert("infunction");
    $.getJSON('entries.json', function(data) {
        alert("we got the file");
        $.each(data.entries, function(key, val) {
            alert("reading the file");
            console.log(key + " " + val.date);
        });
    });
};
$(document).ready(function(){
    alert("My body is ready");
    getBlog();
});

<commit_msg>This is readable.<commit_after>//this is my new blog... isn't it great?
function getBlog() {
    alert("infunction");
    $.getJSON('Ientries.json', function(data) {
        alert("we got the file");
        $.each(data.entries, function(key, val) {
            alert("reading the file");
            console.log(key + " " + val.date);
        });
    });
};
$(document).ready(function(){
    alert("My body is ready");
    getBlog();
});

<|endoftext|>


<commit_before>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

var annotationEvents = {};

(function() {
  function clickOptionReplay(element, eventMessage) {
    element.selected = true;
  }

  function clickSelectRecord(eventData, eventMessage) {
    eventMessage.value = eventData.target.value;
  }

  function clickSelectReplay(element, eventMessage) {
    element.value = eventMessage.value;
  }

  function keypressRecord(eventData, eventMessage) {
    if ("value" in eventData.target)
      eventMessage.value = eventData.target.value;
  }

  function keypressReplay(element, eventMessage) {
    if ("value" in eventMessage)
      element.value = eventMessage.value;
  }

  annotationEvents = {
    "keypress": {
      guard: function(eventData, eventMessage) {
        return eventMessage.type == "keypress";
      },
      record: keypressRecord,
      replay: keypressReplay
    },
    "clickOption": {
      guard: function(eventData, eventMessage) {
        return eventMessage.nodeName == "option" &&
               eventMessage.type == "click";
      },
      record: null,
      replay: clickOptionReplay
    },
    "clickSelect": {
      guard: function(eventData, eventMessage) {
        return eventMessage.nodeName == "select" &&
               eventMessage.type == "click";
      },
      record: clickSelectRecord,
      replay: clickSelectReplay
    },
  };
})();
<commit_msg>Fixed a keypress record/replay problem.<commit_after>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

var annotationEvents = {};

(function() {
  function clickOptionReplay(element, eventMessage) {
    element.selected = true;
  }

  function clickSelectRecord(eventData, eventMessage) {
    eventMessage.value = eventData.target.value;
  }

  function clickSelectReplay(element, eventMessage) {
    element.value = eventMessage.value;
  }

  function keypressReplay(element, eventMessage) {
    element.value=element.value+String.fromCharCode(eventMessage.keyCode);
  }

  annotationEvents = {
    "keypress": {
      guard: function(eventData, eventMessage) {
        return eventMessage.type == "keypress";
      },
      record: null,
      replay: keypressReplay
    },
    "clickOption": {
      guard: function(eventData, eventMessage) {
        return eventMessage.nodeName == "option" &&
               eventMessage.type == "click";
      },
      record: null,
      replay: clickOptionReplay
    },
    "clickSelect": {
      guard: function(eventData, eventMessage) {
        return eventMessage.nodeName == "select" &&
               eventMessage.type == "click";
      },
      record: clickSelectRecord,
      replay: clickSelectReplay
    },
  };
})();
<|endoftext|>