<commit_before>/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, window, $, brackets */

/**
 * Manages layout of panels surrounding the editor area, and size of the editor area (but not its contents).
 *
 * Updates panel sizes when the window is resized. Maintains the max resizing limits for panels, based on
 * currently available window size.
 *
 * Events:
 *    - editorAreaResize -- When editor-holder's size changes for any reason (including panel show/hide
 *              panel resize, or the window resize).
 *              The 2nd arg is the new editor-holder height.
 *              The 3rd arg is a refreshHint flag for internal EditorManager use.
 */
define(function (require, exports, module) {
    "use strict";

    var AppInit                 = require("utils/AppInit"),
        Resizer                 = require("utils/Resizer");

    /** @type {jQueryObject} The ".content" vertical stack (editor + all header/footer panels) */
    var $windowContent;

    /** @type {jQueryObject} The "#editor-holder": has only one visible child, the current CodeMirror
        instance (or the no-editor placeholder) */
    var $editorHolder;

    /** @type {boolean} Have we already started listening for the end of the ongoing window resize? */
    var windowResizing = false;

    /**
     * Calculates the available height for the full-size Editor (or the no-editor placeholder),
     * accounting for the current size of all visible panels, toolbar, & status bar.
     * @return {number}
     */
    function calcEditorHeight() {
        var availableHt = $windowContent.height();

        $editorHolder.siblings().each(function (i, elem) {
            var $elem = $(elem);
            if ($elem.css("display") !== "none" && $elem.css("position") !== "absolute") {
                availableHt -= $elem.outerHeight();
            }
        });

        // Clip value to 0 (it could be negative if a panel wants more space than we have)
        return Math.max(availableHt, 0);
    }

    /** Updates panel resize limits to disallow making panels big enough to shrink editor area below 0 */
    function updateResizeLimits() {
        var editorAreaHeight = $editorHolder.height();

        $editorHolder.siblings().each(function (i, elem) {
            var $elem = $(elem);
            if ($elem.css("display") === "none") {
                $elem.data("maxsize", editorAreaHeight);
            } else {
                $elem.data("maxsize", editorAreaHeight + $elem.outerHeight());
            }
        });
    }

    /**
     * Calculates a new size for editor-holder and resizes it accordingly, then and dispatches the "editorAreaResize"
     * event. (The editors within are resized by EditorManager, in response to that event).
     *
     * @param {string=} refreshHint  One of "skip", "force", or undefined. See EditorManager docs.
     */
    function triggerEditorResize(refreshHint) {
        // Find how much space is left for the editor
        var editorAreaHeight = calcEditorHeight();

        $editorHolder.height(editorAreaHeight);  // affects size of "not-editor" placeholder as well

        // Resize editor to fill the space
        $(exports).trigger("editorAreaResize", [editorAreaHeight, refreshHint]);
    }

    /** Trigger editor area resize whenever the window is resized */
    function handleWindowResize() {
        // These are not initialized in Jasmine Spec Runner window until a test
        // is run that creates a mock document.
        if (!$windowContent || !$editorHolder) {
            return;
        }

        // Immediately adjust editor's height, but skip the refresh since CodeMirror will call refresh()
        // itself when it sees the window resize event
        // triggerEditorResize("skip");

        // FIXME (issue #4564) Workaround https://github.com/marijnh/CodeMirror/issues/1787
        triggerEditorResize();

        if (!windowResizing) {
            windowResizing = true;

            // We don't need any fancy debouncing here - we just need to react before the user can start
            // resizing any panels at the new window size. So just listen for first mousemove once the
            // window resize releases mouse capture.
            $(window.document).one("mousemove", function () {
                windowResizing = false;
                updateResizeLimits();
            });
        }
    }

    /** Trigger editor area resize whenever the given panel is shown/hidden/resized */
    function listenToResize($panel) {
        // Update editor height when shown/hidden, & continuously as panel is resized
        $panel.on("panelCollapsed panelExpanded panelResizeUpdate", function () {
            triggerEditorResize();
        });
        // Update max size of sibling panels when shown/hidden, & at *end* of resize gesture
        $panel.on("panelCollapsed panelExpanded panelResizeEnd", function () {
            updateResizeLimits();
        });
    }

    /**
     * Represents a panel below the editor area (a child of ".content").
     *
     * @param {!jQueryObject} $panel  The entire panel, including any chrome, already in the DOM.
     * @param {number=} minSize  Minimum height of panel in px; default is DEFAULT_MIN_SIZE
     */
    function Panel($panel, minSize) {
        this.$panel = $panel;

        Resizer.makeResizable($panel[0], Resizer.DIRECTION_VERTICAL, Resizer.POSITION_TOP, minSize, false, undefined, true);
        listenToResize($panel);
    }

    /** @type {jQueryObject} */
    Panel.prototype.$panel = null;

    Panel.prototype.isVisible = function () {
        return this.$panel.is(":visible");
    };

    Panel.prototype.show = function () {
        Resizer.show(this.$panel[0]);
    };
    Panel.prototype.hide = function () {
        Resizer.hide(this.$panel[0]);
    };

    Panel.prototype.setVisible = function (visible) {
        if (visible) {
            Resizer.show(this.$panel[0]);
        } else {
            Resizer.hide(this.$panel[0]);
        }
    };

    /**
     * Creates a new panel beneath the editor area and above the status bar footer. Panel is initially invisible.
     *
     * @param {!string} id  Unique id for this panel. Use package-style naming, e.g. "myextension.feature.panelname"
     * @param {!jQueryObject} $panel  DOM content to use as the panel. Need not be in the document yet.
     * @param {number=} minSize  Minimum height of panel in px; default is DEFAULT_MIN_SIZE
     * @return {!Panel}
     */
    function createBottomPanel(id, $panel, minSize) {
        $panel.insertBefore("#status-bar");
        $panel.hide();
        updateResizeLimits();  // initialize panel's max size

        return new Panel($panel, minSize);
    }

    /**
     * Used by EditorManager to notify us of layout changes our normal panel/window listeners wouldn't detect.
     * For internal use only: most code should call EditorManager.resizeEditor().
     */
    function _notifyLayoutChange(refreshHint) {
        triggerEditorResize(refreshHint);
        updateResizeLimits();
    }

    // Attach to key parts of the overall UI, once created
    AppInit.htmlReady(function () {
        $windowContent = $(".content");
        $editorHolder = $("#editor-holder");

        // Sidebar is a special case: it isn't a Panel, and is not created dynamically. Need to explicitly
        // listen for resize here.
        listenToResize($("#sidebar"));
    });

    // Unit test only: allow passing in mock DOM notes, e.g. for use with SpecRunnerUtils.createMockEditor()
    function _setMockDOM($mockWindowContent, $mockEditorHolder) {
        $windowContent = $mockWindowContent;
        $editorHolder = $mockEditorHolder;
    }

    // If someone adds a panel in the .content stack the old way, make sure we still listen for resize/show/hide
    // (Resizer emits a deprecation warning for us - no need to log anything here)
    $(Resizer).on("deprecatedPanelAdded", function (event, $panel) {
        listenToResize($panel);
    });

    // Add this as a capture handler so we're guaranteed to run it before the editor does its own
    // refresh on resize.
    window.addEventListener("resize", handleWindowResize, true);

    // Define public API
    exports.createBottomPanel    = createBottomPanel;
    exports._notifyLayoutChange  = _notifyLayoutChange;
    exports._setMockDOM          = _setMockDOM;
});
<commit_msg>Modified comment<commit_after>/*
 * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, window, $, brackets */

/**
 * Manages layout of panels surrounding the editor area, and size of the editor area (but not its contents).
 *
 * Updates panel sizes when the window is resized. Maintains the max resizing limits for panels, based on
 * currently available window size.
 *
 * Events:
 *    - editorAreaResize -- When editor-holder's size changes for any reason (including panel show/hide
 *              panel resize, or the window resize).
 *              The 2nd arg is the new editor-holder height.
 *              The 3rd arg is a refreshHint flag for internal EditorManager use.
 */
define(function (require, exports, module) {
    "use strict";

    var AppInit                 = require("utils/AppInit"),
        Resizer                 = require("utils/Resizer");

    /** @type {jQueryObject} The ".content" vertical stack (editor + all header/footer panels) */
    var $windowContent;

    /** @type {jQueryObject} The "#editor-holder": has only one visible child, the current CodeMirror
        instance (or the no-editor placeholder) */
    var $editorHolder;

    /** @type {boolean} Have we already started listening for the end of the ongoing window resize? */
    var windowResizing = false;

    /**
     * Calculates the available height for the full-size Editor (or the no-editor placeholder),
     * accounting for the current size of all visible panels, toolbar, & status bar.
     * @return {number}
     */
    function calcEditorHeight() {
        var availableHt = $windowContent.height();

        $editorHolder.siblings().each(function (i, elem) {
            var $elem = $(elem);
            if ($elem.css("display") !== "none" && $elem.css("position") !== "absolute") {
                availableHt -= $elem.outerHeight();
            }
        });

        // Clip value to 0 (it could be negative if a panel wants more space than we have)
        return Math.max(availableHt, 0);
    }

    /** Updates panel resize limits to disallow making panels big enough to shrink editor area below 0 */
    function updateResizeLimits() {
        var editorAreaHeight = $editorHolder.height();

        $editorHolder.siblings().each(function (i, elem) {
            var $elem = $(elem);
            if ($elem.css("display") === "none") {
                $elem.data("maxsize", editorAreaHeight);
            } else {
                $elem.data("maxsize", editorAreaHeight + $elem.outerHeight());
            }
        });
    }

    /**
     * Calculates a new size for editor-holder and resizes it accordingly, then and dispatches the "editorAreaResize"
     * event. (The editors within are resized by EditorManager, in response to that event).
     *
     * @param {string=} refreshHint  One of "skip", "force", or undefined. See EditorManager docs.
     */
    function triggerEditorResize(refreshHint) {
        // Find how much space is left for the editor
        var editorAreaHeight = calcEditorHeight();

        $editorHolder.height(editorAreaHeight);  // affects size of "not-editor" placeholder as well

        // Resize editor to fill the space
        $(exports).trigger("editorAreaResize", [editorAreaHeight, refreshHint]);
    }

    /** Trigger editor area resize whenever the window is resized */
    function handleWindowResize() {
        // These are not initialized in Jasmine Spec Runner window until a test
        // is run that creates a mock document.
        if (!$windowContent || !$editorHolder) {
            return;
        }

        // Immediately adjust editor's height, but skip the refresh since CodeMirror will call refresh()
        // itself when it sees the window resize event
        // triggerEditorResize("skip");

        // FIXME (issue #4564) Workaround https://github.com/marijnh/CodeMirror/issues/1787
        triggerEditorResize();

        if (!windowResizing) {
            windowResizing = true;

            // We don't need any fancy debouncing here - we just need to react before the user can start
            // resizing any panels at the new window size. So just listen for first mousemove once the
            // window resize releases mouse capture.
            $(window.document).one("mousemove", function () {
                windowResizing = false;
                updateResizeLimits();
            });
        }
    }

    /** Trigger editor area resize whenever the given panel is shown/hidden/resized */
    function listenToResize($panel) {
        // Update editor height when shown/hidden, & continuously as panel is resized
        $panel.on("panelCollapsed panelExpanded panelResizeUpdate", function () {
            triggerEditorResize();
        });
        // Update max size of sibling panels when shown/hidden, & at *end* of resize gesture
        $panel.on("panelCollapsed panelExpanded panelResizeEnd", function () {
            updateResizeLimits();
        });
    }

    /**
     * Represents a panel below the editor area (a child of ".content").
     *
     * @param {!jQueryObject} $panel  The entire panel, including any chrome, already in the DOM.
     * @param {number=} minSize  Minimum height of panel in px; default is DEFAULT_MIN_SIZE
     */
    function Panel($panel, minSize) {
        this.$panel = $panel;

        Resizer.makeResizable($panel[0], Resizer.DIRECTION_VERTICAL, Resizer.POSITION_TOP, minSize, false, undefined, true);
        listenToResize($panel);
    }

    /** @type {jQueryObject} */
    Panel.prototype.$panel = null;

    Panel.prototype.isVisible = function () {
        return this.$panel.is(":visible");
    };

    Panel.prototype.show = function () {
        Resizer.show(this.$panel[0]);
    };
    Panel.prototype.hide = function () {
        Resizer.hide(this.$panel[0]);
    };

    Panel.prototype.setVisible = function (visible) {
        if (visible) {
            Resizer.show(this.$panel[0]);
        } else {
            Resizer.hide(this.$panel[0]);
        }
    };

    /**
     * Creates a new panel beneath the editor area and above the status bar footer. Panel is initially invisible.
     *
     * @param {!string} id  Unique id for this panel. Use package-style naming, e.g. "myextension.feature.panelname"
     * @param {!jQueryObject} $panel  DOM content to use as the panel. Need not be in the document yet.
     * @param {number=} minSize  Minimum height of panel in px.
     * @return {!Panel}
     */
    function createBottomPanel(id, $panel, minSize) {
        $panel.insertBefore("#status-bar");
        $panel.hide();
        updateResizeLimits();  // initialize panel's max size

        return new Panel($panel, minSize);
    }

    /**
     * Used by EditorManager to notify us of layout changes our normal panel/window listeners wouldn't detect.
     * For internal use only: most code should call EditorManager.resizeEditor().
     */
    function _notifyLayoutChange(refreshHint) {
        triggerEditorResize(refreshHint);
        updateResizeLimits();
    }

    // Attach to key parts of the overall UI, once created
    AppInit.htmlReady(function () {
        $windowContent = $(".content");
        $editorHolder = $("#editor-holder");

        // Sidebar is a special case: it isn't a Panel, and is not created dynamically. Need to explicitly
        // listen for resize here.
        listenToResize($("#sidebar"));
    });

    // Unit test only: allow passing in mock DOM notes, e.g. for use with SpecRunnerUtils.createMockEditor()
    function _setMockDOM($mockWindowContent, $mockEditorHolder) {
        $windowContent = $mockWindowContent;
        $editorHolder = $mockEditorHolder;
    }

    // If someone adds a panel in the .content stack the old way, make sure we still listen for resize/show/hide
    // (Resizer emits a deprecation warning for us - no need to log anything here)
    $(Resizer).on("deprecatedPanelAdded", function (event, $panel) {
        listenToResize($panel);
    });

    // Add this as a capture handler so we're guaranteed to run it before the editor does its own
    // refresh on resize.
    window.addEventListener("resize", handleWindowResize, true);

    // Define public API
    exports.createBottomPanel    = createBottomPanel;
    exports._notifyLayoutChange  = _notifyLayoutChange;
    exports._setMockDOM          = _setMockDOM;
});
<|endoftext|>


<commit_before>let motivos = [
	'No começo era legal, mas agora eu quero matar todo mundo',
	'Uma capivara me disse pra fazer isso',
	'Papai quer que eu faça medicina',
	'Acabou o café',
	'Acabou o leite do café',
	'Acabou o açucar do café',
	'Nunca posso nada nessa p****!',
	'Demissão não se pede, se ganha!',
	'A empresa usa Sybase',
	'O comercial vendeu uma feature que não existe no sistema',
	'Agendaram a homologação do sistema para às 18h de sexta'
];

document.getElementById('motivo').firstChild.innerText = motivos[Math.trunc(Math.random() * motivos.length)];
<commit_msg>novo motivo<commit_after>let motivos = [
	'No começo era legal, mas agora eu quero matar todo mundo',
	'Uma capivara me disse pra fazer isso',
	'Papai quer que eu faça medicina',
	'Acabou o café',
	'Acabou o leite do café',
	'Acabou o açucar do café',
	'Nunca posso nada nessa p****!',
	'Demissão não se pede, se ganha!',
	'A empresa usa Sybase',
	'O comercial vendeu uma feature que não existe no sistema',
	'Agendaram a homologação do sistema para às 18h de sexta',
	'Não entendi a change, mas os totais voltaram a bater'
];

document.getElementById('motivo').firstChild.innerText = motivos[Math.trunc(Math.random() * motivos.length)];
<|endoftext|>


<commit_before>/* global window */
define(['../core/extend', '../core/ext/Properties', '../core/on', '../core/deferredUtils', './utils/setClass', './utils/append'], function (extend, Properties, on, def, setClass, append) {
	'use strict';
	window.setTimeout(function () {
		on(window.document.body, 'click', function (/*evt*/) {
			on.emit(window.document.body, '9jsclosewidgets', { target: null });
			//evt.stopPropagation();
		});
	}, 0);
	function createWaitNode (parent) {
		setClass(parent, 'njsWaiting');
		return setClass(append(parent, 'div'), 'njsWaitNode');
	}
	function destroyWaitNode (parent, node) {
		setClass(parent, '!njsWaiting');
		parent.removeChild(node);
	}
	var EventHandler = function (owner, collection, action) {
		this.owner = owner;
		this.action = action;
		this.remove = function () {
			var index = collection.indexOf(this);
			if (index >= 0) {
				collection.splice(index, 1);
			}
		};
		this.stopPropagation = function () {
			this.bubbles = false;
			this.cancelled = true;
		};
		collection.push(this);
	};

	var Widget = extend({
		'$njsWidget': true,
		/**
		 * Calls destroy() on every registered child, and later removes all event listeners.
		 * Extend this function in order to do any implementation specific destroy logic,like finalizing non-ninejs child components.
		 * @return {undefined}
		 */
		destroy: function () {
			var cnt,
				len = this.$njsChildWidgets.length;
			for (cnt = 0; cnt < len; cnt += 1) {
				this.$njsChildWidgets[cnt].destroy();
			}
			len = this.$njsEventListenerHandlers.length;
			this.remove();
			for (cnt = 0; cnt < len; cnt += 1) {
				this.$njsEventListenerHandlers[cnt].remove();
			}
		},
		/**
		 * Retisters a new child widget.
		 * @param  {ninejs/ui/Widget} w the new child to be added.
		 * @return {undefined}
		 */
		registerChildWidget: function (w) {
			this.$njsChildWidgets.push(w);
		},
		/**
		 * Remove this Widget from its parent, if this.domNode and this.domNode.parentNode are defined. Also emits a 'removing' event with an empty data.
		 * This method is used during destroying sequence in order to detach child and parent widgets.
		 *
		 * @return {boolean} true if the component was removed from its parent; false otherwise.
		 */
		remove: function () {
			if (this.domNode && this.domNode.parentNode) {
				this.emit('removing', {});
				this.domNode.parentNode.removeChild(this.domNode);
				return true;
			}
			return false;
		},
		/**
		 * Sets the new Skin. If value is a string just returns loadSkin(value); otherwise the following checks are performed:
		 * If this widget has a skinContract; then every function and property of the current skinContract must have a match on the new skin.
		 * @param  {string|promise|object} value New skin
		 * @return {object}       A promise (if loadSkin() was called); or the actual skin value (object or string)
		 * @throws {Error} If one function or property is not found
		 */
		skinSetter: function (value) {
			if (typeof(value) === 'string') {
				return this.loadSkin(value);
			}
			var self = this;
			this.skin = value;
			return def.when(value, function (sk) {
				var skinContract = self.skinContract,
					p,
					item;
				if (skinContract) {
					for (p in skinContract) {
						if (skinContract.hasOwnProperty(p)) {
							item = skinContract[p];
							if (item.type === 'function') {
								if (typeof(sk[p]) !== 'function') {
									throw new Error('incompatible skins. This skin must have a ' + p + ' function defined');
								}
							}
							else if (item.type === 'property') {
								if ((typeof(sk[p]) === 'undefined') || (typeof(sk[p]) === 'function')) {
									throw new Error('incompatible skins. This skin must have a ' + p + ' property defined');
								}
							}
						}
					}
				}
				self.skin = sk;
				return sk;
			}, function (err) {
				throw new Error(err);
			});
		},
		/**
		 * Sets the given css class (or classes separated by space).
		 * If the domNode exists the assigment is performed imnediately, otherwise is executed on the 'updateSkin' event (only once.)
		 * @param  {string} v Single or space-separated list of CSS classes
		 * @return {undefined}
		 */
		classSetter: function (v) {
			var arg = v.split(' ');
			var self = this;
			def.when(this.domNode, function () {
				arg.unshift(self.domNode);
				setClass.apply(null, arg);
			});
		},
		/**
		 * Sets the given id to the domNoe.
		 * If the domNode exists the assigment is performed imnediately, otherwise is executed on the 'updateSkin' event (only once.)
		 * @param  {string} v dom id for this component
		 * @return {undefined}
		 */
		idSetter: function (v) {
			var self = this;
			def.when(this.domNode, function () {
				self.domNode.id = v;
			});
		},
		/**
		 * Sets the given style to the domNode.
		 * If the domNode exists the assigment is performed imnediately, otherwise is executed on the 'updateSkin' event (only once.)
		 * @param  {object} v Style for widget's domNode
		 * @return {undefined}
		 */
		styleSetter: function (v) {
			var self = this;
			def.when(this.domNode, function () {
				self.domNode.style = v;
			});
		},
		/**
		 * If the current skin is a single object (not an array) and it applies (skin.applies() is true) then is cosindered the current option to be applied.
		 *
		 * If the current skin is an array then all elements are checked to see if any one applies, the first one that applies is considered the one to be applied.
		 *
		 * If some skin was selected to be applied and is different from the currentSkin the following steps are performed:
		 * (1) If currentSkin is defined an 'updatingSkin' event is emitted; and all other skins (the ones that did not apply) are disabled.
		 * (2) A promise is built using skin.enable().
		 *
		 * @return {promise} A promise that gets resoved when skin.enable(); after that the new skin is assigned to currentSkin and onUpdatedSkin() gets called.
		 */
		updateSkin: function () {
			var self = this;
			return def.when(this.skin, function () {
				var cnt, itemSkin, currentSkin = self.currentSkin, skinList = [], toApply;
				if ((typeof(self.skin) === 'object') && !extend.isArray(self.skin)) {
					skinList.push(self.skin);
					if (self.skin.applies()){
						toApply = self.skin;
					}
				}
				else if (self.skin && self.skin.length) {
					for (cnt = 0; cnt < self.skin.length; cnt += 1) {
						itemSkin = self.skin[cnt];
						if (!toApply && itemSkin.applies()){
							toApply = itemSkin;
						}
						skinList.push(itemSkin);
					}
				}
				if (toApply !== currentSkin) {
					if (currentSkin) {
						self.emit('updatingSkin', {});
						for (cnt = 0; cnt < skinList.length; cnt += 1) {
							itemSkin = skinList[cnt];
							itemSkin.disable();
						}
					}
					return def.when(toApply.enable(self), function () {
						self.currentSkin = toApply;
						if (self.$njsShowDefer) {
							self.$njsShowDefer.resolve(self.domNode);
							self.$njsShowDefer = null;
						}
						self.onUpdatedSkin();
					});
				}
			});
		},
		/**
		 * Calls update over the currentSkin and then emits an 'updatedSkin' event without data.
		 * @return {undefined}
		 */
		onUpdatedSkin: function () {
			var self = this;
			this.currentSkin.updated(this);
			setTimeout(function() {
				self.emit('updatedSkin', {});
			}, 10);
		},
		/**
		 * If currentSkin is defined it gets disabled.
		 * After that currentSkin is set to null and updateSkin() is called.
		 * @return {object} same as updateSkin()
		 */
		forceUpdateSkin: function() {
			if (this.currentSkin) {
				this.currentSkin.disable();
			}
			this.currentSkin = null;
			this.updateSkin();
		},
		/**
		 * Wraps the skin within a promise and calls 'skin' setter. Then the module represented by 'name' is required and resolved to the actual value.
		 * @param  {string} name Skin component AMD path
		 * @return {promise}      The promise used to wraps the skin
		 */
		loadSkin: function(name) {
			var defer = def.defer();
			this.set('skin', defer.promise);
			require([name], function (skin) {
				defer.resolve(skin);
			});
			return defer.promise;
		},
		/**
		 * Adds all arguments to the Event Listener Handlers list. This list is later used when destroying the widget to call remove over all handlers.
		 * @return {undefined}
		 */
		own: function () {
			var cnt,
				len = arguments.length;
			for (cnt = 0; cnt < len; cnt += 1) {
				this.$njsEventListenerHandlers.push(arguments[cnt]);
			}
		},
		/**
		 * If the widged has a currentSkin then it is appended (as text or domNode) to the parent node.
		 * If the widged does not have a skin yet, then a promise is returned that resolves when updateSkin() finished; at that point all event listeners are moved (for the old domNode) and attached to the new one. The node is appended to the parent node and a self referece is returned.
		 * @param  {string|domNode} parentNode The id of the dom element, or the element itself
		 * @return {object}            This widget or a promise
		 */
		show: function (parentNode) {
			var listeners,
				current,
				cnt,
				self = this;
			function appendIt() {
				if (typeof(parentNode) === 'string') {
					parentNode = window.document.getElementById(parentNode);
				}
				if (parentNode) {
					parentNode.appendChild(self.domNode);
				}
				return self;
			}
			if (!this.currentSkin) {
				if (this.waitSkin) {
					return this.waitSkin;
				}
				else {
					this.waitSkin = def.when(this.updateSkin(), function(/*sk*/) {
						if (self.domNode) {
							listeners =  self.$njsEventListeners;
							for (cnt = 0; cnt < self.$njsEventListenerHandlers; cnt += 1) {
								current = self.$njsEventListenerHandlers[cnt];
								current.remove();
							}
							self.$njsEventListenerHandlers = [];
							for (var p in listeners) {
								if (listeners.hasOwnProperty(p)) {
									current = listeners[p];
									for (cnt = 0; cnt < current.length; cnt += 1) {
										self.$njsEventListenerHandlers.push(on(self.domNode, p, current[cnt]));
									}
								}
							}
						}
						var result = appendIt();
						self.waitSkin = null;
						return result;
					});
					return this.waitSkin;
				}
			}
			else {
				return appendIt();
			}
		},
		/**
		 * Register a new handler for the given event.
		 * @param  {string} type         event type
		 * @param  {function} action       new handler
		 * @param  {boolean} persistEvent if true the action will be added to internal event listeners map and the handler to the event listeners handler list
		 * @return {object}              A pointer to the new event handler
		 * @throws {Error} If domNode is not defined
		 */
		on: function (type, action, persistEvent) {
			var r;
			if (!this.$njsEventListeners[type]) {
				this.$njsEventListeners[type] = [];
			}
			r = new EventHandler(this, this.$njsEventListeners[type], function (e) {
				action.apply(this.owner, arguments);
				if (this.owner.domNode && e.bubbles && (!e.cancelled)) {
					on.emit(this.owner.domNode, type, e);
				}
			});
			if (persistEvent) {
				this.$njsEventListenerHandlers.push(r);
			}
			else {
				this.own(r);
			}
			return r;
		},
		/**
		 * Short hand for on.emit(this.domNode, type, data)
		 * @param  {string} type Type of event/message being emitted
		 * @param  {object} data Event/message data
		 * @return {undefined}
		 */
		emit: function (type, data) {
			var method = 'on' + type;
			if (!this[method]) {
				this[method] = function (e) {
					var cnt,
						arr = this.$njsEventListeners[type] || [],
						len = arr.length;
					for (cnt = 0; cnt < len; cnt += 1) {
						arr[cnt].action.call(arr[cnt], e);
					}
				};
			}
			this[method].call(this, data);
		},
		/**
		 * Allows for a Widget to display a state while waiting for a promise.
		 * @param defer {Promise}
		 * @returns {Promise}
		 */
		wait: function (defer) {
			var d,
				self = this;
			if (defer) {
				if (typeof(defer.then) === 'function') {
					var w = self.waitNode || self.domNode;
					if (this.domNode) {
						var waitNode = createWaitNode(w);
						return def.when(defer, function () {
							destroyWaitNode(w, waitNode);
						}, function () {
							destroyWaitNode(w, waitNode);
						});
					}
					else {
						return def.when(this.show(), function () {
							var waitNode = createWaitNode(w);
							return def.when(defer, function () {
								destroyWaitNode(w, waitNode);
							}, function () {
								destroyWaitNode(w, waitNode);
							});
						}, function (err) {
							throw err;
						});
					}
				}
			}
			d = def.defer();
			d.resolve(true);
			return d.promise;
		}
	}, Properties,
	/**
	 * Sets default values for skin, skinContract,$njsEventListeners, $njsEventListenerHandlers, and $njsChildWidgets.
	 *
	 * @return {undefined}
	 */
	function () {
		this.skin = this.skin || [];
		this.skinContract = this.skinContract || [];
		this.$njsEventListeners = {};
		this.$njsEventListenerHandlers = [];
		this.$njsChildWidgets = [];
		this.$njsShowDefer = def.defer();
		this.domNode = this.$njsShowDefer.promise;
	});
	return Widget;
});<commit_msg>Fix domNode resolution on Widget.<commit_after>/* global window */
define(['../core/extend', '../core/ext/Properties', '../core/on', '../core/deferredUtils', './utils/setClass', './utils/append'], function (extend, Properties, on, def, setClass, append) {
	'use strict';
	window.setTimeout(function () {
		on(window.document.body, 'click', function (/*evt*/) {
			on.emit(window.document.body, '9jsclosewidgets', { target: null });
			//evt.stopPropagation();
		});
	}, 0);
	function createWaitNode (parent) {
		setClass(parent, 'njsWaiting');
		return setClass(append(parent, 'div'), 'njsWaitNode');
	}
	function destroyWaitNode (parent, node) {
		setClass(parent, '!njsWaiting');
		parent.removeChild(node);
	}
	var EventHandler = function (owner, collection, action) {
		this.owner = owner;
		this.action = action;
		this.remove = function () {
			var index = collection.indexOf(this);
			if (index >= 0) {
				collection.splice(index, 1);
			}
		};
		this.stopPropagation = function () {
			this.bubbles = false;
			this.cancelled = true;
		};
		collection.push(this);
	};

	var Widget = extend({
		'$njsWidget': true,
		/**
		 * Calls destroy() on every registered child, and later removes all event listeners.
		 * Extend this function in order to do any implementation specific destroy logic,like finalizing non-ninejs child components.
		 * @return {undefined}
		 */
		destroy: function () {
			var cnt,
				len = this.$njsChildWidgets.length;
			for (cnt = 0; cnt < len; cnt += 1) {
				this.$njsChildWidgets[cnt].destroy();
			}
			len = this.$njsEventListenerHandlers.length;
			this.remove();
			for (cnt = 0; cnt < len; cnt += 1) {
				this.$njsEventListenerHandlers[cnt].remove();
			}
		},
		/**
		 * Retisters a new child widget.
		 * @param  {ninejs/ui/Widget} w the new child to be added.
		 * @return {undefined}
		 */
		registerChildWidget: function (w) {
			this.$njsChildWidgets.push(w);
		},
		/**
		 * Remove this Widget from its parent, if this.domNode and this.domNode.parentNode are defined. Also emits a 'removing' event with an empty data.
		 * This method is used during destroying sequence in order to detach child and parent widgets.
		 *
		 * @return {boolean} true if the component was removed from its parent; false otherwise.
		 */
		remove: function () {
			if (this.domNode && this.domNode.parentNode) {
				this.emit('removing', {});
				this.domNode.parentNode.removeChild(this.domNode);
				return true;
			}
			return false;
		},
		/**
		 * Sets the new Skin. If value is a string just returns loadSkin(value); otherwise the following checks are performed:
		 * If this widget has a skinContract; then every function and property of the current skinContract must have a match on the new skin.
		 * @param  {string|promise|object} value New skin
		 * @return {object}       A promise (if loadSkin() was called); or the actual skin value (object or string)
		 * @throws {Error} If one function or property is not found
		 */
		skinSetter: function (value) {
			if (typeof(value) === 'string') {
				return this.loadSkin(value);
			}
			var self = this;
			this.skin = value;
			return def.when(value, function (sk) {
				var skinContract = self.skinContract,
					p,
					item;
				if (skinContract) {
					for (p in skinContract) {
						if (skinContract.hasOwnProperty(p)) {
							item = skinContract[p];
							if (item.type === 'function') {
								if (typeof(sk[p]) !== 'function') {
									throw new Error('incompatible skins. This skin must have a ' + p + ' function defined');
								}
							}
							else if (item.type === 'property') {
								if ((typeof(sk[p]) === 'undefined') || (typeof(sk[p]) === 'function')) {
									throw new Error('incompatible skins. This skin must have a ' + p + ' property defined');
								}
							}
						}
					}
				}
				self.skin = sk;
				return sk;
			}, function (err) {
				throw new Error(err);
			});
		},
		/**
		 * Sets the given css class (or classes separated by space).
		 * If the domNode exists the assigment is performed imnediately, otherwise is executed on the 'updateSkin' event (only once.)
		 * @param  {string} v Single or space-separated list of CSS classes
		 * @return {undefined}
		 */
		classSetter: function (v) {
			var arg = v.split(' ');
			var self = this;
			def.when(this.domNode, function () {
				arg.unshift(self.domNode);
				setClass.apply(null, arg);
			});
		},
		/**
		 * Sets the given id to the domNoe.
		 * If the domNode exists the assigment is performed imnediately, otherwise is executed on the 'updateSkin' event (only once.)
		 * @param  {string} v dom id for this component
		 * @return {undefined}
		 */
		idSetter: function (v) {
			var self = this;
			def.when(this.domNode, function () {
				self.domNode.id = v;
			});
		},
		/**
		 * Sets the given style to the domNode.
		 * If the domNode exists the assigment is performed imnediately, otherwise is executed on the 'updateSkin' event (only once.)
		 * @param  {object} v Style for widget's domNode
		 * @return {undefined}
		 */
		styleSetter: function (v) {
			var self = this;
			def.when(this.domNode, function () {
				self.domNode.style = v;
			});
		},
		/**
		 * If the current skin is a single object (not an array) and it applies (skin.applies() is true) then is cosindered the current option to be applied.
		 *
		 * If the current skin is an array then all elements are checked to see if any one applies, the first one that applies is considered the one to be applied.
		 *
		 * If some skin was selected to be applied and is different from the currentSkin the following steps are performed:
		 * (1) If currentSkin is defined an 'updatingSkin' event is emitted; and all other skins (the ones that did not apply) are disabled.
		 * (2) A promise is built using skin.enable().
		 *
		 * @return {promise} A promise that gets resoved when skin.enable(); after that the new skin is assigned to currentSkin and onUpdatedSkin() gets called.
		 */
		updateSkin: function () {
			var self = this;
			return def.when(this.skin, function () {
				var cnt, itemSkin, currentSkin = self.currentSkin, skinList = [], toApply;
				if ((typeof(self.skin) === 'object') && !extend.isArray(self.skin)) {
					skinList.push(self.skin);
					if (self.skin.applies()){
						toApply = self.skin;
					}
				}
				else if (self.skin && self.skin.length) {
					for (cnt = 0; cnt < self.skin.length; cnt += 1) {
						itemSkin = self.skin[cnt];
						if (!toApply && itemSkin.applies()){
							toApply = itemSkin;
						}
						skinList.push(itemSkin);
					}
				}
				if (toApply !== currentSkin) {
					if (currentSkin) {
						self.emit('updatingSkin', {});
						for (cnt = 0; cnt < skinList.length; cnt += 1) {
							itemSkin = skinList[cnt];
							itemSkin.disable();
						}
					}
					return def.when(toApply.enable(self), function () {
						self.currentSkin = toApply;
						if (self.$njsShowDefer) {
							self.$njsShowDefer.resolve(self.domNode);
							self.$njsShowDefer = null;
						}
						self.onUpdatedSkin();
					});
				}
			});
		},
		/**
		 * Calls update over the currentSkin and then emits an 'updatedSkin' event without data.
		 * @return {undefined}
		 */
		onUpdatedSkin: function () {
			var self = this;
			this.currentSkin.updated(this);
			setTimeout(function() {
				self.emit('updatedSkin', {});
			}, 10);
		},
		/**
		 * If currentSkin is defined it gets disabled.
		 * After that currentSkin is set to null and updateSkin() is called.
		 * @return {object} same as updateSkin()
		 */
		forceUpdateSkin: function() {
			if (this.currentSkin) {
				this.currentSkin.disable();
			}
			this.currentSkin = null;
			this.updateSkin();
		},
		/**
		 * Wraps the skin within a promise and calls 'skin' setter. Then the module represented by 'name' is required and resolved to the actual value.
		 * @param  {string} name Skin component AMD path
		 * @return {promise}      The promise used to wraps the skin
		 */
		loadSkin: function(name) {
			var defer = def.defer();
			this.set('skin', defer.promise);
			require([name], function (skin) {
				defer.resolve(skin);
			});
			return defer.promise;
		},
		/**
		 * Adds all arguments to the Event Listener Handlers list. This list is later used when destroying the widget to call remove over all handlers.
		 * @return {undefined}
		 */
		own: function () {
			var cnt,
				len = arguments.length;
			for (cnt = 0; cnt < len; cnt += 1) {
				this.$njsEventListenerHandlers.push(arguments[cnt]);
			}
		},
		/**
		 * If the widged has a currentSkin then it is appended (as text or domNode) to the parent node.
		 * If the widged does not have a skin yet, then a promise is returned that resolves when updateSkin() finished; at that point all event listeners are moved (for the old domNode) and attached to the new one. The node is appended to the parent node and a self referece is returned.
		 * @param  {string|domNode} parentNode The id of the dom element, or the element itself
		 * @return {object}            This widget or a promise
		 */
		show: function (parentNode) {
			var listeners,
				current,
				cnt,
				self = this;
			function appendIt() {
				if (typeof(parentNode) === 'string') {
					parentNode = window.document.getElementById(parentNode);
				}
				if (parentNode) {
					parentNode.appendChild(self.domNode);
				}
				return self;
			}
			if (!this.currentSkin) {
				if (this.waitSkin) {
					return this.waitSkin;
				}
				else {
					this.waitSkin = def.when(this.updateSkin(), function(/*sk*/) {
						if (self.domNode) {
							listeners =  self.$njsEventListeners;
							for (cnt = 0; cnt < self.$njsEventListenerHandlers; cnt += 1) {
								current = self.$njsEventListenerHandlers[cnt];
								current.remove();
							}
							self.$njsEventListenerHandlers = [];
							for (var p in listeners) {
								if (listeners.hasOwnProperty(p)) {
									current = listeners[p];
									for (cnt = 0; cnt < current.length; cnt += 1) {
										self.$njsEventListenerHandlers.push(on(self.domNode, p, current[cnt]));
									}
								}
							}
						}
						var result = appendIt();
						self.waitSkin = null;
						return result;
					});
					return this.waitSkin;
				}
			}
			else {
				return appendIt();
			}
		},
		/**
		 * Register a new handler for the given event.
		 * @param  {string} type         event type
		 * @param  {function} action       new handler
		 * @param  {boolean} persistEvent if true the action will be added to internal event listeners map and the handler to the event listeners handler list
		 * @return {object}              A pointer to the new event handler
		 * @throws {Error} If domNode is not defined
		 */
		on: function (type, action, persistEvent) {
			var r;
			if (!this.$njsEventListeners[type]) {
				this.$njsEventListeners[type] = [];
			}
			r = new EventHandler(this, this.$njsEventListeners[type], function (e) {
				action.apply(this.owner, arguments);
				if (this.owner.domNode && e.bubbles && (!e.cancelled)) {
					on.emit(this.owner.domNode, type, e);
				}
			});
			if (persistEvent) {
				this.$njsEventListenerHandlers.push(r);
			}
			else {
				this.own(r);
			}
			return r;
		},
		/**
		 * Short hand for on.emit(this.domNode, type, data)
		 * @param  {string} type Type of event/message being emitted
		 * @param  {object} data Event/message data
		 * @return {undefined}
		 */
		emit: function (type, data) {
			var method = 'on' + type;
			if (!this[method]) {
				this[method] = function (e) {
					var cnt,
						arr = this.$njsEventListeners[type] || [],
						len = arr.length;
					for (cnt = 0; cnt < len; cnt += 1) {
						arr[cnt].action.call(arr[cnt], e);
					}
				};
			}
			this[method].call(this, data);
		},
		/**
		 * Allows for a Widget to display a state while waiting for a promise.
		 * @param defer {Promise}
		 * @returns {Promise}
		 */
		wait: function (defer) {
			var d,
				self = this;
			if (defer) {
				if (typeof(defer.then) === 'function') {
					var w = self.waitNode || self.domNode;
					if (this.domNode) {
						var waitNode = createWaitNode(w);
						return def.when(defer, function () {
							destroyWaitNode(w, waitNode);
						}, function () {
							destroyWaitNode(w, waitNode);
						});
					}
					else {
						return def.when(this.show(), function () {
							var waitNode = createWaitNode(w);
							return def.when(defer, function () {
								destroyWaitNode(w, waitNode);
							}, function () {
								destroyWaitNode(w, waitNode);
							});
						}, function (err) {
							throw err;
						});
					}
				}
			}
			d = def.defer();
			d.resolve(true);
			return d.promise;
		}
	}, Properties,
	/**
	 * Sets default values for skin, skinContract,$njsEventListeners, $njsEventListenerHandlers, and $njsChildWidgets.
	 *
	 * @return {undefined}
	 */
	function () {
		this.skin = this.skin || [];
		this.skinContract = this.skinContract || [];
		this.$njsEventListeners = {};
		this.$njsEventListenerHandlers = [];
		this.$njsChildWidgets = [];
		this.$njsShowDefer = def.defer();
		// this.domNode = this.$njsShowDefer.promise;
		if (!this.domNode) {
            this.domNode = this.$njsShowDefer.promise;
        }
        else {
            this.$njsShowDefer.resolve(this.domNode);
            this.$njsShowDefer = null;
        }
	});
	return Widget;
});<|endoftext|>


<commit_before>
(function() {
  /**
   * Ace Editor Custom theme.
   */
  define('ace/theme/asi',['require','exports'], (require, exports) => {
    exports.isDark = true;
    exports.cssClass = 'ace-asi';
  })
  ///////////////////////////////////////

  const LOCAL_STORAGE_KEY = 'asi-snake';

  const CFG = {
    cellSize: 20,
    gridPadding: 5,
    snakeLength: 4,
    speed: 100,
  };

  const D = {
    UP: 'up',
    RIGHT: 'right',
    DOWN: 'down',
    LEFT: 'left',
  };

  const styleMap = {
    0: getCSSVar('cell-inactive-color'),
    1: getCSSVar('cell-snake-head-color'),
    2: getCSSVar('cell-snake-tail-color'),
    3: getCSSVar('cell-point-color'),
  };

  const initialContent = `/**
 * @description Function to return the moves to locate the next point.
 *
 * @param {Number} xMax Number of cells across the x axis.
 * @param {Number} yMax Number of cells across the y axis.
 * @param {Array} snake Array of [X,Y] coordinates of the current snake from head to tail.
 * @param {Array} point [X, Y] coordinates of where the point will be.
 * @param {Object} D Constant directions to be returned (UP, RIGHT, DOWN, LEFT)
 *
 * @return {Array} Moves for each cell i.e [D.UP, D.UP, D.RIGHT]
 */

function findPath(xMax, yMax, snake, point, D) {

   // Do your thing...

}`;

  let timeout;
  let running;

  const canvas = document.getElementById('js__canvas');
  const startButton = document.getElementById('js__start');
  const resetButton = document.getElementById('js__reset');
  const pointsEl = document.getElementById('js__points');
  const scoreEl = document.getElementById('js__score');
  const movesEl = document.getElementById('js__moves');
  const ideEl = document.getElementById('js__ide');
  const consoleEl = document.getElementById('js__console');

  const ctx = canvas.getContext('2d');

  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;

  const xMax = Math.floor((canvas.width + CFG.gridPadding) / (CFG.cellSize + CFG.gridPadding));
  const yMax = Math.floor((canvas.height + CFG.gridPadding) / (CFG.cellSize + CFG.gridPadding));

  const xPadding = (canvas.width - (CFG.cellSize * xMax)) / (xMax - 1);
  const yPadding = (canvas.height - (CFG.cellSize * yMax)) / (yMax - 1);

  const editor = ace.edit(ideEl);

  editor.setShowPrintMargin(false);
  editor.setShowFoldWidgets(false);
  editor.setHighlightActiveLine(false);
  editor.$blockScrolling = Infinity;
  editor.setTheme('ace/theme/asi');
  editor.getSession().setMode('ace/mode/javascript');
  editor.session.setOptions({
    tabSize: 2,
    useSoftTabs: true,
  });

  editor.setValue(localStorage.getItem(LOCAL_STORAGE_KEY) || initialContent, 1);
  editor.on('change', handleEditorChange);

  startButton.addEventListener('click', handleStart);
  resetButton.addEventListener('click', handleReset);

  handleReset();

  function getCSSVar(variable) {
    return window
      .getComputedStyle(document.documentElement)
      .getPropertyValue(`--${variable}`)
      .trim();
  }

  function handleStart() {
    const snake = createSnake();
    const point = createPoint(snake);
    const points = 0;
    const moves =[];

    running = true;

    startButton.setAttribute('disabled', '');
    resetButton.removeAttribute('disabled');

    update({ points, point, snake, moves });
    redraw({ point, snake });
  }

  function handleReset() {
    clearTimeout(timeout);

    running = false;

    resetButton.setAttribute('disabled', '');
    startButton.removeAttribute('disabled');
    handleConsoleLog('');

    redraw({ snake: createSnake() });
  }

  function handleEditorChange() {
    localStorage.setItem(LOCAL_STORAGE_KEY, editor.getValue());
  }

  function handlePointIncrease(points, avgerage, score) {
    pointsEl.innerHTML = points;
    movesEl.innerHTML = Math.floor(avgerage);
    scoreEl.innerHTML = Math.floor(score);
  }

  function handleConsoleLog(message, error) {
    if (error) {
      consoleEl.classList.add('asi-console--error');
    } else {
      consoleEl.classList.remove('asi-console--error');
    }

    consoleEl.innerHTML = message;
  }

  function sandboxRequestMoves({ snake, point }, points, avg, score) {
    let sandboxTimeout;
    let sandbox = new Worker('sandbox.js');

    function cleanSandbox() {
      clearTimeout(sandboxTimeout);
      sandbox.terminate();
      sandbox = null;
    }

    sandbox.onerror = (error) => {
      handleConsoleLog(error);
      cleanSandbox();
    }

    sandbox.onmessage = ({ data: { action, error, moves } }) => {
      switch (action) {
      case 'moves':
        update({ snake, point, moves }, points, avg.concat([moves.length]), score);
        break;
      case 'error':
        handleConsoleLog(error, true);
        break;
      }

      cleanSandbox();
    };

    sandbox.postMessage({
      action: 'request_moves',
      fn: editor.getValue(),
      args: [xMax, yMax, snake, point, D],
    });

    sandboxTimeout = setTimeout(() => {
      console.log('Sandbox time out');
      cleanSandbox();
      handleReset();
    }, 5000);
  }

  function createSnake(position) {
    return new Array(CFG.snakeLength).fill().map((v, i) => [
      Math.floor(xMax / 2) - i,
      Math.floor(yMax / 2)
    ]);
  }

  function createGrid({ snake, point }) {
    const grid = [];

    for (let y = 0; y < yMax; y++) {
      const row = [];

      for (let x = 0; x < xMax; x++) {
        row.push(0);
      }

      grid.push(row);
    }

    if (Array.isArray(snake)) {
      snake.forEach(([x, y], i) => grid[y][x] = i === 0 ? 1 : 2);
    }

    if (Array.isArray(point)) {
      grid[point[1]][point[0]] = 3;
    }

    return grid;
  }

  function createPoint(snake) {
    const x = Math.floor(Math.random() * xMax);
    const y = Math.floor(Math.random() * yMax);

    if (snake.some(([sX, sY]) => x === sX && y === sY)) {
      return createPoint(snake);
    }

    return [x, y];
  }

  function redraw({ point, snake } = {}) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    createGrid({ point, snake }).forEach((row, y) => {
      row.forEach((value, x) => {
        ctx.fillStyle = styleMap[value];
        ctx.fillRect(
          x * (CFG.cellSize + xPadding),
          y * (CFG.cellSize + yPadding),
          CFG.cellSize,
          CFG.cellSize);
      });
    });
  }

  function update({ snake, point, moves }, points = 0, movesHistory = [], score = 0, fromSandbox = false) {
    if (!running) {
      return;
    }

    if (Array.isArray(moves) && moves.length === 0 && !fromSandbox) {
      return sandboxRequestMoves({ snake, point }, points, movesHistory, score);
    }

    if (!Array.isArray(moves) || moves.length === 0) {
      return handleConsoleLog('You need to return some moves bruh 🙄');
    }

    const nextDirection = moves.shift();
    const nextPositionExtended = getNextPosition(snake, nextDirection);
    const nextPosition = nextPositionExtended.slice(0, -1);

    if (!nextDirection || !Object.values(D).includes(nextDirection)) {
      return handleConsoleLog('Looks like something in the returned array is not one of the constant directions (D).');
    }

    const didCollectPoint = willCollectPoint(point, nextPositionExtended);

    if (didCollectPoint) {
      const avg = calcMedian(movesHistory);

      snake = nextPositionExtended;
      point = createPoint(snake);
      points = points + 1;
      score = calcScore(score, avg, points);
      handlePointIncrease(points, avg, score);
    } else {
      snake = nextPosition;
    }

    if (!didCollectPoint && !validateNextPosition(nextPosition)) {
      return handleConsoleLog('The 🐍 crashed 💥');
    }

    redraw({ point, snake });

    timeout = setTimeout(() =>
      update({ snake, point, moves }, points, movesHistory, score, false),
      1000 / CFG.speed
    );
  }

  function getNextPosition(snake, direction) {
    return [shiftPosition(snake[0], direction)].concat(snake);
  }

  function shiftPosition([x, y], direction) {
    switch (direction) {
      case D.UP:    return [x, y - 1];
      case D.RIGHT: return [x + 1, y];
      case D.DOWN:  return [x, y + 1];
      case D.LEFT:  return [x - 1, y];
    }
  }

  function validateNextPosition(nextPosition) {
    return nextPosition.every(([x, y], i) =>
      x >= 0 && x < xMax && y >= 0 && y < yMax &&
      !nextPosition.find(([fX, fY], fI) => fI !== i && fX === x && fY === y)
    );
  }

  function willCollectPoint(point, nextPosition) {
    return point[0] === nextPosition[0][0] &&
      point[1] === nextPosition[0][1];
  }

  function calcMedian(moves) {
    moves = moves.slice(0).sort((a, b) => a - b);

    if (moves.length === 0) {
      avg = 0;
    } if (moves.length === 1) {
      avg = moves[0];
    } else if ((moves.length / 2) % 1) {
      avg = (moves[Math.floor(moves.length / 2)] +  moves[Math.ceil(moves.length / 2)]) / 2;
    } else {
      avg = moves[moves.length / 2];
    }

    return avg;
  }

  function calcScore(score, avg, points) {
    return score + (((avg / xMax) + (avg / yMax)) * points);
  }
})();
<commit_msg>Fixed issue when no array returnerd<commit_after>
(function() {
  /**
   * Ace Editor Custom theme.
   */
  define('ace/theme/asi',['require','exports'], (require, exports) => {
    exports.isDark = true;
    exports.cssClass = 'ace-asi';
  })
  ///////////////////////////////////////

  const LOCAL_STORAGE_KEY = 'asi-snake';

  const CFG = {
    cellSize: 20,
    gridPadding: 5,
    snakeLength: 4,
    speed: 100,
  };

  const D = {
    UP: 'up',
    RIGHT: 'right',
    DOWN: 'down',
    LEFT: 'left',
  };

  const styleMap = {
    0: getCSSVar('cell-inactive-color'),
    1: getCSSVar('cell-snake-head-color'),
    2: getCSSVar('cell-snake-tail-color'),
    3: getCSSVar('cell-point-color'),
  };

  const initialContent = `/**
 * @description Function to return the moves to locate the next point.
 *
 * @param {Number} xMax Number of cells across the x axis.
 * @param {Number} yMax Number of cells across the y axis.
 * @param {Array} snake Array of [X,Y] coordinates of the current snake from head to tail.
 * @param {Array} point [X, Y] coordinates of where the point will be.
 * @param {Object} D Constant directions to be returned (UP, RIGHT, DOWN, LEFT)
 *
 * @return {Array} Moves for each cell i.e [D.UP, D.UP, D.RIGHT]
 */

function findPath(xMax, yMax, snake, point, D) {

   // Do your thing...

}`;

  let timeout;
  let running;

  const canvas = document.getElementById('js__canvas');
  const startButton = document.getElementById('js__start');
  const resetButton = document.getElementById('js__reset');
  const pointsEl = document.getElementById('js__points');
  const scoreEl = document.getElementById('js__score');
  const movesEl = document.getElementById('js__moves');
  const ideEl = document.getElementById('js__ide');
  const consoleEl = document.getElementById('js__console');

  const ctx = canvas.getContext('2d');

  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;

  const xMax = Math.floor((canvas.width + CFG.gridPadding) / (CFG.cellSize + CFG.gridPadding));
  const yMax = Math.floor((canvas.height + CFG.gridPadding) / (CFG.cellSize + CFG.gridPadding));

  const xPadding = (canvas.width - (CFG.cellSize * xMax)) / (xMax - 1);
  const yPadding = (canvas.height - (CFG.cellSize * yMax)) / (yMax - 1);

  const editor = ace.edit(ideEl);

  editor.setShowPrintMargin(false);
  editor.setShowFoldWidgets(false);
  editor.setHighlightActiveLine(false);
  editor.$blockScrolling = Infinity;
  editor.setTheme('ace/theme/asi');
  editor.getSession().setMode('ace/mode/javascript');
  editor.session.setOptions({
    tabSize: 2,
    useSoftTabs: true,
  });

  editor.setValue(localStorage.getItem(LOCAL_STORAGE_KEY) || initialContent, 1);
  editor.on('change', handleEditorChange);

  startButton.addEventListener('click', handleStart);
  resetButton.addEventListener('click', handleReset);

  handleReset();

  function getCSSVar(variable) {
    return window
      .getComputedStyle(document.documentElement)
      .getPropertyValue(`--${variable}`)
      .trim();
  }

  function handleStart() {
    const snake = createSnake();
    const point = createPoint(snake);
    const points = 0;
    const moves =[];

    running = true;

    startButton.setAttribute('disabled', '');
    resetButton.removeAttribute('disabled');

    update({ points, point, snake, moves });
    redraw({ point, snake });
  }

  function handleReset() {
    clearTimeout(timeout);

    running = false;

    resetButton.setAttribute('disabled', '');
    startButton.removeAttribute('disabled');
    handleConsoleLog('');

    redraw({ snake: createSnake() });
  }

  function handleEditorChange() {
    localStorage.setItem(LOCAL_STORAGE_KEY, editor.getValue());
  }

  function handlePointIncrease(points, avgerage, score) {
    pointsEl.innerHTML = points;
    movesEl.innerHTML = Math.floor(avgerage);
    scoreEl.innerHTML = Math.floor(score);
  }

  function handleConsoleLog(message, error) {
    if (error) {
      consoleEl.classList.add('asi-console--error');
    } else {
      consoleEl.classList.remove('asi-console--error');
    }

    consoleEl.innerHTML = message;
  }

  function sandboxRequestMoves({ snake, point }, points, movesHistory, score) {
    let sandboxTimeout;
    let sandbox = new Worker('sandbox.js');

    function cleanSandbox() {
      clearTimeout(sandboxTimeout);
      sandbox.terminate();
      sandbox = null;
    }

    sandbox.onerror = (error) => {
      handleConsoleLog(error);
      cleanSandbox();
    }

    sandbox.onmessage = ({ data: { action, error, moves } }) => {
      switch (action) {
      case 'moves':
        update(
          { snake, point, moves },
          points,
          Array.isArray(moves) && moves.length > 0 ? movesHistory.concat([moves.length]) : movesHistory,
          score
        );
        break;
      case 'error':
        handleConsoleLog(error, true);
        break;
      }

      cleanSandbox();
    };

    sandbox.postMessage({
      action: 'request_moves',
      fn: editor.getValue(),
      args: [xMax, yMax, snake, point, D],
    });

    sandboxTimeout = setTimeout(() => {
      console.log('Sandbox time out');
      cleanSandbox();
      handleReset();
    }, 5000);
  }

  function createSnake(position) {
    return new Array(CFG.snakeLength).fill().map((v, i) => [
      Math.floor(xMax / 2) - i,
      Math.floor(yMax / 2)
    ]);
  }

  function createGrid({ snake, point }) {
    const grid = [];

    for (let y = 0; y < yMax; y++) {
      const row = [];

      for (let x = 0; x < xMax; x++) {
        row.push(0);
      }

      grid.push(row);
    }

    if (Array.isArray(snake)) {
      snake.forEach(([x, y], i) => grid[y][x] = i === 0 ? 1 : 2);
    }

    if (Array.isArray(point)) {
      grid[point[1]][point[0]] = 3;
    }

    return grid;
  }

  function createPoint(snake) {
    const x = Math.floor(Math.random() * xMax);
    const y = Math.floor(Math.random() * yMax);

    if (snake.some(([sX, sY]) => x === sX && y === sY)) {
      return createPoint(snake);
    }

    return [x, y];
  }

  function redraw({ point, snake } = {}) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    createGrid({ point, snake }).forEach((row, y) => {
      row.forEach((value, x) => {
        ctx.fillStyle = styleMap[value];
        ctx.fillRect(
          x * (CFG.cellSize + xPadding),
          y * (CFG.cellSize + yPadding),
          CFG.cellSize,
          CFG.cellSize);
      });
    });
  }

  function update({ snake, point, moves }, points = 0, movesHistory = [], score = 0, fromSandbox = false) {
    if (!running) {
      return;
    }

    if (Array.isArray(moves) && moves.length === 0 && !fromSandbox) {
      return sandboxRequestMoves({ snake, point }, points, movesHistory, score);
    }

    if (!Array.isArray(moves) || moves.length === 0) {
      return handleConsoleLog('You need to return some moves bruh 🙄');
    }

    const nextDirection = moves.shift();
    const nextPositionExtended = getNextPosition(snake, nextDirection);
    const nextPosition = nextPositionExtended.slice(0, -1);

    if (!nextDirection || !Object.values(D).includes(nextDirection)) {
      return handleConsoleLog('Looks like something in the returned array is not one of the constant directions (D).');
    }

    const didCollectPoint = willCollectPoint(point, nextPositionExtended);

    if (didCollectPoint) {
      const avg = calcMedian(movesHistory);

      snake = nextPositionExtended;
      point = createPoint(snake);
      points = points + 1;
      score = calcScore(score, avg, points);
      handlePointIncrease(points, avg, score);
    } else {
      snake = nextPosition;
    }

    if (!didCollectPoint && !validateNextPosition(nextPosition)) {
      return handleConsoleLog('The 🐍 crashed 💥');
    }

    redraw({ point, snake });

    timeout = setTimeout(() =>
      update({ snake, point, moves }, points, movesHistory, score, false),
      1000 / CFG.speed
    );
  }

  function getNextPosition(snake, direction) {
    return [shiftPosition(snake[0], direction)].concat(snake);
  }

  function shiftPosition([x, y], direction) {
    switch (direction) {
      case D.UP:    return [x, y - 1];
      case D.RIGHT: return [x + 1, y];
      case D.DOWN:  return [x, y + 1];
      case D.LEFT:  return [x - 1, y];
    }
  }

  function validateNextPosition(nextPosition) {
    return nextPosition.every(([x, y], i) =>
      x >= 0 && x < xMax && y >= 0 && y < yMax &&
      !nextPosition.find(([fX, fY], fI) => fI !== i && fX === x && fY === y)
    );
  }

  function willCollectPoint(point, nextPosition) {
    return point[0] === nextPosition[0][0] &&
      point[1] === nextPosition[0][1];
  }

  function calcMedian(moves) {
    moves = moves.slice(0).sort((a, b) => a - b);

    if (moves.length === 0) {
      avg = 0;
    } if (moves.length === 1) {
      avg = moves[0];
    } else if ((moves.length / 2) % 1) {
      avg = (moves[Math.floor(moves.length / 2)] +  moves[Math.ceil(moves.length / 2)]) / 2;
    } else {
      avg = moves[moves.length / 2];
    }

    return avg;
  }

  function calcScore(score, avg, points) {
    return score + (((avg / xMax) + (avg / yMax)) * points);
  }
})();
<|endoftext|>


<commit_before>'use strict';

const Script = require('smooch-bot').Script;

module.exports = new Script({
    processing: {
        prompt: (bot) => bot.say('Beep boop...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say('Hi! I\'m Smooch Bot!')
                .then(() => 'askName');
        }
    },

    askName: {
        prompt: (bot) => bot.say('What\'s your name?'),
        receive: (bot, message) => {
            const name = message.text;
            return bot.setProp('name', name)
                .then(() => bot.say(`Great! I'll call you ${name}`))
                .then(() => 'finish');
        }
    },

    finish: {
        receive: (bot, message) => {
            return bot.getProp('name')
                .then((name) => bot.say(`Désolé ${name}, mon créateur ne m/'a pas ` +
                        'appris à faire autre chose!'))
                .then(() => 'finish');
        }
    }
});
<commit_msg>traduction into french<commit_after>'use strict';

const Script = require('smooch-bot').Script;

module.exports = new Script({
    processing: {
        prompt: (bot) => bot.say('Beep boop...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say('Bonjour! Je m\'appelle Estèphe!')
                .then(() => 'askName');
        }
    },

    askName: {
        prompt: (bot) => bot.say('What\'s your name?'),
        receive: (bot, message) => {
            const name = message.text;
            return bot.setProp('name', name)
                .then(() => bot.say(`Super! Je vais t'appeler ${name}`))
                .then(() => 'finish');
        }
    },

    finish: {
        receive: (bot, message) => {
            return bot.getProp('name')
                .then((name) => bot.say(`Désolé ${name}, mon créateur ne m'a pas ` +
                        'appris à faire autre chose!'))
                .then(() => 'finish');
        }
    }
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Adio Visualizer For Youtube
// @namespace    http://tampermonkey.net/
// @version      0.3 BETA
// @description  A simple audio visualizer for Youtube.
// @author       MrAnyone
// @match        https://www.youtube.com/watch?v=*
// @include      https://www.youtube.com/embed/*
// @require      http://code.jquery.com/jquery-latest.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.10/pixi.min.js
// @grant        Thank you very much for the inspiration! https://www.youtube.com/watch?v=okMfwg15lz0
// ==/UserScript==
/* jshint -W097 */
'use strict';

/****************
    SETUP VARS
****************/

//Do debug
var doDebug = true;

//Used to remove some "dead bards"
var excludeRatio = 33;

/**********************
    VISUALIZER VARS
**********************/

//Audio handler variables
var audioCtx = null;
var analyser = null;
var dataArray = null;
var source = null;
var $video = null;

//Pixi vars
var container = null;
var renderer = null;
var g = null;

//Visualization variables
var barWidth = null;
var sizeControl = null;
var i = null;

/****************
    FUNCTIONS
****************/

$(document).ready(function() {
    init();
    setElementSource("video");
    setupView();
    requestAnimationFrame(animate);
});

//Init function
function init() {
    try {

        //Get audio apis from different browsers
        if (!(navigator.getUserMedia)) {
            navigator.getUserMedia = (navigator.getUserMedia ||
                navigator.webkitGetUserMedia ||
                navigator.mozGetUserMedia ||
                navigator.msGetUserMedia);
        }

        if (!(window.AudioContext)) {
            window.AudioContext = window.AudioContext || window.webkitAudioContext;
        }

        //Create the audio context
        if (!(audioCtx)) {
            audioCtx = new AudioContext();
        }

        //Setup the analyser node
        if (!(analyser)) {
            analyser = audioCtx.createAnalyser();
            analyser.fftSize = 256;
            analyser.minDecibels = -80;
            analyser.maxDecibels = 0;
            analyser.smoothingTimeConstant = 0.8;
        }

        //Generate the dataArray
        if (!(dataArray)) {
            dataArray = new Uint8Array(analyser.fftSize / 2);
        }

        //Simple function to map values from one range to another
        Number.prototype.map = function(in_min, in_max, out_min, out_max) {
            return (this - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
        };

        debug("Init successfull!", "INFO");
    } catch (e) {
        alert("Error! Probably your browser doesn't support the Web Audio API!");
        debug(e, "ERROR");
    }
}

function setElementSource(id) {
    //Try to find the video
    $video = $(id);

    if ($video) {
        //Create audio element
        source = audioCtx.createMediaElementSource($video[0]);

        //Route source to analyser & speakers
        source.connect(analyser);
        source.connect(audioCtx.destination);

        debug("setElementSource successfull!", "INFO");
    } else {
        debug("The video element was not found!", "WARNING");
    }
}

function setupView() {
    try {
        //Generate "PIXI stuff"
        if (!(container || renderer || g)) {
            //The container
            container = new PIXI.Container(0x66FF99);

            //The renderer
            renderer = PIXI.autoDetectRenderer($("#player-api").width(), $("#player-api").height());

            //Add the view (canvas) of the renderer
            $("#player-mole-container").prepend(renderer.view);

            //Generate PIXI graphics for bar draw
            g = new PIXI.Graphics();
        }
        //The barWidth based on a percent of the view based on the dataArray
        barWidth = (100 / (dataArray.length - excludeRatio)) * (renderer.width / 100);

        debug("Setup view successfull!", "INFO");
    } catch (e) {
        debug("Failed to setup the view!\n" + e, "ERROR");
    }
}

function animate() {
    //Animate loop
    requestAnimationFrame(animate);

    //Get the audio data
    passByteFrequencyData(dataArray);

    //Removes the "older bars" from graphics
    g.clear();

    //Starts drawing with a color & oppacity
    g.beginFill(0x5CE6FF, 1);

    //Generate the bars based on i dataArray audio size
    for (i = 0; i < dataArray.length - excludeRatio; i++) {
        sizeControl = dataArray[i].map(0, 255, 0, renderer.height);
        g.drawRect(barWidth * i, renderer.height - sizeControl, barWidth, sizeControl);
    }

    //Finally, add the generated stuff to container
    container.addChild(g);

    //Render the Container
    renderer.render(container);
}

/************
    UTILS
************/

//A debug function
function debug(msg, type) {
    if (doDebug) {
        switch (type) {
            case "ERROR":
                console.log("[ERROR] YTMV > " + msg);
                break;
            case "INFO":
                console.log("[INFO] YTMV > " + msg);
                break;
            case "WARNING":
                console.log("[WARNING] YTMV > " + msg);
                break;
            default:
                console.log("[DEBUG] YTMV > " + msg);
                break;
        }
    }
}

//Get the data from the running analyser
function passByteFrequencyData(array) {
    try {
        analyser.getByteFrequencyData(array);
    } catch (e) {
        debug("Error passing the ByteFrequencyData!", "ERROR");
    }
}<commit_msg>Change version to 0.5 BETA<commit_after>// ==UserScript==
// @name         Adio Visualizer For Youtube
// @namespace    http://tampermonkey.net/
// @version      0.5 BETA
// @description  A simple audio visualizer for Youtube.
// @author       MrAnyone
// @match        https://www.youtube.com/watch?v=*
// @include      https://www.youtube.com/embed/*
// @require      http://code.jquery.com/jquery-latest.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/pixi.js/3.0.10/pixi.min.js
// @grant        Thank you very much for the inspiration! https://www.youtube.com/watch?v=okMfwg15lz0
// ==/UserScript==
/* jshint -W097 */
'use strict';

/****************
    SETUP VARS
****************/

//Do debug
var doDebug = true;

//Used to remove some "dead bards"
var excludeRatio = 33;

/**********************
    VISUALIZER VARS
**********************/

//Audio handler variables
var audioCtx = null;
var analyser = null;
var dataArray = null;
var source = null;
var $video = null;

//Pixi vars
var container = null;
var renderer = null;
var g = null;

//Visualization variables
var barWidth = null;
var sizeControl = null;
var i = null;

/****************
    FUNCTIONS
****************/

$(document).ready(function() {
    init();
    setElementSource("video");
    setupView();
    requestAnimationFrame(animate);
});

//Init function
function init() {
    try {

        //Get audio apis from different browsers
        if (!(navigator.getUserMedia)) {
            navigator.getUserMedia = (navigator.getUserMedia ||
                navigator.webkitGetUserMedia ||
                navigator.mozGetUserMedia ||
                navigator.msGetUserMedia);
        }

        if (!(window.AudioContext)) {
            window.AudioContext = window.AudioContext || window.webkitAudioContext;
        }

        //Create the audio context
        if (!(audioCtx)) {
            audioCtx = new AudioContext();
        }

        //Setup the analyser node
        if (!(analyser)) {
            analyser = audioCtx.createAnalyser();
            analyser.fftSize = 256;
            analyser.minDecibels = -80;
            analyser.maxDecibels = 0;
            analyser.smoothingTimeConstant = 0.8;
        }

        //Generate the dataArray
        if (!(dataArray)) {
            dataArray = new Uint8Array(analyser.fftSize / 2);
        }

        //Simple function to map values from one range to another
        Number.prototype.map = function(in_min, in_max, out_min, out_max) {
            return (this - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
        };

        debug("Init successfull!", "INFO");
    } catch (e) {
        alert("Error! Probably your browser doesn't support the Web Audio API!");
        debug(e, "ERROR");
    }
}

//Try to find the video DOM with given ID and create an audio source
function setElementSource(id) {
    //Try to find the video
    $video = $(id);

    if ($video) {
        //Create audio element
        source = audioCtx.createMediaElementSource($video[0]);

        //Route source to analyser & speakers
        source.connect(analyser);
        source.connect(audioCtx.destination);

        debug("setElementSource successfull!", "INFO");
    } else {
        debug("The video element was not found!", "WARNING");
    }
}

//Setup the visualizer
function setupView() {
    try {
        //Generate "PIXI stuff"
        if (!(container || renderer || g)) {
            //The container
            container = new PIXI.Container(0x66FF99);

            //The renderer
            renderer = PIXI.autoDetectRenderer($("#player-api").width(), $("#player-api").height());

            //Add the view (canvas) of the renderer
            $("#player-mole-container").prepend(renderer.view);

            //Generate PIXI graphics for bar draw
            g = new PIXI.Graphics();
        }
        //The barWidth based on a percent of the view based on the dataArray
        barWidth = (100 / (dataArray.length - excludeRatio)) * (renderer.width / 100);

        debug("Setup view successfull!", "INFO");
    } catch (e) {
        debug("Failed to setup the view!\n" + e, "ERROR");
    }
}

//The animate loop
function animate() {
    //Animate loop
    requestAnimationFrame(animate);

    //Get the audio data
    passByteFrequencyData(dataArray);

    //Removes the "older bars" from graphics
    g.clear();

    //Starts drawing with a color & oppacity
    g.beginFill(0x5CE6FF, 1);

    //Generate the bars based on i dataArray audio size
    for (i = 0; i < dataArray.length - excludeRatio; i++) {
        sizeControl = dataArray[i].map(0, 255, 0, renderer.height);
        g.drawRect(barWidth * i, renderer.height - sizeControl, barWidth, sizeControl);
    }

    //Finally, add the generated stuff to container
    container.addChild(g);

    //Render the Container
    renderer.render(container);
}

/************
    UTILS
************/

//A debug function
function debug(msg, type) {
    if (doDebug) {
        switch (type) {
            case "ERROR":
                console.log("[ERROR] YTMV > " + msg);
                break;
            case "INFO":
                console.log("[INFO] YTMV > " + msg);
                break;
            case "WARNING":
                console.log("[WARNING] YTMV > " + msg);
                break;
            default:
                console.log("[DEBUG] YTMV > " + msg);
                break;
        }
    }
}

//Get the data from the running analyser
function passByteFrequencyData(array) {
    try {
        analyser.getByteFrequencyData(array);
    } catch (e) {
        debug("Error passing the ByteFrequencyData!", "ERROR");
    }
}<|endoftext|>


<commit_before>(function(){
  colors = ['#eeeeee', '#d6e685', '#8cc665', '#44a340', '#1e6823'];
  days = document.getElementsByClassName('day');
  Array.prototype.forEach.call(days, function(element) {
    element.addEventListener('click', function(event){
      event.stopPropagation()
      currentColor = element.getAttribute('fill');
      nextIndex = (colors.indexOf(currentColor) + 1) % colors.length;
      element.setAttribute('fill', colors[nextIndex]);
    });
  });
})();
<commit_msg>Make to draw by dragging<commit_after>(function(){
  colors = ['#eeeeee', '#d6e685', '#8cc665', '#44a340', '#1e6823'];
  days = document.getElementsByClassName('day');
  Array.prototype.forEach.call(days, function(element) {
    element.addEventListener('click', function(event) {
      event.stopPropagation();
    });
    element.addEventListener('mouseenter', function(event) {
      currentColor = element.getAttribute('fill');
      if (event.buttons & 1 && !event.shiftKey) {
        nextIndex = Math.min(colors.indexOf(currentColor) + 1, colors.length - 1);
        element.setAttribute('fill', colors[nextIndex]);
      } else if (event.buttons & 1 && event.shiftKey) {
        nextIndex = Math.max(colors.indexOf(currentColor) - 1, 0);
        element.setAttribute('fill', colors[nextIndex]);
      }
    });
  });
})();
<|endoftext|>


<commit_before>
var scrollJS = (function(){

	svgElements = [];

	//return function
	return function(selector, options){

		//container object
		var svg = {};
		if(document.querySelector) svg.elem   = document.querySelector(selector);
		else return false; //fuck browsers that don't have querySelector

		//general options
		svg._options = _.assign({

			visible: false,
			isTweened: false,
			duration: 0.5,
			scrollDirection: null,
			scrollBegin: 0,
			scrollEnd: 0

		}, options);

		//TODO add option for appearance type (fade, scroll, spin, etc);

		svg.intermediateTween = {};

		//TODO parse numeric values from css-specifc attribitues, i.e., 100 from 'border-radius: 100%'
		if(options.init){
			svg._initialTween = options.init;
			TweenLite.to(svg.elem, svg._options.duration, _.assign(options.init));
		}

		//TODO allow for object prop to be excluded and assumed to have remained the same
		var tweenFunc = function(){

			var tweenModel = {};

			//TODO Abstract this out so we can use it here and when we find the differences onscroll
			//Compare and find the differences between the start and end states
			_.forEach(svg._options.scrollEndState, function(value, key){

				if(options.init[key] ){

					tweenModel[key] = options.init.css && options.init.css[key] ?  value - options.init.css[key] : value - options.init[key];
				}

				else {
					tweenModel[key] = value;
				}
			});

			console.log('tweenModel', tweenModel);

			var totalTraversal = svg._options.scrollEnd - svg._options.scrollBegin;
			console.log('totalTraversal', totalTraversal);
			var tween = {};
			_.forEach(tweenModel, function(value, key){

				tween[key] = value / totalTraversal;

			});
			console.log('tween', tween);
			return tween;

		};

		svg.tween = tweenFunc();

		//add the svg element to the svgElements list
		svgElements.push(svg);
		return svg;
	};

})();

var svgElements = [];
'use strict';

//TODO this needs to be local to each SVG

//TODO fix tweenFunc so it caches the css properties for us earlier.

var tweenIt = function(){

	//console.log('tweenIt invoked');

	_.forEach(svgElements, function(svg){

		if(window.pageYOffset < 0)
			TweenMax.to(svg.elem, 0.016, svg._initialTween);

		if(window.pageYOffset > svg._options.scrollEnd){
			TweenMax.to(svg.elem, 0.016, svg._options.scrollEndState);
		}

		if(window.pageYOffset <= svg._options.scrollEnd && window.pageYOffset >= svg._options.scrollBegin && window.pageYOffset > 0){

			//console.log('Should be tweening!');

			if(svg._options.isTweened){

				//TODO make function so it can be called recursively

				_.forEach(svg._initialTween, function(value, key){

					if ( key === 'css' ) {

						_.forEach(svg._initialTween[key], function(value, key){
							svg.intermediateTween.css[key] = value + (svg.tween[key] * window.pageYOffset)
						})

					}

					else {

						svg.intermediateTween[key] = svg._initialTween[key] + (svg.tween[key] * window.pageYOffset)
						//console.log(key, value);

					}

				})

			}

			//otherwise lets initiate the tween;
			else {
				svg._options.isTweened = true;
			}

			//console.log(svg.intermediateTween);
			TweenMax.to(svg.elem, 0.016, svg.intermediateTween);

		}

	});

}

//TODO add option for media-query like conditional animations
//TODO add option to use css classes to declare start and end states

window.addEventListener('scroll', function(){

	window.requestAnimationFrame(tweenIt);

}, false);

window.addEventListener('load', tweenIt, false);

<commit_msg>v0.0.2<commit_after>
var scrollJS = (function(){

	svgElements = [];

	//return function
	return function(selector, options){

		//container object
		var svg = {};
		if(document.querySelector) svg.elem   = document.querySelector(selector);
		else return false; //fuck browsers that don't have querySelector

		//general options
		svg._options = _.assign({

			visible: false,
			isTweened: false,
			duration: 0.5,
			scrollDirection: null,
			scrollBegin: 0,
			scrollEnd: 0

		}, options);

		//TODO add option for appearance type (fade, scroll, spin, etc);

		svg.intermediateTween = {};

		if(options.init){
			svg._initialTween = options.init;
			TweenLite.to(svg.elem, svg._options.duration, _.assign(options.init));
		}

		//TODO allow for object prop to be excluded and assumed to have remained the same
		var tweenFunc = function(){

			var tweenModel = {};

			//TODO Abstract this out so we can use it here and when we find the differences onscroll
			//Compare and find the differences between the start and end states
			_.forEach(svg._options.scrollEndState, function(value, key){

				if(options.init[key] ){

					tweenModel[key] = options.init.css && options.init.css[key] ?  value - options.init.css[key] : value - options.init[key];
				}

				else {
					tweenModel[key] = value;
				}
			});

			console.log('tweenModel', tweenModel);

			var totalTraversal = svg._options.scrollEnd - svg._options.scrollBegin;
			console.log('totalTraversal', totalTraversal);
			var tween = {};
			_.forEach(tweenModel, function(value, key){

				tween[key] = value / totalTraversal;

			});
			console.log('tween', tween);
			return tween;

		};

		svg.tween = tweenFunc();

		//add the svg element to the svgElements list
		svgElements.push(svg);
		return svg;
	};

})();

var svgElements = [];
'use strict';

//TODO fix tweenFunc so it caches the css properties for us earlier.

//TODO fix bug with start positions greater than 0. Need to subtract pageoffset from start

var tweenIt = function(){

	//console.log('tweenIt invoked');

	_.forEach(svgElements, function(svg){

		if(window.pageYOffset < 0)
			TweenMax.to(svg.elem, 0.016, svg._initialTween);

		if(window.pageYOffset > svg._options.scrollEnd){
			TweenMax.to(svg.elem, 0.016, svg._options.scrollEndState);
		}

		if(window.pageYOffset <= svg._options.scrollEnd && window.pageYOffset >= svg._options.scrollBegin && window.pageYOffset > 0){

			//console.log('Should be tweening!');

			if(svg._options.isTweened){

				//TODO make function so it can be called recursively

				_.forEach(svg._initialTween, function(value, key){

					if ( key === 'css' ) {

						_.forEach(svg._initialTween[key], function(value, key){
							svg.intermediateTween.css[key] = value + (svg.tween[key] * (window.pageYOffset - svg._options.scrollBegin))
						})

					}

					else {

						svg.intermediateTween[key] = svg._initialTween[key] + (svg.tween[key] * window.pageYOffset)
						//console.log(key, value);

					}

				})

			}

			//otherwise lets initiate the tween;
			else {
				svg._options.isTweened = true;
			}

			//console.log(svg.intermediateTween);
			TweenMax.to(svg.elem, 0.016, svg.intermediateTween);

		}

	});

}

//TODO add option for media-query like conditional animations
//TODO add option to use css classes to declare start and end states

window.addEventListener('scroll', function(){

	window.requestAnimationFrame(tweenIt);

}, false);

window.addEventListener('load', tweenIt, false);

<|endoftext|>


<commit_before>/*****************************/
/*         sekshi.js         */
/*         by Sooyou         */
/*                           */
/*  Last Modified: 24/11/14  */
/*  by: Sooyou               */
/*****************************/
/* Bot created for We <3 KPOP*/
/*          on plug.dj.      */
/*                           */
/*          join at:         */
/*    play.welovekpop.club   */
/*****************************/

var mongoose = require("mongoose");
var Plugged = require("plugged");
var redis = require("redis");
var nconf = require("nconf");
var path = require("path");
var util = require("util");
var fs = require("fs");

function Sekshi(args) {
    Sekshi.super_.call(this);

    args = args || {};

    this.config = new nconf.Provider();
    this.config.file(("config" in args) ? args["config"] : "./config.json");

    this.modules = [];
    this.modulePath = ("modules" in args) ? args["modules"] : "./modules";
    this.delimiter = this.config.get("delimiter") || "!";

    this.loadModulesSync();
}

util.inherits(Sekshi, Plugged);

Sekshi.prototype.start = function() {
    try {
        this.login(this.config.get("credentials"));
    }
    catch (err) {
        console.error(err.message);
        return;
    }

    this.on(this.CONNECTED, function _onConnect() {

        this.connect(this.config.get("room"), function _onRoomJoin(err) {
            if(!err) {
                this.on(this.CHAT, this.onMessage.bind(this));
            } else {
                console.log(err);
                process.exit(1);
            }
        }.bind(this));

    }.bind(this));
};

Sekshi.prototype.stop = function() {
    this.unloadModulesSync();

    this.logout(function _logout() {
        this.off(this.CHAT);
    });
};

Sekshi.prototype.parseArguments = function(str, args) {
    args = args || [];
    str = str || "";
    var length = str.length;
    var compound = false;   //quoted?
    var sarg = false;       //started argument?
    var sidx = 0;           //start index
    var cidx = 0;           //current index

    //instead of apply the rules ([^"]+)"|(\S+) in an regular expression
    //it seemed to be a quicker approach to apply the rules manually
    //old school style.
    //rule1: every non quoted word is a single argument
    //rule2: quoted words are meant to be a single argument
    while(cidx < length) {
        if(!sarg && str[cidx] !== ' ') {

            if(str[cidx] === '"') {
                sidx = ++cidx;
                compound = true;
            } else {
                sidx = cidx;
            }

            sarg = true;
        } else if(sarg && !compound && str[cidx] === ' ') {
            args.push(str.slice(sidx, cidx));
            sarg = false;
        } else if(sarg && compound && str[cidx] === '"') {
            args.push(str.slice(sidx, cidx));
            compound = false;
            sarg = false;
        }

        cidx++;
    }

    if(sarg)
        args.push(str.slice(sidx, cidx));
};

Sekshi.prototype.onMessage = function(msg) {
    if(msg.message.charAt(0) === this.delimiter) {
        this.deleteMessage(msg.cid);

        var self = this;
        var args = [];
        var func = null;

        this.parseArguments(msg.message, args);

        func = args.shift().replace(this.delimiter, '');
        args.unshift(self.getUserByID(msg.id, true));

        for(var i = 0, l = self.modules.length; i < l; i++) {
            if(self.modules[i].enabled && typeof self.modules[i].module[func] === "function") {
                if(args[0].role >= self.modules[i].module.permissions[func])
                    self.modules[i].module[func].apply(self.modules[i].module, args);
                else
                    self.sendChat(['@', msg.username, " you don't have permission to use this command"].join(''), 5*1000);
                break;
            }
        }
    } else {
        console.log([msg.username, ": ", msg.message].join(''));
    }
};

//delimiter used for chat commands
Sekshi.prototype.setDelimiter = function(delimiter) {
    this.delimiter = delimiter;
};

Sekshi.prototype.reloadmodules = function(modulePath) {
    this.unloadModulesSync(modulePath);
    this.loadModulesSync(modulePath);
};

//get array of module files
Sekshi.prototype.getModuleFiles = function(modulePath, modules) {
    if(!fs.existsSync(modulePath)) {
        console.log(modulePath + " does not exist!");
        return [];
    }

    modules = modules || [];
    var stat = fs.statSync(modulePath);

    if(typeof stat === "undefined") {
        console.log("An error occured while fetching the path");
    } else {
        if(stat.isDirectory()) {
            var files = fs.readdirSync(modulePath);

            if(typeof files !== "undefined") {
                for(var i = 0, l = files.length; i < l; i++)
                    this.getModuleFiles(path.join(modulePath, files[i]), modules);
            }

        } else if(stat.isFile()) {
            if(modulePath.slice(modulePath.indexOf('.') + 1) === "module.js")
                modules.push(["./", modulePath].join(''));
        }
    }

    return modules;
};

//load all modules of a base path
Sekshi.prototype.loadModulesSync = function(modulePath) {
    modulePath = modulePath || this.modulePath;

    var moduleFiles = this.getModuleFiles(modulePath);
    var module = null;

    for(var i = 0, l = moduleFiles.length; i < l; i++) {
        module = require(moduleFiles[i]);

        this.modules.push({
            enabled: true,
            module: new module(this)
        });
    }
};

//unload all modules and delete the cached files
Sekshi.prototype.unloadModulesSync = function(modulePath) {
    modulePath = modulePath || this.modulePath;

    for(var i = 0, l = this.modules.length; i < l; i++) {
        if(typeof this.modules[i].module.destroy !== "undefined")
            this.modules[i].module.destroy();
    }

    this.modules = [];
    var moduleFiles = this.getModuleFiles(modulePath);

    for(var i = 0, l = moduleFiles.length; i < l; i++) {
        delete require.cache[path.resolve(moduleFiles[i])];
    }
};

module.exports = Sekshi;<commit_msg>Neater way to initialize vars in sekshi.js<commit_after>/*****************************/
/*         sekshi.js         */
/*         by Sooyou         */
/*                           */
/*  Last Modified: 24/11/14  */
/*  by: Sooyou               */
/*****************************/
/* Bot created for We <3 KPOP*/
/*          on plug.dj.      */
/*                           */
/*          join at:         */
/*    play.welovekpop.club   */
/*****************************/

var mongoose = require("mongoose");
var Plugged = require("plugged");
var redis = require("redis");
var nconf = require("nconf");
var path = require("path");
var util = require("util");
var fs = require("fs");

function Sekshi(args) {
    Sekshi.super_.call(this);

    args = args || {};

    this.config = new nconf.Provider();
    this.config.file(args["config"] || "./config.json") ;

    this.modules = [];
    this.modulePath = args["modules"] || "./modules";
    this.delimiter = this.config.get("delimiter") || "!";

    this.loadModulesSync();
}

util.inherits(Sekshi, Plugged);

Sekshi.prototype.start = function() {
    try {
        this.login(this.config.get("credentials"));
    }
    catch (err) {
        console.error(err.message);
        return;
    }

    this.on(this.CONNECTED, function _onConnect() {

        this.connect(this.config.get("room"), function _onRoomJoin(err) {
            if(!err) {
                this.on(this.CHAT, this.onMessage.bind(this));
            } else {
                console.log(err);
                process.exit(1);
            }
        }.bind(this));

    }.bind(this));
};

Sekshi.prototype.stop = function() {
    this.unloadModulesSync();

    this.logout(function _logout() {
        this.off(this.CHAT);
    });
};

Sekshi.prototype.parseArguments = function(str, args) {
    args = args || [];
    str = str || "";
    var length = str.length;
    var compound = false;   //quoted?
    var sarg = false;       //started argument?
    var sidx = 0;           //start index
    var cidx = 0;           //current index

    //instead of apply the rules ([^"]+)"|(\S+) in an regular expression
    //it seemed to be a quicker approach to apply the rules manually
    //old school style.
    //rule1: every non quoted word is a single argument
    //rule2: quoted words are meant to be a single argument
    while(cidx < length) {
        if(!sarg && str[cidx] !== ' ') {

            if(str[cidx] === '"') {
                sidx = ++cidx;
                compound = true;
            } else {
                sidx = cidx;
            }

            sarg = true;
        } else if(sarg && !compound && str[cidx] === ' ') {
            args.push(str.slice(sidx, cidx));
            sarg = false;
        } else if(sarg && compound && str[cidx] === '"') {
            args.push(str.slice(sidx, cidx));
            compound = false;
            sarg = false;
        }

        cidx++;
    }

    if(sarg)
        args.push(str.slice(sidx, cidx));
};

Sekshi.prototype.onMessage = function(msg) {
    if(msg.message.charAt(0) === this.delimiter) {
        this.deleteMessage(msg.cid);

        var self = this;
        var args = [];
        var func = null;

        this.parseArguments(msg.message, args);

        func = args.shift().replace(this.delimiter, '');
        args.unshift(self.getUserByID(msg.id, true));

        for(var i = 0, l = self.modules.length; i < l; i++) {
            if(self.modules[i].enabled && typeof self.modules[i].module[func] === "function") {
                if(args[0].role >= self.modules[i].module.permissions[func])
                    self.modules[i].module[func].apply(self.modules[i].module, args);
                else
                    self.sendChat(['@', msg.username, " you don't have permission to use this command"].join(''), 5*1000);
                break;
            }
        }
    } else {
        console.log([msg.username, ": ", msg.message].join(''));
    }
};

//delimiter used for chat commands
Sekshi.prototype.setDelimiter = function(delimiter) {
    this.delimiter = delimiter;
};

Sekshi.prototype.reloadmodules = function(modulePath) {
    this.unloadModulesSync(modulePath);
    this.loadModulesSync(modulePath);
};

//get array of module files
Sekshi.prototype.getModuleFiles = function(modulePath, modules) {
    if(!fs.existsSync(modulePath)) {
        console.log(modulePath + " does not exist!");
        return [];
    }

    modules = modules || [];
    var stat = fs.statSync(modulePath);

    if(typeof stat === "undefined") {
        console.log("An error occured while fetching the path");
    } else {
        if(stat.isDirectory()) {
            var files = fs.readdirSync(modulePath);

            if(typeof files !== "undefined") {
                for(var i = 0, l = files.length; i < l; i++)
                    this.getModuleFiles(path.join(modulePath, files[i]), modules);
            }

        } else if(stat.isFile()) {
            if(modulePath.slice(modulePath.indexOf('.') + 1) === "module.js")
                modules.push(["./", modulePath].join(''));
        }
    }

    return modules;
};

//load all modules of a base path
Sekshi.prototype.loadModulesSync = function(modulePath) {
    modulePath = modulePath || this.modulePath;

    var moduleFiles = this.getModuleFiles(modulePath);
    var module = null;

    for(var i = 0, l = moduleFiles.length; i < l; i++) {
        module = require(moduleFiles[i]);

        this.modules.push({
            enabled: true,
            module: new module(this)
        });
    }
};

//unload all modules and delete the cached files
Sekshi.prototype.unloadModulesSync = function(modulePath) {
    modulePath = modulePath || this.modulePath;

    for(var i = 0, l = this.modules.length; i < l; i++) {
        if(typeof this.modules[i].module.destroy !== "undefined")
            this.modules[i].module.destroy();
    }

    this.modules = [];
    var moduleFiles = this.getModuleFiles(modulePath);

    for(var i = 0, l = moduleFiles.length; i < l; i++) {
        delete require.cache[path.resolve(moduleFiles[i])];
    }
};

module.exports = Sekshi;<|endoftext|>