<commit_before>/* See license.txt for terms of usage */

define([
    "firebug/firebug",
    "firebug/chrome/firefox",
    "firebug/lib/locale",
    "firebug/lib/events",
    "firebug/lib/dom",
    "firebug/lib/options",
    "firebug/lib/domplate",
    "firebug/lib/object",
    "firebug/lib/css",
],
function(Firebug, Firefox, Locale, Events, Dom, Options, Domplate, Obj, Css) {

"use strict"

// ********************************************************************************************* //
// Constants

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

var domAttribs = ["nodeName", "id", "name", "offsetWidth", "offsetHeight"];
var cssAttribs = ["position"];
var compAttribs = [
    "width", "height", "zIndex", "position", "top", "right", "bottom", "left",
    "margin-top", "margin-right", "margin-bottom", "margin-left", "color",
    "backgroundColor", "fontFamily", "cssFloat", "display", "visibility"
];

// ********************************************************************************************* //
// Domplate

var {domplate, DIV, TABLE, TBODY, TR, TD, SPAN} = Domplate;

var tableTag =
    TABLE({"class": "fbQuickInfoTable", cellpadding: 0, cellspacing: 0},
        TBODY(
            TR({"class": "pin"},
                TD({"class": "", align: "right"},
                    DIV({"class": "fbQuickInfoPin $pin", onclick: "$onClickPin"})
                )
            )
        )
    );

var titleTag =
    TR(
        TD({"class": "fbQuickInfoBoxTitle"},
            "$title"
        )
    );

var rowTag =
    TR({"class": "row"},
        TD({"class": ""},
            SPAN({"class": "fbQuickInfoName"}, "$name: "),
            SPAN({"class": "fbQuickInfoValue"}, "$value")
        )
    );

// ********************************************************************************************* //
// Implementation

/**
 * Displays the most important DOM properties and computed CSS styles for the currently
 * inspected element. It can be freely positioned at the monitor via drag & drop.
 */
var QuickInfoBox = Obj.extend(Firebug.Module,
/** @lends QuickInfoBox */
{
    boxEnabled: undefined,
    dragging: false,
    storedX: null,
    storedY: null,
    prevX: null,
    prevY: null,

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Module

    initialize: function()
    {
        Firebug.Module.initialize.apply(this, arguments);

        var contentFrame = this.getContentFrame();
        Events.addEventListener(contentFrame, "load", this.onContentLoaded.bind(this), true);
        Events.addEventListener(contentFrame, "mousedown", this.quickInfoBoxHandler.bind(this), true);
        Events.addEventListener(contentFrame, "mouseover", this.quickInfoBoxHandler.bind(this), true);
        Events.addEventListener(contentFrame, "mouseout", this.quickInfoBoxHandler.bind(this), true);
    },

    shutdown: function()
    {
        Firebug.Module.shutdown.apply(this, arguments);
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    onContentLoaded: function(event)
    {
        var doc = this.getContentDoc();
        doc.body.classList.add("fbQuickInfoPanelBody");

        Css.appendStylesheet(doc, "chrome://firebug/skin/quickInfoBox.css");
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Show/hide

    show: function(element)
    {
        if (!this.boxEnabled || !element)
            return;

        this.needsToHide = false;

        var box = Firebug.chrome.$("fbQuickInfoPanel");
        if (box.state === "closed")
        {
            var content = Firefox.getElementById("content");
            this.storedX = this.storedX || content.tabContainer.boxObject.screenX + 5;
            this.storedY = this.storedY || content.tabContainer.boxObject.screenY + 35;

            // Dynamically set noautohide to avoid Mozilla bug 545265.
            if (!this.noautohideAdded)
            {
                this.noautohideAdded = true;
                box.addEventListener("popupshowing", function runOnce()
                {
                    box.removeEventListener("popupshowing", runOnce, false);
                    box.setAttribute("noautohide", true);
                }, false);
            }

            box.openPopupAtScreen(this.storedX, this.storedY, false);
        }

        var doc = this.getContentDoc();
        var parentNode = doc.body;

        // The tableTag template doesn't have its own object and so, we specify
        // all event handlers and properties through the input object.
        var input = {
            onClickPin: this.onClickPin.bind(this),
            pin: Options.get("pinQuickInfoBox") ? "pin" : ""
        }

        // Render the basic quick-box layout. It's a table where every row represents
        // a CSS property or a section title. The pin icon displayed at the top-right
        // corner also gets one row.
        var table = tableTag.replace(input, parentNode, this);
        var tbody = table.firstChild;

        var needsTitle = this.addRows(element, tbody, domAttribs);
        var needsTitle2 = this.addRows(element.style, tbody, cssAttribs);

        // Properly create section titles.
        if (needsTitle || needsTitle2)
            titleTag.insertRows({title: Locale.$STR("quickInfo")}, tbody.firstChild, this);

        titleTag.insertRows({title: Locale.$STR("computedStyle")}, tbody.lastChild, this);

        // Generate content (a row == CSS property)
        this.addRows(element, tbody, compAttribs, true);

        // Always update size of the panel according to the content size. Some elements might
        // have more styles than others and so, require more space. We always need
        // to avoid scroll-bars.
        // Keep the default width (specified in firebugOverlay.xul for fbQuickInfoPanel)
        // and change only the height.
        box.sizeTo(box.popupBoxObject.width, doc.documentElement.clientHeight);
    },

    hide: function()
    {
        // If the preference says pin == true then do not hide.
        // xxxHonza: the box should be hidden when the user switches out of the HTML panel.
        if (Options.get("pinQuickInfoBox") && Firebug.PanelActivation.isPanelEnabled("html") )
            return;

        // if mouse is over panel defer hiding to mouseout to not cause flickering
        if (this.mouseover || this.dragging)
        {
            this.needsToHide = true;
            return;
        }

        var box = Firebug.chrome.$("fbQuickInfoPanel");

        this.prevX = null;
        this.prevY = null;
        this.needsToHide = false;

        // Debugging tip: Remove this line if you want to inspect the info-box content
        // using DOM inspector add-on.
        box.hidePopup();
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    addRows: function(domBase, parentNode, attribs, computedStyle)
    {
        if (!domBase)
            return;

        // Iterate over all attributes and generate HTML content of the info box.
        var needsTitle = false;
        for (var i=0; i<attribs.length; i++)
        {
            var value;
            if (computedStyle)
            {
                var defaultView = Dom.getNonFrameBody(domBase).ownerDocument.defaultView;
                var cs = defaultView.getComputedStyle(domBase, null);

                value = cs.getPropertyValue(attribs[i]);

                if (value && /rgb\(\d+,\s\d+,\s\d+\)/.test(value))
                    value = rgbToHex(value);
            }
            else
            {
                value = domBase[attribs[i]];
            }

            if (!value)
                continue;

            // There is at least one value displayed so, the title should be generated.
            needsTitle = true;

            var input = {name: attribs[i], value: value}
            rowTag.insertRows(input, parentNode.lastChild, rowTag);
        }

        return needsTitle;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Events

    onClickPin: function(event)
    {
        var target = event.target;
        if (!Css.hasClass(target, "fbQuickInfoPin"))
            return;

        // The state of the pin needs to be updated in preferences.
        Options.togglePref("pinQuickInfoBox");

        // Update also the icon state.
        Css.toggleClass(target, "pin");
    },

    // xxxHonza: executed directly from firebugOverlay.xul. We should register
    // regular listeners so, the QuickInfoBox object doesn't have to be exposed
    // through Firebug object (see at the bottom of this file).
    handleEvent: function(event)
    {
        switch (event.type)
        {
        case "mousemove":
            if (!this.dragging || !this.box)
                return;

            var diffX, diffY,
                boxX = this.box.screenX,
                boxY = this.box.screenY,
                x = event.screenX,
                y = event.screenY;

            diffX = x - this.prevX;
            diffY = y - this.prevY;

            this.box.moveTo(boxX + diffX, boxY + diffY);

            this.prevX = x;
            this.prevY = y;
            this.storedX = boxX;
            this.storedY = boxY;
            break;

        case "mousedown":
            this.qiPanel = Firebug.chrome.$("fbQuickInfoPanel");
            this.box = this.qiPanel.boxObject;
            Events.addEventListener(this.qiPanel, "mousemove", this, true);
            Events.addEventListener(this.qiPanel, "mouseup", this, true);
            this.dragging = true;
            this.prevX = event.screenX;
            this.prevY = event.screenY;
            break;

        case "mouseup":
            Events.removeEventListener(this.qiPanel, "mousemove", this, true);
            Events.removeEventListener(this.qiPanel, "mouseup", this, true);
            this.qiPanel = this.box = null;
            this.prevX = this.prevY = null;
            this.dragging = false;
            break;

        // this is a hack to find when mouse enters and leaves panel
        // it requires that #fbQuickInfoPanel have border
        case "mouseover":
            if (this.dragging)
                return;
            this.mouseover = true;
            break;

        case "mouseout":
            if (this.dragging)
                return;
            this.mouseover = false;
            // if hiding was deferred because mouse was over panel hide it
            if (this.needsToHide && event.target.nodeName == "panel")
                this.hide();
            break;
        }
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    /**
     * Toggle the quick info box.
     */
    toggleQuickInfoBox: function()
    {
        var box = Firebug.chrome.$("fbQuickInfoPanel");

        if (box.state == "open")
            QuickInfoBox.hide();

        QuickInfoBox.boxEnabled = !QuickInfoBox.boxEnabled;

        Options.set("showQuickInfoBox", QuickInfoBox.boxEnabled);
    },

    /**
     * Pass all quick info box events to QuickInfoBox.handleEvent() for handling.
     * @param {Event} event Event to handle
     */
    quickInfoBoxHandler: function(event)
    {
        FBTrace.sysout("event executed");
        QuickInfoBox.handleEvent(event);
    },

    /**
     * Hide the quick info box.
     */
    hideQuickInfoBox: function()
    {
        var box = Firebug.chrome.$("fbQuickInfoPanel");

        if (box.state === "open")
            QuickInfoBox.hide();
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Helpers

    getContentFrame: function()
    {
        var box = Firebug.chrome.$("fbQuickInfoPanel");
        return box.getElementsByClassName("fbQuickInfoPanelContent")[0];
    },

    getContentDoc: function()
    {
        var contentFrame = this.getContentFrame();
        return contentFrame.contentWindow.document;
    },

    showPanel: function(browser, panel) {

        if( panel && panel.name !== 'html' ){
            var box = Firebug.chrome.$("fbQuickInfoPanel");
            box.hidePopup();
        }
    },
});

// ********************************************************************************************* //
// Helpers

// xxxHonza: duplication of Css.rgbToHex
function rgbToHex(value)
{
    return value.replace(/\brgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/gi, function(_, r, g, b)
    {
        return "#"+((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase();
    });
}

// ********************************************************************************************* //
// Registration

Firebug.registerModule(QuickInfoBox);

return QuickInfoBox;

// ********************************************************************************************* //
});
<commit_msg>add fix from honza comment<commit_after>/* See license.txt for terms of usage */

define([
    "firebug/firebug",
    "firebug/chrome/firefox",
    "firebug/lib/locale",
    "firebug/lib/events",
    "firebug/lib/dom",
    "firebug/lib/options",
    "firebug/lib/domplate",
    "firebug/lib/object",
    "firebug/lib/css",
],
function(Firebug, Firefox, Locale, Events, Dom, Options, Domplate, Obj, Css) {

"use strict"

// ********************************************************************************************* //
// Constants

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

var domAttribs = ["nodeName", "id", "name", "offsetWidth", "offsetHeight"];
var cssAttribs = ["position"];
var compAttribs = [
    "width", "height", "zIndex", "position", "top", "right", "bottom", "left",
    "margin-top", "margin-right", "margin-bottom", "margin-left", "color",
    "backgroundColor", "fontFamily", "cssFloat", "display", "visibility"
];

// ********************************************************************************************* //
// Domplate

var {domplate, DIV, TABLE, TBODY, TR, TD, SPAN} = Domplate;

var tableTag =
    TABLE({"class": "fbQuickInfoTable", cellpadding: 0, cellspacing: 0},
        TBODY(
            TR({"class": "pin"},
                TD({"class": "", align: "right"},
                    DIV({"class": "fbQuickInfoPin $pin", onclick: "$onClickPin"})
                )
            )
        )
    );

var titleTag =
    TR(
        TD({"class": "fbQuickInfoBoxTitle"},
            "$title"
        )
    );

var rowTag =
    TR({"class": "row"},
        TD({"class": ""},
            SPAN({"class": "fbQuickInfoName"}, "$name: "),
            SPAN({"class": "fbQuickInfoValue"}, "$value")
        )
    );

// ********************************************************************************************* //
// Implementation

/**
 * Displays the most important DOM properties and computed CSS styles for the currently
 * inspected element. It can be freely positioned at the monitor via drag & drop.
 */
var QuickInfoBox = Obj.extend(Firebug.Module,
/** @lends QuickInfoBox */
{
    boxEnabled: undefined,
    dragging: false,
    storedX: null,
    storedY: null,
    prevX: null,
    prevY: null,

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Module

    initialize: function()
    {
        Firebug.Module.initialize.apply(this, arguments);

        var contentFrame = this.getContentFrame();
        Events.addEventListener(contentFrame, "load", this.onContentLoaded.bind(this), true);
        Events.addEventListener(contentFrame, "mousedown", this.quickInfoBoxHandler.bind(this), true);
        Events.addEventListener(contentFrame, "mouseover", this.quickInfoBoxHandler.bind(this), true);
        Events.addEventListener(contentFrame, "mouseout", this.quickInfoBoxHandler.bind(this), true);
    },

    shutdown: function()
    {
        Firebug.Module.shutdown.apply(this, arguments);

        var contentFrame = this.getContentFrame();
        Events.removeEventListener(contentFrame, "mousedown", this.quickInfoBoxHandler.bind(this), true);
        Events.removeEventListener(contentFrame, "mouseover", this.quickInfoBoxHandler.bind(this), true);
        Events.removeEventListener(contentFrame, "mouseout", this.quickInfoBoxHandler.bind(this), true);
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    onContentLoaded: function(event)
    {
        var doc = this.getContentDoc();
        doc.body.classList.add("fbQuickInfoPanelBody");

        Css.appendStylesheet(doc, "chrome://firebug/skin/quickInfoBox.css");
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Show/hide

    show: function(element)
    {
        if (!this.boxEnabled || !element)
            return;

        this.needsToHide = false;

        var box = Firebug.chrome.$("fbQuickInfoPanel");
        if (box.state === "closed")
        {
            var content = Firefox.getElementById("content");
            this.storedX = this.storedX || content.tabContainer.boxObject.screenX + 5;
            this.storedY = this.storedY || content.tabContainer.boxObject.screenY + 35;

            // Dynamically set noautohide to avoid Mozilla bug 545265.
            if (!this.noautohideAdded)
            {
                this.noautohideAdded = true;
                box.addEventListener("popupshowing", function runOnce()
                {
                    box.removeEventListener("popupshowing", runOnce, false);
                    box.setAttribute("noautohide", true);
                }, false);
            }

            box.openPopupAtScreen(this.storedX, this.storedY, false);
        }

        var doc = this.getContentDoc();
        var parentNode = doc.body;

        // The tableTag template doesn't have its own object and so, we specify
        // all event handlers and properties through the input object.
        var input = {
            onClickPin: this.onClickPin.bind(this),
            pin: Options.get("pinQuickInfoBox") ? "pin" : ""
        }

        // Render the basic quick-box layout. It's a table where every row represents
        // a CSS property or a section title. The pin icon displayed at the top-right
        // corner also gets one row.
        var table = tableTag.replace(input, parentNode, this);
        var tbody = table.firstChild;

        var needsTitle = this.addRows(element, tbody, domAttribs);
        var needsTitle2 = this.addRows(element.style, tbody, cssAttribs);

        // Properly create section titles.
        if (needsTitle || needsTitle2)
            titleTag.insertRows({title: Locale.$STR("quickInfo")}, tbody.firstChild, this);

        titleTag.insertRows({title: Locale.$STR("computedStyle")}, tbody.lastChild, this);

        // Generate content (a row == CSS property)
        this.addRows(element, tbody, compAttribs, true);

        // Always update size of the panel according to the content size. Some elements might
        // have more styles than others and so, require more space. We always need
        // to avoid scroll-bars.
        // Keep the default width (specified in firebugOverlay.xul for fbQuickInfoPanel)
        // and change only the height.
        box.sizeTo(box.popupBoxObject.width, doc.documentElement.clientHeight);
    },

    hide: function()
    {
        // If the preference says pin == true then do not hide.
        // xxxHonza: the box should be hidden when the user switches out of the HTML panel.
        if (Options.get("pinQuickInfoBox"))
            return;

        // if mouse is over panel defer hiding to mouseout to not cause flickering
        if (this.mouseover || this.dragging)
        {
            this.needsToHide = true;
            return;
        }

        var box = Firebug.chrome.$("fbQuickInfoPanel");

        this.prevX = null;
        this.prevY = null;
        this.needsToHide = false;

        // Debugging tip: Remove this line if you want to inspect the info-box content
        // using DOM inspector add-on.
        box.hidePopup();
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    addRows: function(domBase, parentNode, attribs, computedStyle)
    {
        if (!domBase)
            return;

        // Iterate over all attributes and generate HTML content of the info box.
        var needsTitle = false;
        for (var i=0; i<attribs.length; i++)
        {
            var value;
            if (computedStyle)
            {
                var defaultView = Dom.getNonFrameBody(domBase).ownerDocument.defaultView;
                var cs = defaultView.getComputedStyle(domBase, null);

                value = cs.getPropertyValue(attribs[i]);

                if (value && /rgb\(\d+,\s\d+,\s\d+\)/.test(value))
                    value = rgbToHex(value);
            }
            else
            {
                value = domBase[attribs[i]];
            }

            if (!value)
                continue;

            // There is at least one value displayed so, the title should be generated.
            needsTitle = true;

            var input = {name: attribs[i], value: value}
            rowTag.insertRows(input, parentNode.lastChild, rowTag);
        }

        return needsTitle;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Events

    onClickPin: function(event)
    {
        var target = event.target;
        if (!Css.hasClass(target, "fbQuickInfoPin"))
            return;

        // The state of the pin needs to be updated in preferences.
        Options.togglePref("pinQuickInfoBox");

        // Update also the icon state.
        Css.toggleClass(target, "pin");
    },

    // xxxHonza: executed directly from firebugOverlay.xul. We should register
    // regular listeners so, the QuickInfoBox object doesn't have to be exposed
    // through Firebug object (see at the bottom of this file).
    handleEvent: function(event)
    {
        switch (event.type)
        {
        case "mousemove":
            if (!this.dragging || !this.box)
                return;

            var diffX, diffY,
                boxX = this.box.screenX,
                boxY = this.box.screenY,
                x = event.screenX,
                y = event.screenY;

            diffX = x - this.prevX;
            diffY = y - this.prevY;

            this.box.moveTo(boxX + diffX, boxY + diffY);

            this.prevX = x;
            this.prevY = y;
            this.storedX = boxX;
            this.storedY = boxY;
            break;

        case "mousedown":
            this.qiPanel = Firebug.chrome.$("fbQuickInfoPanel");
            this.box = this.qiPanel.boxObject;
            Events.addEventListener(this.qiPanel, "mousemove", this, true);
            Events.addEventListener(this.qiPanel, "mouseup", this, true);
            this.dragging = true;
            this.prevX = event.screenX;
            this.prevY = event.screenY;
            break;

        case "mouseup":
            Events.removeEventListener(this.qiPanel, "mousemove", this, true);
            Events.removeEventListener(this.qiPanel, "mouseup", this, true);
            this.qiPanel = this.box = null;
            this.prevX = this.prevY = null;
            this.dragging = false;
            break;

        // this is a hack to find when mouse enters and leaves panel
        // it requires that #fbQuickInfoPanel have border
        case "mouseover":
            if (this.dragging)
                return;
            this.mouseover = true;
            break;

        case "mouseout":
            if (this.dragging)
                return;
            this.mouseover = false;
            // if hiding was deferred because mouse was over panel hide it
            if (this.needsToHide && event.target.nodeName == "panel")
                this.hide();
            break;
        }
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    /**
     * Toggle the quick info box.
     */
    toggleQuickInfoBox: function()
    {
        var box = Firebug.chrome.$("fbQuickInfoPanel");

        if (box.state == "open")
            QuickInfoBox.hide();

        QuickInfoBox.boxEnabled = !QuickInfoBox.boxEnabled;

        Options.set("showQuickInfoBox", QuickInfoBox.boxEnabled);
    },

    /**
     * Pass all quick info box events to QuickInfoBox.handleEvent() for handling.
     * @param {Event} event Event to handle
     */
    quickInfoBoxHandler: function(event)
    {
        QuickInfoBox.handleEvent(event);
    },

    /**
     * Hide the quick info box.
     */
    hideQuickInfoBox: function()
    {
        var box = Firebug.chrome.$("fbQuickInfoPanel");

        if (box.state === "open")
            QuickInfoBox.hide();
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Helpers

    getContentFrame: function()
    {
        var box = Firebug.chrome.$("fbQuickInfoPanel");
        return box.getElementsByClassName("fbQuickInfoPanelContent")[0];
    },

    getContentDoc: function()
    {
        var contentFrame = this.getContentFrame();
        return contentFrame.contentWindow.document;
    },

    showPanel: function(browser, panel)
    {
        if( panel && panel.name !== 'html' )
        {
            var box = Firebug.chrome.$("fbQuickInfoPanel");
            box.hidePopup();
        }
    }
});

// ********************************************************************************************* //
// Helpers

// xxxHonza: duplication of Css.rgbToHex
function rgbToHex(value)
{
    return value.replace(/\brgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/gi, function(_, r, g, b)
    {
        return "#"+((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase();
    });
}

// ********************************************************************************************* //
// Registration

Firebug.registerModule(QuickInfoBox);

return QuickInfoBox;

// ********************************************************************************************* //
});
<|endoftext|>


<commit_before>const { Chromeless } = require('chromeless')
const { expect } = require('chai')

// make sure you do npm i chai
// to run this example just run
// mocha path/to/this/file

describe('When searching on google', function () {
  it('shows results', async function () {
    this.timeout(10000); //we need to increase the timeout or else mocha will exit with an error
    const chromeless = new Chromeless()

    await chromeless.goto('https://google.com')
      .wait('input[name="q"]')
      .type('chromeless github', 'input[name="q"]')
      .press(13) // press enter
      .wait('#resultStats')

    const result = await chromeless.exists('a[href*="graphcool/chromeless"]')

    expect(result).to.be.true
    await chromeless.end()
  })
})

describe('When clicking on the image of the demo playground', function () {
  it('should redirect to the demo', async function () {
    this.timeout(10000); //we need to increase the timeout or else mocha will exit with an error
    const chromeless = new Chromeless()
    await chromeless.goto('https://github.com/graphcool/chromeless')
      .wait('a[href="https://chromeless.netlify.com/"]')
      .click('a[href="https://chromeless.netlify.com/"]')
      .wait('#root')

    const url = awaitchromeless.evaluate(url => window.location.href)

    expect(url).to.match(/^https\:\/\/chromeless\.netlify\.com/)
    await chromeless.end()
  })
})
<commit_msg>Fixes a typo<commit_after>const { Chromeless } = require('chromeless')
const { expect } = require('chai')

// make sure you do npm i chai
// to run this example just run
// mocha path/to/this/file

describe('When searching on google', function () {
  it('shows results', async function () {
    this.timeout(10000); //we need to increase the timeout or else mocha will exit with an error
    const chromeless = new Chromeless()

    await chromeless.goto('https://google.com')
      .wait('input[name="q"]')
      .type('chromeless github', 'input[name="q"]')
      .press(13) // press enter
      .wait('#resultStats')

    const result = await chromeless.exists('a[href*="graphcool/chromeless"]')

    expect(result).to.be.true
    await chromeless.end()
  })
})

describe('When clicking on the image of the demo playground', function () {
  it('should redirect to the demo', async function () {
    this.timeout(10000); //we need to increase the timeout or else mocha will exit with an error
    const chromeless = new Chromeless()
    await chromeless.goto('https://github.com/graphcool/chromeless')
      .wait('a[href="https://chromeless.netlify.com/"]')
      .click('a[href="https://chromeless.netlify.com/"]')
      .wait('#root')

    const url = await chromeless.evaluate(url => window.location.href)

    expect(url).to.match(/^https\:\/\/chromeless\.netlify\.com/)
    await chromeless.end()
  })
})
<|endoftext|>


<commit_before>// Submote by Zarlach

console.log('Submote is running');

// Global vars
var subMotes    = {};           // subMotes[emote] = source
var subEmotes   = {};           // subEmotes[emote] = id
var bttvEmotes  = {};           // bttvEmotes[emote] = source

// Check if emotes are already stored locally
if (localStorage.getItem("subEmotes") === null)
    loadSubEmotes();
else
{
    subEmotes           = JSON.parse(localStorage.getItem("subEmotes"));
    var genDate         = Date.parse(subEmotes['generated_at']);
    var currentDate     = new Date();
    var expirationDate  = genDate+3600*1000/2;  //30 minutes

    //Check if expired
    if(currentDate.getTime() >= expirationDate)
        loadSubEmotes();
}

if (localStorage.getItem("bttvEmotes") === null){
    loadBetterTTVemotes();
    loadCustomEmotes();
}
else{
    bttvEmotes = JSON.parse(localStorage.getItem("bttvEmotes"));

        bttvEmotes      = JSON.parse(localStorage.getItem("bttvEmotes"));
    var genDate         = bttvEmotes['generated_at'];
    var currentDate     = new Date();
    var expirationDate  = genDate+3600*1000/2;  //30 minutes

    //Check if expired
    if(currentDate.getTime() >= expirationDate)
        loadBetterTTVemotes();

    loadCustomEmotes();
}

/**
 * Observe for new messages
 */
$(document).ready(function(evt){

    // An attempt to find a new and better way to detect new messages
    var target = document;

    // Observer intsance
    var observer = new MutationObserver(function(mutations){
        mutations.forEach(function(mutation){
            if(mutation.addedNodes){

                // Check for queued messages
                if(mutation.addedNodes.length > 1){
                    for(var i = 0; i < mutation.addedNodes.length; i++){

                        // Make it readable to newMessage()
                        var message = [mutation.addedNodes[i]];

                        newMessage(message);
                    }
                }else{
                    newMessage(mutation.addedNodes);
                }
            }
        });
    });

    // Mutation config
    var config = {
        attributes: true,
        childList: true,
        subtree: true,
        characterData: true,
     };

    // Start the observer
    //while(!target || target === null)
    //    target = document.querySelector('.chat-lines');

    observer.observe(target, config);
});

/**
 * Handle new messages
 */
function newMessage(message){
    var line = false;
    var bttv = true;        // Assume true

    // Check if using vanilla or FrankerFaceZ
    if($(message[1]).find('.chat-line').length){
        line    = $(message[1]).find('li.chat-line');
        bttv    = false;
    }

    // Check if using BetterTTV
    if(bttv && !line && $(message[0]).length){
        line    = $(message[0]);
    }

    // No message line detection? Abort
    if(!line) return false;

    // Fetch message metadata
    // var room    = line.attr('data-room');
    var sender  = line.attr('data-sender');
    var sender  = line.find('.from').html();
    var badges  = line.find('.badges');
    var text    = line.find('.message');

    // No message text? Abort
    if(!text.length) return false;

    // If room and sender is undefined, the user is using vanilla Twitch
    if(sender === undefined && line.find('.from').length){
        sender = line.find('.from').html().toLowerCase();
    }

    // Parse message text
    parseMessage(text, subEmotes);
    parseMessage(text, bttvEmotes);

    // Append submote badges
    if(sender.toLowerCase() === 'zarlach' && !badges.find('.submote-dev').length){
        $(badges).append('

');
        $(badges).find('.submote-dev').css({
            'width': '18px',
            'height': '18px',
            'background-image': 'url(https://cdn.rawgit.com/Zarlach/Submote/master/images/dev-badge.png)',
            'background-repat': 'no-repeat',
            'background-size': '18px 18px'
        });
    }

    return true;
}

/**
 * Load subscriber emotes from web
 */
function loadSubEmotes()
{
    $.getJSON('https:twitchemotes.com/api_cache/v2/subscriber.json', function(data)
    {
        $.each(data['channels'], function(channel, properties)
        {
            $.each(properties['emotes'], function(list, emote){
                subEmotes[emote['code']] = emote['image_id'];
            });
        });

        $.each(data['meta'], function(key, val){
            subEmotes[key] = val;
        });
    })
    .done(function()
    {
        subEmotes['provider'] = 'twitch';
        localStorage.setItem("subEmotes", JSON.stringify(subEmotes));
    })
    .fail(function()
    {
        //sendError('Submote could not load sub emotes');
        console.log('Could not load sub emotes');
    });
}

/**
 * Load BetterTTV emotes from web
 */
function loadBetterTTVemotes()
{
    $.getJSON('https://cdn.betterttv.net/emotes/emotes.json', function(data){
        $.each(data, function(key, value){
            bttvEmotes[value['regex'].replace('\\', '')] = value['url'].replace('//', 'https://');
        });
    })
    .done(function(){
        bttvEmotes['provider'] = 'betterttv';
        bttvEmotes['generated_at'] = new Date();

        localStorage.setItem("bttvEmotes", JSON.stringify(bttvEmotes));
    })
    .fail(function(){
        console.log('Submote could not load BetterTTV emotes');
    });
}

/**
 * Load Custom Emotes
 */
function loadCustomEmotes(){
    // To-do: generalize bttvEmotes into customEmotes
    bttvEmotes['PepePls']       = 'https://cdn.betterttv.net/emote/55898e122612142e6aaa935b/1x';
    bttvEmotes['SnoopPls']      = 'https://cdn.betterttv.net/emote/55a05e85cc07004a41f8b1d7/1x';
    bttvEmotes[':ditto:']       = 'https://cdn.betterttv.net/emote/554da1a289d53f2d12781907/1x';
    bttvEmotes['FeelsOhWait']   = 'http://cdn.betterttv.net/emote/55ab96ce9406e5482db53424/1x';
    bttvEmotes['CarePackageHype'] = 'https://cdn.betterttv.net/emote/55af0cf9be915dff65e4be1a/1x';
    bttvEmotes['blueHula']      = 'https://cdn.betterttv.net/emote/55adf775917af02166048acd/1x';
    bttvEmotes['ShakeItOff']    = 'https://cdn.betterttv.net/emote/55a9875be80089ed0bf297a0/1x';

    // FrankerFaceZ
    bttvEmotes['CatBag']        = 'http://cdn.frankerfacez.com/emoticon/25927/1';
    bttvEmotes['CoolCatBag']    = 'http://cdn.frankerfacez.com/emoticon/41091/1';
    bttvEmotes['LilZ']          = 'http://cdn.frankerfacez.com/emoticon/28136/1';
    bttvEmotes['ZreknarF']      = 'http://cdn.frankerfacez.com/emoticon/1/1';
    bttvEmotes['BeanieHipster'] = 'http://cdn.frankerfacez.com/emoticon/3/1';
    bttvEmotes['ManChicken']    = 'http://cdn.frankerfacez.com/emoticon/4/1';
    bttvEmotes['YellowFever']   = 'http://cdn.frankerfacez.com/emoticon/5/1';
    bttvEmotes['YooHoo']        = 'http://cdn.frankerfacez.com/emoticon/6/1';

    // Twitch Turbo
    bttvEmotes['duDudu']        = 'https://static-cdn.jtvnw.net/emoticons/v1/23139/1.0';
    bttvEmotes['KappaHD']       = 'https://static-cdn.jtvnw.net/emoticons/v1/3286/1.0';
    bttvEmotes['MiniK']         = 'https://static-cdn.jtvnw.net/emoticons/v1/3287/1.0';
    bttvEmotes['riPepperonis']  = 'https://static-cdn.jtvnw.net/emoticons/v1/23141/1.0';

    localStorage.setItem("bttvEmotes", JSON.stringify(bttvEmotes));
}

/**
 * Scan message and replace words with emotes!
 *
 * @param  {element} ele        Message element
 * @param  {object} list        List of emotes
 * @param  {string} provider    Emote provider
 */
function parseMessage(ele, list)
{
    var msg         = ele.html();
    var split       = msg.replace(/(<([^>]+)>)/ig,'').trim().split(' ');
        split       = $.grep(split,function(n){ return(n) });
    var words       = [];
    var provider    = list['provider'];
    var regex;
    var word;

    //Remove duplicates
    $.each(split, function(i, el){
        if($.inArray(el, words) === -1) words.push(el);
    });

    //
    for (var i = 0; i <= words.length; i++) {
        word = words[i];

        if(validateFilter(word, list, provider))
        {
            regex = new RegExp('\\b'+word+'\\b(?=[^"]*(?:"[^"]*"[^"]*)*$)', 'g');
            msg = msg.replace(regex, generateEmoteImage(word, list[word], provider));
        }
    }

    ele.html(msg.trim());
}

/**
 * Generate emote image HTML tag.
 *
 * @param  {int} id Emote ID
 * @return {string}    HTML img tag
 */
function generateEmoteImage(emote, source, provider)
{
    switch(provider){
        case 'twitch':
            return '';
            break;
        case 'betterttv':
            return '';
            break;
    }
}

/**
 * Run selected word through a filter
 *
 * @param  {string}     word     Selected word
 * @param  {object}     list     Emote list
 * @param  {string}     provider Emote provider
 * @return {boolean}    Returns whether or not the word passed the filter
 */
function validateFilter(word, list, provider)
{
    var regex = new RegExp('^(?=.*[a-z])(?=.*[A-Z]).+$');

    //General filter
    if(list[word] === undefined
        || !regex.test(word)
        || word === 'provider'
        || word === 'generated_at'
        || word.length === 0)
        return false;

    //Provider specific filter
    switch(provider){
        case 'twitch':
            if (word[0] !== word[0].toLowerCase()
                || word === 'double'
                || word === 'triple'
                || word === 'quadra'
                || word === 'penta'
                || word === 'kill'
                || word === 'snap')
                return false;

            break;
        case 'betterttv':

            break;
    }

    return true;
}

/**
 * In case something fails, let the user know!
 *
 * @param  {string} message Error description
 */
function sendError(message)
{
    $('body').append('

'+message+'
Please refresh

');
}<commit_msg>Edited, removed and added emotes<commit_after>// Submote by Zarlach

console.log('Submote is running');

// Global vars
var subMotes    = {};           // subMotes[emote] = source
var subEmotes   = {};           // subEmotes[emote] = id
var bttvEmotes  = {};           // bttvEmotes[emote] = source

// Check if emotes are already stored locally
if (localStorage.getItem("subEmotes") === null)
    loadSubEmotes();
else
{
    subEmotes           = JSON.parse(localStorage.getItem("subEmotes"));
    var genDate         = Date.parse(subEmotes['generated_at']);
    var currentDate     = new Date();
    var expirationDate  = genDate+3600*1000/2;  //30 minutes

    //Check if expired
    if(currentDate.getTime() >= expirationDate)
        loadSubEmotes();
}

if (localStorage.getItem("bttvEmotes") === null){
    loadBetterTTVemotes();
    loadCustomEmotes();
}
else{
    bttvEmotes = JSON.parse(localStorage.getItem("bttvEmotes"));

        bttvEmotes      = JSON.parse(localStorage.getItem("bttvEmotes"));
    var genDate         = bttvEmotes['generated_at'];
    var currentDate     = new Date();
    var expirationDate  = genDate+3600*1000/2;  //30 minutes

    //Check if expired
    if(currentDate.getTime() >= expirationDate)
        loadBetterTTVemotes();

    loadCustomEmotes();
}

/**
 * Observe for new messages
 */
$(document).ready(function(evt){

    // An attempt to find a new and better way to detect new messages
    var target = document;

    // Observer intsance
    var observer = new MutationObserver(function(mutations){
        mutations.forEach(function(mutation){
            if(mutation.addedNodes){

                // Check for queued messages
                if(mutation.addedNodes.length > 1){
                    for(var i = 0; i < mutation.addedNodes.length; i++){

                        // Make it readable to newMessage()
                        var message = [mutation.addedNodes[i]];

                        newMessage(message);
                    }
                }else{
                    newMessage(mutation.addedNodes);
                }
            }
        });
    });

    // Mutation config
    var config = {
        attributes: true,
        childList: true,
        subtree: true,
        characterData: true,
     };

    // Start the observer
    //while(!target || target === null)
    //    target = document.querySelector('.chat-lines');

    observer.observe(target, config);
});

/**
 * Handle new messages
 */
function newMessage(message){
    var line = false;
    var bttv = true;        // Assume true

    // Check if using vanilla or FrankerFaceZ
    if($(message[1]).find('.chat-line').length){
        line    = $(message[1]).find('li.chat-line');
        bttv    = false;
    }

    // Check if using BetterTTV
    if(bttv && !line && $(message[0]).length){
        line    = $(message[0]);
    }

    // No message line detection? Abort
    if(!line) return false;

    // Fetch message metadata
    // var room    = line.attr('data-room');
    var sender  = line.attr('data-sender');
    var sender  = line.find('.from').html();
    var badges  = line.find('.badges');
    var text    = line.find('.message');

    // No message text? Abort
    if(!text.length) return false;

    // If room and sender is undefined, the user is using vanilla Twitch
    if(sender === undefined && line.find('.from').length){
        sender = line.find('.from').html().toLowerCase();
    }

    // Parse message text
    parseMessage(text, subEmotes);
    parseMessage(text, bttvEmotes);

    // Append submote badges
    if(sender.toLowerCase() === 'zarlach' && !badges.find('.submote-dev').length){
        $(badges).append('

');
        $(badges).find('.submote-dev').css({
            'width': '18px',
            'height': '18px',
            'background-image': 'url(https://cdn.rawgit.com/Zarlach/Submote/master/images/dev-badge.png)',
            'background-repat': 'no-repeat',
            'background-size': '18px 18px'
        });
    }

    return true;
}

/**
 * Load subscriber emotes from web
 */
function loadSubEmotes()
{
    $.getJSON('https:twitchemotes.com/api_cache/v2/subscriber.json', function(data)
    {
        $.each(data['channels'], function(channel, properties)
        {
            $.each(properties['emotes'], function(list, emote){
                subEmotes[emote['code']] = emote['image_id'];
            });
        });

        $.each(data['meta'], function(key, val){
            subEmotes[key] = val;
        });
    })
    .done(function()
    {
        subEmotes['provider'] = 'twitch';
        localStorage.setItem("subEmotes", JSON.stringify(subEmotes));
    })
    .fail(function()
    {
        //sendError('Submote could not load sub emotes');
        console.log('Could not load sub emotes');
    });
}

/**
 * Load BetterTTV emotes from web
 */
function loadBetterTTVemotes()
{
    $.getJSON('https://cdn.betterttv.net/emotes/emotes.json', function(data){
        $.each(data, function(key, value){
            bttvEmotes[value['regex'].replace('\\', '')] = value['url'].replace('//', 'https://');
        });
    })
    .done(function(){
        bttvEmotes['provider'] = 'betterttv';
        bttvEmotes['generated_at'] = new Date();

        localStorage.setItem("bttvEmotes", JSON.stringify(bttvEmotes));
    })
    .fail(function(){
        console.log('Submote could not load BetterTTV emotes');
    });
}

/**
 * Load Custom Emotes
 */
function loadCustomEmotes(){
    // To-do: generalize bttvEmotes into customEmotes
    bttvEmotes['Submote']       = 'https://cdn.rawgit.com/Zarlach/Submote/master/images/icon16.png';

    bttvEmotes['PepePls']       = 'https://cdn.betterttv.net/emote/55898e122612142e6aaa935b/1x';
    bttvEmotes['SnoopPls']      = 'https://cdn.betterttv.net/emote/55a05e85cc07004a41f8b1d7/1x';
    bttvEmotes['Ditto']         = 'https://cdn.betterttv.net/emote/554da1a289d53f2d12781907/1x';
    bttvEmotes['FeelsOhWait']   = 'http://cdn.betterttv.net/emote/55ab96ce9406e5482db53424/1x';
    bttvEmotes['blueHula']      = 'https://cdn.betterttv.net/emote/55adf775917af02166048acd/1x';
    bttvEmotes['ShakeItOff']    = 'https://cdn.betterttv.net/emote/55a9875be80089ed0bf297a0/1x';

    // FrankerFaceZ
    bttvEmotes['CatBag']        = 'http://cdn.frankerfacez.com/emoticon/25927/1';
    bttvEmotes['CoolCatBag']    = 'http://cdn.frankerfacez.com/emoticon/41091/1';
    bttvEmotes['LilZ']          = 'http://cdn.frankerfacez.com/emoticon/28136/1';
    bttvEmotes['ZreknarF']      = 'http://cdn.frankerfacez.com/emoticon/1/1';
    bttvEmotes['BeanieHipster'] = 'http://cdn.frankerfacez.com/emoticon/3/1';
    bttvEmotes['ManChicken']    = 'http://cdn.frankerfacez.com/emoticon/4/1';
    bttvEmotes['YellowFever']   = 'http://cdn.frankerfacez.com/emoticon/5/1';
    bttvEmotes['YooHoo']        = 'http://cdn.frankerfacez.com/emoticon/6/1';

    // Twitch Turbo
    bttvEmotes['duDudu']        = 'https://static-cdn.jtvnw.net/emoticons/v1/23139/1.0';
    bttvEmotes['KappaHD']       = 'https://static-cdn.jtvnw.net/emoticons/v1/3286/1.0';
    bttvEmotes['MiniK']         = 'https://static-cdn.jtvnw.net/emoticons/v1/3287/1.0';
    bttvEmotes['riPepperonis']  = 'https://static-cdn.jtvnw.net/emoticons/v1/23141/1.0';

    localStorage.setItem("bttvEmotes", JSON.stringify(bttvEmotes));
}

/**
 * Scan message and replace words with emotes!
 *
 * @param  {element} ele        Message element
 * @param  {object} list        List of emotes
 * @param  {string} provider    Emote provider
 */
function parseMessage(ele, list)
{
    var msg         = ele.html();
    var split       = msg.replace(/(<([^>]+)>)/ig,'').trim().split(' ');
        split       = $.grep(split,function(n){ return(n) });
    var words       = [];
    var provider    = list['provider'];
    var regex;
    var word;

    //Remove duplicates
    $.each(split, function(i, el){
        if($.inArray(el, words) === -1) words.push(el);
    });

    //
    for (var i = 0; i <= words.length; i++) {
        word = words[i];

        if(validateFilter(word, list, provider))
        {
            regex = new RegExp('\\b'+word+'\\b(?=[^"]*(?:"[^"]*"[^"]*)*$)', 'g');
            msg = msg.replace(regex, generateEmoteImage(word, list[word], provider));
        }
    }

    ele.html(msg.trim());
}

/**
 * Generate emote image HTML tag.
 *
 * @param  {int} id Emote ID
 * @return {string}    HTML img tag
 */
function generateEmoteImage(emote, source, provider)
{
    switch(provider){
        case 'twitch':
            return '';
            break;
        case 'betterttv':
            return '';
            break;
    }
}

/**
 * Run selected word through a filter
 *
 * @param  {string}     word     Selected word
 * @param  {object}     list     Emote list
 * @param  {string}     provider Emote provider
 * @return {boolean}    Returns whether or not the word passed the filter
 */
function validateFilter(word, list, provider)
{
    var regex = new RegExp('^(?=.*[a-z])(?=.*[A-Z]).+$');

    //General filter
    if(list[word] === undefined
        || !regex.test(word)
        || word === 'provider'
        || word === 'generated_at'
        || word.length === 0)
        return false;

    //Provider specific filter
    switch(provider){
        case 'twitch':
            if (word[0] !== word[0].toLowerCase()
                || word === 'double'
                || word === 'triple'
                || word === 'quadra'
                || word === 'penta'
                || word === 'kill'
                || word === 'snap')
                return false;

            break;
        case 'betterttv':

            break;
    }

    return true;
}

/**
 * In case something fails, let the user know!
 *
 * @param  {string} message Error description
 */
function sendError(message)
{
    $('body').append('

'+message+'
Please refresh

');
}<|endoftext|>


<commit_before>import types from '../constants'

const { ADD_SHIPPING_INFO } = types

const initialState = {
    initialized: false
}

const orderReducer = (state = initialState, action) => {
    switch (action.type) {
    case ADD_SHIPPING_INFO:
        return {
            ...state,
            initialized: true,
            shippingInfo: {
                consumer: action.consumer,
                details: action.details
            }
        }
    default:
        return state
    }
}
export default orderReducer
<commit_msg>changing the shape of the state<commit_after>import types from '../constants'

const { ADD_SHIPPING_INFO } = types

const initialState = {
    initialized: false
}

const orderReducer = (state = initialState, action) => {
    switch (action.type) {
    case ADD_SHIPPING_INFO:
        return {
            ...state,
            initialized: true,
            shippingInfo: {
                consumer: action.consumer,
                shipAccount: action.details.shipAccount,
                shipAccountNum: action.details.shipAccountNum,
                comments: action.details.comments
            }
        }
    default:
        return state
    }
}
export default orderReducer
<|endoftext|>


<commit_before>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2015, Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 */
(function () {

    /**
     * GUI Object

     * A very basic object to manage GUI elements

     * The object simply register on the "pointerdown"

     * or "touchstart" event and call the onClick function"
     * @class
     * @extends me.Sprite
     * @memberOf me
     * @constructor
     * @param {Number} x the x coordinate of the GUI Object
     * @param {Number} y the y coordinate of the GUI Object
     * @param {Object} settings See {@link me.Entity}
     * @example
     *
     * // create a basic GUI Object
     * var myButton = me.GUI_Object.extend(
     * {
     *    init:function (x, y)
     *    {
     *       var settings = {}
     *       settings.image = "button";
     *       settings.framewidth = 100;
     *       settings.frameheight = 50;
     *       // super constructor
     *       this._super(me.GUI_Object, "init", [x, y, settings]);
     *       // define the object z order
     *       this.z = 4;
     *    },
     *
     *    // output something in the console
     *    // when the object is clicked
     *    onClick:function (event)
     *    {
     *       console.log("clicked!");
     *       // don't propagate the event
     *       return false;
     *    }
     * });
     *
     * // add the object at pos (10,10)
     * me.game.world.addChild(new myButton(10,10));
     *
     */
    me.GUI_Object = me.Sprite.extend({
    /** @scope me.GUI_Object.prototype */

        /**
         * @ignore
         */
        init : function (x, y, settings) {
            /**
             * object can be clicked or not
             * @public
             * @type boolean
             * @default true
             * @name me.GUI_Object#isClickable
             */
            this.isClickable = true;

            /**
             * Tap and hold threshold timeout in ms
             * @type {number}
             * @default 250
             * @name me.GUI_Object#holdThreshold
             */
            this.holdThreshold = 250;

            /**
             * object can be tap and hold
             * @public
             * @type boolean
             * @default false
             * @name me.GUI_Object#isHoldable
             */
            this.isHoldable = false;

            // object has been updated (clicked,etc..)
            this.holdTimeout = null;
            this.updated = false;
            this.released = true;

            // call the parent constructor
            this._super(me.Sprite, "init", [ x, y, settings ]);

            // GUI items use screen coordinates
            this.floating = true;

            // register on mouse event
            me.input.registerPointerEvent("pointerdown", this, this.clicked.bind(this));
            me.input.registerPointerEvent("pointerup", this, this.release.bind(this));
            me.input.registerPointerEvent("pointerenter", this, this.clicked.bind(this));
            me.input.registerPointerEvent("pointerleave", this, this.release.bind(this));
            me.input.registerPointerEvent("pointercancel", this, this.release.bind(this));
        },

        /**
         * return true if the object has been clicked
         * @ignore
         */
        update : function () {
            if (this.updated) {
                // clear the flag
                if (!this.released) {
                    this.updated = false;
                }
                return true;
            }
            return false;
        },

        /**
         * function callback for the pointerdown event
         * @ignore
         */
        clicked : function (event) {
            // Check if left mouse button is pressed OR if device has touch
            if ((event.which === 1 || me.device.touch) && this.isClickable) {
                this.updated = true;
                if (this.isHoldable) {
                    if (this.holdTimeout !== null) {
                        me.timer.clearTimeout(this.holdTimeout);
                    }
                    this.holdTimeout = me.timer.setTimeout(this.hold.bind(this), this.holdThreshold, false);
                    this.released = false;
                }
                return this.onClick(event);
            }
        },

        /**
         * function called when the object is pressed

         * to be extended

         * return false if we need to stop propagating the event
         * @name onClick
         * @memberOf me.GUI_Object
         * @public
         * @function
         * @param {Event} event the event object
         */
        onClick : function () {
            return false;
        },

        /**
         * function callback for the pointerup event
         * @ignore
         */
        release : function (event) {
            this.released = true;
            me.timer.clearTimeout(this.holdTimeout);
            return this.onRelease(event);
        },

        /**
         * function called when the object is pressed and released

         * to be extended

         * return false if we need to stop propagating the event
         * @name onClick
         * @memberOf me.GUI_Object
         * @public
         * @function
         * @param {Event} event the event object
         */
        onRelease : function () {
            return false;
        },

        /**
         * function callback for the tap and hold timer event
         * @ignore
         */
        hold : function () {
            me.timer.clearTimeout(this.holdTimeout);
            if (!this.released) {
                this.onHold();
            }
        },

        /**
         * function called when the object is pressed and held

         * to be extended

         * @name onHold
         * @memberOf me.GUI_Object
         * @public
         * @function
         */
        onHold : function () {},

        /**
         * OnDestroy notification function

         * Called by engine before deleting the object

         * be sure to call the parent function if overwritten
         * @name onDestroyEvent
         * @memberOf me.GUI_Object
         * @public
         * @function
         */
        onDestroyEvent : function () {
            me.input.releasePointerEvent("pointerdown", this);
            me.input.releasePointerEvent("pointerup", this);
            me.input.releasePointerEvent("pointerenter", this);
            me.input.releasePointerEvent("pointerleave", this);
            me.input.releasePointerEvent("pointercancel", this);
            me.timer.clearTimeout(this.holdTimeout);
        }
    });
})();
<commit_msg>:memo: Fix name for `me.GUI_Object.onRelease`<commit_after>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2015, Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 */
(function () {

    /**
     * GUI Object

     * A very basic object to manage GUI elements

     * The object simply register on the "pointerdown"

     * or "touchstart" event and call the onClick function"
     * @class
     * @extends me.Sprite
     * @memberOf me
     * @constructor
     * @param {Number} x the x coordinate of the GUI Object
     * @param {Number} y the y coordinate of the GUI Object
     * @param {Object} settings See {@link me.Entity}
     * @example
     *
     * // create a basic GUI Object
     * var myButton = me.GUI_Object.extend(
     * {
     *    init:function (x, y)
     *    {
     *       var settings = {}
     *       settings.image = "button";
     *       settings.framewidth = 100;
     *       settings.frameheight = 50;
     *       // super constructor
     *       this._super(me.GUI_Object, "init", [x, y, settings]);
     *       // define the object z order
     *       this.z = 4;
     *    },
     *
     *    // output something in the console
     *    // when the object is clicked
     *    onClick:function (event)
     *    {
     *       console.log("clicked!");
     *       // don't propagate the event
     *       return false;
     *    }
     * });
     *
     * // add the object at pos (10,10)
     * me.game.world.addChild(new myButton(10,10));
     *
     */
    me.GUI_Object = me.Sprite.extend({
    /** @scope me.GUI_Object.prototype */

        /**
         * @ignore
         */
        init : function (x, y, settings) {
            /**
             * object can be clicked or not
             * @public
             * @type boolean
             * @default true
             * @name me.GUI_Object#isClickable
             */
            this.isClickable = true;

            /**
             * Tap and hold threshold timeout in ms
             * @type {number}
             * @default 250
             * @name me.GUI_Object#holdThreshold
             */
            this.holdThreshold = 250;

            /**
             * object can be tap and hold
             * @public
             * @type boolean
             * @default false
             * @name me.GUI_Object#isHoldable
             */
            this.isHoldable = false;

            // object has been updated (clicked,etc..)
            this.holdTimeout = null;
            this.updated = false;
            this.released = true;

            // call the parent constructor
            this._super(me.Sprite, "init", [ x, y, settings ]);

            // GUI items use screen coordinates
            this.floating = true;

            // register on mouse event
            me.input.registerPointerEvent("pointerdown", this, this.clicked.bind(this));
            me.input.registerPointerEvent("pointerup", this, this.release.bind(this));
            me.input.registerPointerEvent("pointerenter", this, this.clicked.bind(this));
            me.input.registerPointerEvent("pointerleave", this, this.release.bind(this));
            me.input.registerPointerEvent("pointercancel", this, this.release.bind(this));
        },

        /**
         * return true if the object has been clicked
         * @ignore
         */
        update : function () {
            if (this.updated) {
                // clear the flag
                if (!this.released) {
                    this.updated = false;
                }
                return true;
            }
            return false;
        },

        /**
         * function callback for the pointerdown event
         * @ignore
         */
        clicked : function (event) {
            // Check if left mouse button is pressed OR if device has touch
            if ((event.which === 1 || me.device.touch) && this.isClickable) {
                this.updated = true;
                if (this.isHoldable) {
                    if (this.holdTimeout !== null) {
                        me.timer.clearTimeout(this.holdTimeout);
                    }
                    this.holdTimeout = me.timer.setTimeout(this.hold.bind(this), this.holdThreshold, false);
                    this.released = false;
                }
                return this.onClick(event);
            }
        },

        /**
         * function called when the object is pressed

         * to be extended

         * return false if we need to stop propagating the event
         * @name onClick
         * @memberOf me.GUI_Object
         * @public
         * @function
         * @param {Event} event the event object
         */
        onClick : function () {
            return false;
        },

        /**
         * function callback for the pointerup event
         * @ignore
         */
        release : function (event) {
            this.released = true;
            me.timer.clearTimeout(this.holdTimeout);
            return this.onRelease(event);
        },

        /**
         * function called when the object is pressed and released

         * to be extended

         * return false if we need to stop propagating the event
         * @name onRelease
         * @memberOf me.GUI_Object
         * @public
         * @function
         * @param {Event} event the event object
         */
        onRelease : function () {
            return false;
        },

        /**
         * function callback for the tap and hold timer event
         * @ignore
         */
        hold : function () {
            me.timer.clearTimeout(this.holdTimeout);
            if (!this.released) {
                this.onHold();
            }
        },

        /**
         * function called when the object is pressed and held

         * to be extended

         * @name onHold
         * @memberOf me.GUI_Object
         * @public
         * @function
         */
        onHold : function () {},

        /**
         * OnDestroy notification function

         * Called by engine before deleting the object

         * be sure to call the parent function if overwritten
         * @name onDestroyEvent
         * @memberOf me.GUI_Object
         * @public
         * @function
         */
        onDestroyEvent : function () {
            me.input.releasePointerEvent("pointerdown", this);
            me.input.releasePointerEvent("pointerup", this);
            me.input.releasePointerEvent("pointerenter", this);
            me.input.releasePointerEvent("pointerleave", this);
            me.input.releasePointerEvent("pointercancel", this);
            me.timer.clearTimeout(this.holdTimeout);
        }
    });
})();
<|endoftext|>


<commit_before>'use strict';

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const color = require("rgb");
const base64js = require('base64-js');
const path = require('path');
const fs = require('fs');
const md5 = require('md5-file');

var baseVersion = 1000;

// host everything in the public folder
app.use(express.static(__dirname + '/public'));

//check github.com/esp8266/Arduino/issues/2228 for example
app.get('/update/base',function(req,res){
    //check version somehow
    //console.dir(req.headers);
    if(parseInt(req.headers['x-esp8266-version'])<baseVersion){
        var full_path = path.join(__dirname,'/bin/base.bin');
        fs.readFile(full_path,"binary",function(err,file){
            if(err){
                res.writeHeader(500, {"Content-Type": "text/plain"});
                res.write(err + "\n");
                res.end();
            }
            else{
                res.writeHeader(200,
                   {"Content-Type": "application/octect-stream",
                   "Content-Disposition": "attachment;filename="+path.basename(full_path),
                   "Content-Length": ""+fs.statSync(full_path)["size"],
                   "x-MD5": md5.sync(full_path)});
                res.write(file, "binary");
                res.end();
            }
        });
    }
    else{
        res.writeHeader(304, {"Content-Type": "text/plain"});
        res.write("304 Not Modified\n");
        res.end();
    }

});

var port = 80;
server.listen(port);

io.on('connection',function(socket){
    console.log("client "+socket['id']+" connected");
    socket.on('subscribe',function(roomName){
        socket.join(roomName);
        console.log("client "+socket['id']+" joined room "+roomName);
        CheckForUpdate(roomName);
    });
});

const STATION_COUNT = 36;
const LED_CLUSTER_COUNT = 45; // actual LED count is 45*3 = 135

//the locally stored color array for each station
var colors = new Array(STATION_COUNT).fill(new Array(LED_CLUSTER_COUNT).fill(new CRGB(0,0,0)));

/*
 * prototype function for CRGB data to mimic the FastLED library
 */
function CRGB(red, green, blue){
    this.r = red;
    this.g = green;
    this.b = blue;
    this.int = function(){
        return r>>16+g>>8+b>>0;
    }
    this.setRGB = function(red, green, blue){
        this.r = red;
        this.g = green;
        this.b = blue;
    }
    this.setHSV = function(hue, sat, val){
        var temp = HSVtoRGB(hue,sat,val);
        this.r = temp.r;
        this.g = temp.g;
        this.b = temp.b;
    }
}

/*
 * Turn off LEDs on a specific station
 */
function Clear(stationId){
    io.sockets.to(stationId).emit('clear','');
}

/*
 * Turn off LEDs on all stations
 */
function ClearAll(){
    io.sockets.to('stations').emit('clear','');
}

/*
 * mimics FastLED FillSolid method
 */
function FillSolid(stationId,startIndex,numToFill,ledColor){
    //update server's copy of the LED custer state
    for(var i=startIndex;i<startIndex+numToFill;i++){
        colors[stationId][i].r = ledColor.r;
        colors[stationId][i].g = ledColor.g;
        colors[stationId][i].b = ledColor.b;
    }
    var dataBuffer = new Uint8Array([startIndex,numToFill,ledColor.r,ledColor.g,ledColor.b]);
    io.sockets.to(stationId).emit('fillSolid',dataBuffer);
}

/*
 * Set a single LED color on a specific station
 */
function SetColor(stationId,startIndex,ledColor){
    //update server's copy of the LED custer state
    colors[stationId][startIndex].r = ledColor.r;
    colors[stationId][startIndex].g = ledColor.g;
    colors[stationId][startIndex].b = ledColor.b;

    var dataBuffer = new Uint8Array([startIndex,ledColor.r,ledColor.g,ledColor.b]);
    io.sockets.to(stationId).emit('setColor',dataBuffer);
}

/*
 * Send a set of different colors to a subset of a specific station
 * Could be the whole station
 * the number of leds is computed by the lenth of the colorArray
 * colorArray is an array of CRGB
 */
function SetColors(stationId,startIndex,colorArray){
    //update server's copy of the LED custer state
    colors[stationId][ledIndex].r = ledColor.r;
    colors[stationId][ledIndex].g = ledColor.g;
    colors[stationId][ledIndex].b = ledColor.b;

    var dataBuffer = new Uint8Array([ledIndex,numToFill,ledColor.r,ledColor.g,ledColor.b]);
    io.sockets.to(stationId).emit('setColors',dataBuffer);
}

/*
 * GOL Station 5 segment code
 */
function SetFiveColors(stationId,fiveColorArray){
    //update server's copy of the LED custer state
    if(stationId<STATION_COUNT){
        for(var i=0;i<5;i++){
            for(var j=0;j<LED_CLUSTER_COUNT/5;j++){
                colors[stationId][i*9+j].r = fiveColorArray[i].r;
                colors[stationId][i*9+j].g = fiveColorArray[i].g;
                colors[stationId][i*9+j].b = fiveColorArray[i].b;
            }
        }
    }

//    var dataArrayBuffer = new ArrayBuffer(15);
//    var dataBuffer = new Uint8Array(dataArrayBuffer);
//    dataArrayBuffer[0]=fiveColorArray[0].r;
//    dataArrayBuffer[1]=fiveColorArray[0].g;
//    dataArrayBuffer[2]=fiveColorArray[0].b;
//
//    dataArrayBuffer[3]=fiveColorArray[1].r;
//    dataArrayBuffer[4]=fiveColorArray[1].g;
//    dataArrayBuffer[5]=fiveColorArray[1].b;
//
//    dataArrayBuffer[6]=fiveColorArray[2].r;
//    dataArrayBuffer[7]=fiveColorArray[2].g;
//    dataArrayBuffer[8]=fiveColorArray[2].b;
//
//    dataArrayBuffer[9]=fiveColorArray[3].r;
//    dataArrayBuffer[10]=fiveColorArray[3].g;
//    dataArrayBuffer[11]=fiveColorArray[3].b;
//
//    dataArrayBuffer[12]=fiveColorArray[4].r;
//    dataArrayBuffer[13]=fiveColorArray[4].g;
//    dataArrayBuffer[14]=fiveColorArray[4].b;

    var dataBuffer2 = new Uint8Array([
        fiveColorArray[0].r,fiveColorArray[0].g,fiveColorArray[0].b,
        fiveColorArray[1].r,fiveColorArray[1].g,fiveColorArray[1].b,
        fiveColorArray[2].r,fiveColorArray[2].g,fiveColorArray[2].b,
        fiveColorArray[3].r,fiveColorArray[3].g,fiveColorArray[3].b,
        fiveColorArray[4].r,fiveColorArray[4].g,fiveColorArray[4].b
    ]);

    io.sockets.to(stationId).emit('setFives',base64js.fromByteArray(dataBuffer2));
    //console.log(base64js.fromByteArray(dataBuffer2));
}

/*
 * Set the colors on the stations to the colors stored on the server
 * typially used on startup to retrieve the last stored state of the system
 */
function SyncColorsFromServer(){
    for(var i=0;i<STATION_COUNT;i++){
        SetColors(i,0,colors[i]);
    }
}

/*
 * sets entire strip colors at once
 * typically used for loading last saved state
 * or used for more efficient devliery of complete color changes
 */
function SetStrip(stationId,colorArray){

}

/*
 * Force clients to check for firmware updates
 */
function CheckForUpdate(stationId){
    io.sockets.to(stationId).emit('checkForUpdate',"");
}

/*
 * Helper method for HSV color model
 */
function HSVtoRGB(h, s, v) {
    var r, g, b, i, f, p, q, t;
    if (arguments.length === 1) {
        s = h.s, v = h.v, h = h.h;
    }
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    switch (i % 6) {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
    }
    return {
        r: Math.round(r * 255),
        g: Math.round(g * 255),
        b: Math.round(b * 255)
    };
}

/*
 * Rainbow test code below here
 */

var hue = 120;
var hueBase = 360;
var colorString = '';

var fiveColors = new Array(5).fill(new CRGB(0,0,0));

var doStuff = function(){
    hue = (hue+6)%hueBase;
    var t_hue = hue;
    for(var i=0;i<STATION_COUNT;i++){
        t_hue = (hue+1)%hueBase;
        fiveColors[0] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);
        t_hue = (hue+2)%hueBase;
        fiveColors[1] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);
        t_hue = (hue+3)%hueBase;
        fiveColors[2] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);
        t_hue = (hue+4)%hueBase;
        fiveColors[3] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);
        t_hue = (hue+5)%hueBase;
        fiveColors[4] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);

        SetFiveColors(i,fiveColors);
    }

    fiveColors[0] = HSVtoRGB(0.0,1.0,1.0);
    fiveColors[1] = HSVtoRGB(0.0,1.0,1.0);
    fiveColors[2] = HSVtoRGB(0.0,1.0,1.0);
    fiveColors[3] = HSVtoRGB(0.0,1.0,1.0);
    fiveColors[4] = HSVtoRGB(0.0,1.0,1.0);
    SetFiveColors(24932,fiveColors);

    //console.log(fiveColors[0]);

};
setInterval(doStuff,5000);<commit_msg>added serverside test code for looking at light colors from two weeks ago<commit_after>'use strict';

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const color = require("rgb");
const base64js = require('base64-js');
const path = require('path');
const fs = require('fs');
const md5 = require('md5-file');

var baseVersion = 1000;

// host everything in the public folder
app.use(express.static(__dirname + '/public'));

//check github.com/esp8266/Arduino/issues/2228 for example
app.get('/update/base',function(req,res){
    //check version somehow
    //console.dir(req.headers);
    if(parseInt(req.headers['x-esp8266-version'])<baseVersion){
        var full_path = path.join(__dirname,'/bin/base.bin');
        fs.readFile(full_path,"binary",function(err,file){
            if(err){
                res.writeHeader(500, {"Content-Type": "text/plain"});
                res.write(err + "\n");
                res.end();
            }
            else{
                res.writeHeader(200,
                   {"Content-Type": "application/octect-stream",
                   "Content-Disposition": "attachment;filename="+path.basename(full_path),
                   "Content-Length": ""+fs.statSync(full_path)["size"],
                   "x-MD5": md5.sync(full_path)});
                res.write(file, "binary");
                res.end();
            }
        });
    }
    else{
        res.writeHeader(304, {"Content-Type": "text/plain"});
        res.write("304 Not Modified\n");
        res.end();
    }

});

var port = 80;
server.listen(port);

io.on('connection',function(socket){
    console.log("client "+socket['id']+" connected");
    socket.on('subscribe',function(roomName){
        socket.join(roomName);
        socket.join('allStations');
        console.log("client "+socket['id']+" joined room "+roomName);
        CheckForUpdate(roomName);
    });
});

const STATION_COUNT = 36;
const LED_CLUSTER_COUNT = 45; // actual LED count is 45*3 = 135

//the locally stored color array for each station
var colors = new Array(STATION_COUNT).fill(new Array(LED_CLUSTER_COUNT).fill(new CRGB(0,0,0)));

/*
 * prototype function for CRGB data to mimic the FastLED library
 */
function CRGB(red, green, blue){
    this.r = red;
    this.g = green;
    this.b = blue;
    this.int = function(){
        return r>>16+g>>8+b>>0;
    }
    this.setRGB = function(red, green, blue){
        this.r = red;
        this.g = green;
        this.b = blue;
    }
    this.setHSV = function(hue, sat, val){
        var temp = HSVtoRGB(hue,sat,val);
        this.r = temp.r;
        this.g = temp.g;
        this.b = temp.b;
    }
}

/*
 * Turn off LEDs on a specific station
 */
function Clear(stationId){
    io.sockets.to(stationId).emit('clear','');
}

/*
 * Turn off LEDs on all stations
 */
function ClearAll(){
    io.sockets.to('stations').emit('clear','');
}

/*
 * mimics FastLED FillSolid method
 */
function FillSolid(stationId,startIndex,numToFill,ledColor){
    if(!isNaN(stationId)){
        //update server's copy of the LED custer state
        for(var i=startIndex;i<startIndex+numToFill;i++){
            colors[stationId][i].r = ledColor.r;
            colors[stationId][i].g = ledColor.g;
            colors[stationId][i].b = ledColor.b;
        }
    }
    var dataBuffer = new Uint8Array([startIndex,numToFill,ledColor.r,ledColor.g,ledColor.b]);
    io.sockets.to(stationId).emit('fillSolid',dataBuffer);
}

/*
 * Set a single LED color on a specific station
 */
function SetColor(stationId,startIndex,ledColor){
    if(!isNaN(stationId)){
        //update server's copy of the LED custer state
        colors[stationId][startIndex].r = ledColor.r;
        colors[stationId][startIndex].g = ledColor.g;
        colors[stationId][startIndex].b = ledColor.b;
    }

    var dataBuffer = new Uint8Array([startIndex,ledColor.r,ledColor.g,ledColor.b]);
    io.sockets.to(stationId).emit('setColor',dataBuffer);
}

/*
 * Send a set of different colors to a subset of a specific station
 * Could be the whole station
 * the number of leds is computed by the lenth of the colorArray
 * colorArray is an array of CRGB
 */
function SetColors(stationId,startIndex,colorArray){
    //update server's copy of the LED custer state
    colors[stationId][ledIndex].r = ledColor.r;
    colors[stationId][ledIndex].g = ledColor.g;
    colors[stationId][ledIndex].b = ledColor.b;

    var dataBuffer = new Uint8Array([ledIndex,numToFill,ledColor.r,ledColor.g,ledColor.b]);
    io.sockets.to(stationId).emit('setColors',dataBuffer);
}

/*
 * GOL Station 5 segment code
 */
function SetFiveColors(stationId,fiveColorArray){
    if(!isNaN(stationId)){
        //update server's copy of the LED custer state
        if(stationId<STATION_COUNT){
            for(var i=0;i<5;i++){
                for(var j=0;j<LED_CLUSTER_COUNT/5;j++){
                    colors[stationId][i*9+j].r = fiveColorArray[i].r;
                    colors[stationId][i*9+j].g = fiveColorArray[i].g;
                    colors[stationId][i*9+j].b = fiveColorArray[i].b;
                }
            }
        }
    }

//    var dataArrayBuffer = new ArrayBuffer(15);
//    var dataBuffer = new Uint8Array(dataArrayBuffer);
//    dataArrayBuffer[0]=fiveColorArray[0].r;
//    dataArrayBuffer[1]=fiveColorArray[0].g;
//    dataArrayBuffer[2]=fiveColorArray[0].b;
//
//    dataArrayBuffer[3]=fiveColorArray[1].r;
//    dataArrayBuffer[4]=fiveColorArray[1].g;
//    dataArrayBuffer[5]=fiveColorArray[1].b;
//
//    dataArrayBuffer[6]=fiveColorArray[2].r;
//    dataArrayBuffer[7]=fiveColorArray[2].g;
//    dataArrayBuffer[8]=fiveColorArray[2].b;
//
//    dataArrayBuffer[9]=fiveColorArray[3].r;
//    dataArrayBuffer[10]=fiveColorArray[3].g;
//    dataArrayBuffer[11]=fiveColorArray[3].b;
//
//    dataArrayBuffer[12]=fiveColorArray[4].r;
//    dataArrayBuffer[13]=fiveColorArray[4].g;
//    dataArrayBuffer[14]=fiveColorArray[4].b;

    var dataBuffer2 = new Uint8Array([
        fiveColorArray[0].r,fiveColorArray[0].g,fiveColorArray[0].b,
        fiveColorArray[1].r,fiveColorArray[1].g,fiveColorArray[1].b,
        fiveColorArray[2].r,fiveColorArray[2].g,fiveColorArray[2].b,
        fiveColorArray[3].r,fiveColorArray[3].g,fiveColorArray[3].b,
        fiveColorArray[4].r,fiveColorArray[4].g,fiveColorArray[4].b
    ]);

    io.sockets.to(stationId).emit('setFives',base64js.fromByteArray(dataBuffer2));
    //console.log(base64js.fromByteArray(dataBuffer2));
}

/*
 * Set the colors on the stations to the colors stored on the server
 * typially used on startup to retrieve the last stored state of the system
 */
function SyncColorsFromServer(){
    for(var i=0;i<STATION_COUNT;i++){
        SetColors(i,0,colors[i]);
    }
}

/*
 * sets entire strip colors at once
 * typically used for loading last saved state
 * or used for more efficient devliery of complete color changes
 */
function SetStrip(stationId,colorArray){

}

/*
 * Force clients to check for firmware updates
 */
function CheckForUpdate(stationId){
    io.sockets.to(stationId).emit('checkForUpdate',"");
}

/*
 * Helper method for HSV color model
 */
function HSVtoRGB(h, s, v) {
    var r, g, b, i, f, p, q, t;
    if (arguments.length === 1) {
        s = h.s, v = h.v, h = h.h;
    }
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    switch (i % 6) {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
    }
    return {
        r: Math.round(r * 255),
        g: Math.round(g * 255),
        b: Math.round(b * 255)
    };
}

/*
 * Rainbow test code below here
 */

var hue = 120;
var hueBase = 360;
var colorString = '';

var fiveColors = new Array(5).fill(new CRGB(0,0,0));

var doStuff = function(){
    hue = (hue+6)%hueBase;
    var t_hue = hue;
    for(var i=0;i<STATION_COUNT;i++){
        t_hue = (hue+1)%hueBase;
        fiveColors[0] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);
        t_hue = (hue+2)%hueBase;
        fiveColors[1] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);
        t_hue = (hue+3)%hueBase;
        fiveColors[2] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);
        t_hue = (hue+4)%hueBase;
        fiveColors[3] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);
        t_hue = (hue+5)%hueBase;
        fiveColors[4] = HSVtoRGB(t_hue/(hueBase),1.0,1.0);

        SetFiveColors(i,fiveColors);
    }

    fiveColors[0] = HSVtoRGB(0.0,1.0,1.0);
    fiveColors[1] = HSVtoRGB(0.0,1.0,1.0);
    fiveColors[2] = HSVtoRGB(0.0,1.0,1.0);
    fiveColors[3] = HSVtoRGB(0.0,1.0,1.0);
    fiveColors[4] = HSVtoRGB(0.0,1.0,1.0);
    SetFiveColors(24932,fiveColors);

    //console.log(fiveColors[0]);

};
//setInterval(doStuff,5000);

var doStuff2 = function(){
    hue = (hue+1)%hueBase;
    var hue2 = (Math.floor(hue/6)*6)%hueBase;
    fiveColors[0] = HSVtoRGB(hue2/(hueBase),1.0,1.0);
    fiveColors[1] = HSVtoRGB(hue2/(hueBase),1.0,1.0);
    fiveColors[2] = HSVtoRGB(hue2/(hueBase),1.0,1.0);
    fiveColors[3] = HSVtoRGB(hue2/(hueBase),1.0,1.0);
    fiveColors[4] = HSVtoRGB(hue2/(hueBase),1.0,1.0);
    SetFiveColors('allStations',fiveColors);
    //console.log(fiveColors[0]); //FillSolid('allStations',0,LED_CLUSTER_COUNT,HSVtoRGB(hue/(hueBase),1.0,1.0));
}
setInterval(doStuff2,1000/60);<|endoftext|>


<commit_before>/* Start of the app, we require everything that is needed */
define(function (require) {
    requirejs(['domReady!', "sugar-web/activity/activity", "sugar-web/graphics/presencepalette", 'activity/memorize-app'], function (doc, activity, presencePalette, memorizeApp) {

        window.memorizeApp = memorizeApp;
        memorizeApp.activity = activity;
        memorizeApp.activity.setup();

        if (window.top.sugar.environment.sharedId) {
            memorizeApp.initUI(function () {
                initPresence(memorizeApp.activity, memorizeApp, presencePalette);
            })
        } else {
            memorizeApp.initUI(function () {
                initPresence(memorizeApp.activity, memorizeApp, presencePalette);
                memorizeApp.computeCards(true);
                memorizeApp.drawGame();
                loadData(memorizeApp.activity, memorizeApp, function () {
                    memorizeApp.drawGame();
                });
            })

        }
    });
});

function loadData(activity, memorizeApp, callback) {
    var timeout = 0;
    if (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) {
        chrome.storage.local.get('sugar_settings', function (values) {
            timeout = 500;
        });
    } else {
        timeout = 0;
    }

    setTimeout(function () {
        activity.getDatastoreObject().loadAsText(function (error, metadata, jsonData) {
            if (jsonData == null) {
                return;
            }

            var data = JSON.parse(jsonData);
            if (data == null) {
                return;
            }

            if (data.game) {
                memorizeApp.game = data.game;
                memorizeApp.game.multiplayer = false;
                memorizeApp.game.selectedCards = [];
                memorizeApp.game.currentPlayer = "";
                memorizeApp.game.players = []
            }

            if (callback) {
                callback();
            }
        });
    }, timeout);
}

function initPresence(activity, memorizeApp, presencepalette, callback) {
    activity.getPresenceObject(function (error, presence) {
        memorizeApp.presence = presence;
        var networkButton = document.getElementById("network-button");
        var presencePalette = new presencepalette.PresencePalette(networkButton, undefined, presence);
        presence.onSharedActivityUserChanged(function (msg) {
            presencePalette.onSharedActivityUserChanged(msg);
        });

        //We use one of the palette feature that allows us to get the full list of current users everytime the list changes
        presencePalette.onUsersListChanged(function (users) {
            memorizeApp.onUsersListChanged(users);
        });

        // Launched with a shared id, activity is already shared
        if (window.top && window.top.sugar && window.top.sugar.environment && window.top.sugar.environment.sharedId) {
            shareActivity(activity, presence, memorizeApp, false);
            presencePalette.setShared(true);
        } else {
            presencePalette.addEventListener('shared', function () {
              presencePalette.popDown();
                shareActivity(activity, presence, memorizeApp, true);
            });
        }

        if (callback) {
            callback();
        }
    });
}

function shareActivity(activity, presence, memorizeApp, isHost) {

    memorizeApp.shareActivity(isHost);

    var userSettings = presence.getUserInfo();

    // Not found, create a new shared activity
    if (!window.top.sugar.environment.sharedId) {
        presence.createSharedActivity('org.olpcfrance.MemorizeActivity', function (groupId) {
            //console.log(groupId)
        });
    }

    // Show a disconnected message when the WebSocket is closed.
    presence.onConnectionClosed(function (event) {
        console.log(event);
        console.log("Connection closed");
    });

    presence.onDataReceived(function (data) {
        memorizeApp.onDataReceived(data);
    });

    presence.listUsers(function (users) {
        console.log(users)
    });
}
<commit_msg>Fix Indentation for MemorizeActivity<commit_after>/* Start of the app, we require everything that is needed */
define(function (require) {
    requirejs(['domReady!', "sugar-web/activity/activity", "sugar-web/graphics/presencepalette", 'activity/memorize-app'], function (doc, activity, presencePalette, memorizeApp) {

        window.memorizeApp = memorizeApp;
        memorizeApp.activity = activity;
        memorizeApp.activity.setup();

        if (window.top.sugar.environment.sharedId) {
            memorizeApp.initUI(function () {
                initPresence(memorizeApp.activity, memorizeApp, presencePalette);
            })
        } else {
            memorizeApp.initUI(function () {
                initPresence(memorizeApp.activity, memorizeApp, presencePalette);
                memorizeApp.computeCards(true);
                memorizeApp.drawGame();
                loadData(memorizeApp.activity, memorizeApp, function () {
                    memorizeApp.drawGame();
                });
            })

        }
    });
});

function loadData(activity, memorizeApp, callback) {
    var timeout = 0;
    if (typeof chrome != 'undefined' && chrome.app && chrome.app.runtime) {
        chrome.storage.local.get('sugar_settings', function (values) {
            timeout = 500;
        });
    } else {
        timeout = 0;
    }

    setTimeout(function () {
        activity.getDatastoreObject().loadAsText(function (error, metadata, jsonData) {
            if (jsonData == null) {
                return;
            }

            var data = JSON.parse(jsonData);
            if (data == null) {
                return;
            }

            if (data.game) {
                memorizeApp.game = data.game;
                memorizeApp.game.multiplayer = false;
                memorizeApp.game.selectedCards = [];
                memorizeApp.game.currentPlayer = "";
                memorizeApp.game.players = []
            }

            if (callback) {
                callback();
            }
        });
    }, timeout);
}

function initPresence(activity, memorizeApp, presencepalette, callback) {
    activity.getPresenceObject(function (error, presence) {
        memorizeApp.presence = presence;
        var networkButton = document.getElementById("network-button");
        var presencePalette = new presencepalette.PresencePalette(networkButton, undefined, presence);
        presence.onSharedActivityUserChanged(function (msg) {
            presencePalette.onSharedActivityUserChanged(msg);
        });

        //We use one of the palette feature that allows us to get the full list of current users everytime the list changes
        presencePalette.onUsersListChanged(function (users) {
            memorizeApp.onUsersListChanged(users);
        });

        // Launched with a shared id, activity is already shared
        if (window.top && window.top.sugar && window.top.sugar.environment && window.top.sugar.environment.sharedId) {
            shareActivity(activity, presence, memorizeApp, false);
            presencePalette.setShared(true);
        } else {
            presencePalette.addEventListener('shared', function () {
                presencePalette.popDown();
                shareActivity(activity, presence, memorizeApp, true);
            });
        }

        if (callback) {
            callback();
        }
    });
}

function shareActivity(activity, presence, memorizeApp, isHost) {

    memorizeApp.shareActivity(isHost);

    var userSettings = presence.getUserInfo();

    // Not found, create a new shared activity
    if (!window.top.sugar.environment.sharedId) {
        presence.createSharedActivity('org.olpcfrance.MemorizeActivity', function (groupId) {
            //console.log(groupId)
        });
    }

    // Show a disconnected message when the WebSocket is closed.
    presence.onConnectionClosed(function (event) {
        console.log(event);
        console.log("Connection closed");
    });

    presence.onDataReceived(function (data) {
        memorizeApp.onDataReceived(data);
    });

    presence.listUsers(function (users) {
        console.log(users)
    });
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2016 Actor LLC. <https://actor.im>
 */

import { find, some } from 'lodash';
import { Store } from 'flux/utils';
import Dispatcher from '../dispatcher/ActorAppDispatcher';
import { ActionTypes, PeerTypes } from '../constants/ActorAppConstants';
import ActorClient from '../utils/ActorClient';

class DialogStore extends Store {
  constructor(dispatcher) {
    super(dispatcher);

    this.dialogs = [];
    this.currentPeer = null;
    this.lastPeer = null;
  }

  getDialogs() {
    return this.dialogs;
  }

  getCurrentPeer() {
    return this.currentPeer;
  }

  getLastPeer() {
    return this.lastPeer;
  }

  isMember() {
    if (this.currentPeer !== null && this.currentPeer.type === PeerTypes.GROUP) {
      const group = ActorClient.getGroup(this.currentPeer.id);
      return group.members.length !== 0;
    } else {
      return true;
    }
  }

  isFavorite(id) {
    const favoriteDialogs = find(this.dialogs, {key: 'favourites'});
    if (!favoriteDialogs) return false;

    return some(favoriteDialogs.shorts, (dialog) => dialog.peer.peer.id === id);
  }

  __onDispatch(action) {
    switch(action.type) {
      case ActionTypes.DIALOGS_CHANGED:
        this.dialogs = action.dialogs;
        this.__emitChange();
        break;
      case ActionTypes.BIND_DIALOG_PEER:
        this.lastPeer = this.currentPeer;
        this.currentPeer = action.peer;
        this.__emitChange();
        break;
      case ActionTypes.UNBIND_DIALOG_PEER:
        this.lastPeer = this.currentPeer;
        this.currentPeer = null;
        this.__emitChange();
        break;
      default:
    }
  }
}

export default new DialogStore(Dispatcher);
<commit_msg>fix(web): isMember should check if group not exists<commit_after>/*
 * Copyright (C) 2015-2016 Actor LLC. <https://actor.im>
 */

import { find, some } from 'lodash';
import { Store } from 'flux/utils';
import Dispatcher from '../dispatcher/ActorAppDispatcher';
import { ActionTypes, PeerTypes } from '../constants/ActorAppConstants';
import ActorClient from '../utils/ActorClient';

class DialogStore extends Store {
  constructor(dispatcher) {
    super(dispatcher);

    this.dialogs = [];
    this.currentPeer = null;
    this.lastPeer = null;
  }

  getDialogs() {
    return this.dialogs;
  }

  getCurrentPeer() {
    return this.currentPeer;
  }

  getLastPeer() {
    return this.lastPeer;
  }

  isMember() {
    if (this.currentPeer !== null && this.currentPeer.type === PeerTypes.GROUP) {
      const group = ActorClient.getGroup(this.currentPeer.id);
      return group && group.members.length !== 0;
    }

    return true;
  }

  isFavorite(id) {
    const favoriteDialogs = find(this.dialogs, {key: 'favourites'});
    if (!favoriteDialogs) return false;

    return some(favoriteDialogs.shorts, (dialog) => dialog.peer.peer.id === id);
  }

  __onDispatch(action) {
    switch(action.type) {
      case ActionTypes.DIALOGS_CHANGED:
        this.dialogs = action.dialogs;
        this.__emitChange();
        break;
      case ActionTypes.BIND_DIALOG_PEER:
        this.lastPeer = this.currentPeer;
        this.currentPeer = action.peer;
        this.__emitChange();
        break;
      case ActionTypes.UNBIND_DIALOG_PEER:
        this.lastPeer = this.currentPeer;
        this.currentPeer = null;
        this.__emitChange();
        break;
      default:
    }
  }
}

export default new DialogStore(Dispatcher);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import HighchartsChart from '../HighchartsChart';
import Options3d from '../Options3d';

const CHART = {
  options3d: { enabled: true }
};
const ZAXIS = {
  id: 'zAxis'
};

class Highcharts3dChart extends Component {

  static propTypes = Options3d.propTypes;

  render () {
    const {
      children, alpha, axisLabelPosition, beta, depth, fitToPlot, frame, viewDistance, ...rest
    } = this.props;

    return (
      <HighchartsChart chart={CHART} zAxis={ZAXIS} {...rest}>
        <Options3d
          alpha={alpha}
          axisLabelPosition={axisLabelPosition}
          beta={beta}
          depth={depth}
          fitToPlot={fitToPlot}
          frame={frame}
          viewDistance={viewDistance} />
        {children}
      </HighchartsChart>
    );
  }
}

export default Highcharts3dChart;
<commit_msg>Rewrite Highcharts3dChart<commit_after>import React from 'react';
import HighchartsChart from '../HighchartsChart';
import Options3d from '../Options3d';

const CHART = {
  options3d: { enabled: true }
};
const ZAXIS = {
  id: 'zAxis'
};

const Highcharts3dChart = ({
  children,
  alpha,
  axisLabelPosition,
  beta,
  depth,
  fitToPlot,
  frame,
  viewDistance,
  ...rest
}) => (
  <HighchartsChart chart={CHART} zAxis={ZAXIS} {...rest}>
    <Options3d
      alpha={alpha}
      axisLabelPosition={axisLabelPosition}
      beta={beta}
      depth={depth}
      fitToPlot={fitToPlot}
      frame={frame}
      viewDistance={viewDistance}
    />
    {children}
  </HighchartsChart>
);
Highcharts3dChart.propTypes = Options3d.propTypes;

export default Highcharts3dChart;
<|endoftext|>