<commit_before>/*
 * Wegas
 * http://wegas.albasim.ch
 *
 * Copyright (c) 2013, 2014, 2015 School of Business and Engineering Vaud, Comem
 * Licensed under the MIT License
 */

/**
 *
 * @fileoverview
 * @author Cyril Junod <cyril.junod at gmail.com>
 */
/*global YUI*/
YUI.add("wegas-entitychooser", function(Y) {
    "use strict";
    var CONTENTBOX = "contentBox", EntityChooser, EntityChooser2,
        CLASSES = {
            CHOOSEN: "chooser-choosen"
        };

    EntityChooser = Y.Base.create("wegas-entitychooser",
        Y.Widget,
        [Y.WidgetChild,
            Y.Wegas.Widget,
            Y.Wegas.Editable],
        {
            CONTENT_TEMPLATE: "

",
            initializer: function() {
                this._handlers = [];
                /**
                 * hold a ref to the currently selected name
                 */
                this.currentTarget = null;
            },
            hideAllOverlay: function() {
                Y.Wegas.Widget.prototype.hideAllOverlay.call(this);
                this._currentWidget && this._currentWidget.hideAllOverlay();
            },
            syncUI: function() {
                var items = (this.get("variable.evaluated") ?
                    (this.get("flatten") ?
                        this.get("variable.evaluated").flatten() :
                        this.get("variable.evaluated").get("items")) :
                    []),
                    i, tmp,
                    entityBox = this.get(CONTENTBOX).one(".chooser-entities"),
                    length = items.length,
                    filter = [];

                entityBox.empty();
                if (this.get("classFilter")) {
                    if (!Y.Lang.isArray(this.get("classFilter"))) {
                        filter.push(this.get("classFilter"));
                    } else {
                        filter = filter.concat(this.get("classFilter"));
                    }
                }

                for (i = 0; i < length; i += 1) {
                    if (filter.length === 0 || Y.Array.find(filter, function(item) {
                        return item === items[i].get("@class");
                    })) {
                        if ((!items[i].getInstance().getAttrs().hasOwnProperty("active") ||
                            items[i].getInstance().get("active")) &&
                            (!items[i].getInstance().getAttrs().hasOwnProperty("enabled") ||
                                items[i].getInstance().get("enabled"))) {
                            entityBox.append("
" +
                                (items[i].get("title") || items[i].get("label")) + "
");
                        }
                    }
                }
                if (this.currentTarget) {
                    tmp = entityBox.all("[data-name='" + this.currentTarget + "']");
                    tmp.addClass(CLASSES.CHOOSEN);
                    if (!tmp.size()) { //current target exists but is not rendered anymore
                        Y.later(200, this, function() {
                            this.widget.destroy();
                            this.widget = null;
                        });
                        this.currentTarget = null;
                    }
                }
            },
            bindUI: function() {
                this.get(CONTENTBOX).delegate("click", function(e) {
                    var targetName = e.target.getData("name");
                    if (this.currentTarget === targetName) { // I'm the choosen one
                        return;
                    }
                    this.genWidget(targetName);
                    this.get(CONTENTBOX).all("." + CLASSES.CHOOSEN).removeClass(CLASSES.CHOOSEN);
                    e.target.addClass(CLASSES.CHOOSEN);
                    this.currentTarget = targetName;
                }, ".chooser-entities .chooser-entity", this);
                this._handlers.push(Y.Wegas.Facade.Variable.after("update", this.syncUI, this));
            },
            genWidget: function(name) {
                var cfg = this.get("widget"),
                    ctx = this;
                Y.Wegas.Editable.use(cfg, function(Y) {
                    if (ctx.widget) {
                        ctx.widget.set(ctx.get("widgetAttr"), {
                            name: name
                        });
                    } else {
                        cfg[ctx.get("widgetAttr")] = {
                            name: name
                        };
                        Y.Wegas.use(cfg, Y.bind(function() {
                            this.widget = Y.Wegas.Widget.create(cfg);
                            this.widget.render(this.get(CONTENTBOX).one(".chooser-widget"));
                            this.widget.on(["*:message", "*:showOverlay", "*:hideOverlay"], this.fire, this); // Event on the loaded
                        }, ctx));
                    }
                });
            },
            destructor: function() {
                this.widget && this.widget.destroy();
                Y.Array.each(this._handlers, function(handle) {
                    handle.detach();
                });
            }

        },
        {
            ATTRS: {
                variable: {
                    getter: Y.Wegas.Widget.VARIABLEDESCRIPTORGETTER,
                    _inputex: {
                        _type: "variableselect",
                        label: "Folder",
                        classFilter: ["ListDescriptor"]
                    }
                },
                widget: {
                    value: {
                        type: "HistoryDialog"
                    },
                    getter: function(v) {
                        return Y.JSON.parse(Y.JSON.stringify(v));
                    },
                    _inputex: {
                        _type: "group",
                        fields: [{
                                type: "string",
                                name: "type",
                                label: "Type"
                            }]
                    }
                },
                widgetAttr: {
                    value: "dialogueVariable",
                    type: "string"
                },
                flatten: {
                    type: "boolean",
                    value: "true"
                },
                classFilter: {
                    type: "array",
                    _inputex: {
                        elementType: {
                            required: true,
                            type: "select",
                            choices: Y.Wegas.persistence.AVAILABLE_TYPES
                        }
                    }
                }
            }
        });
    Y.Wegas.EntityChooser = EntityChooser;

    EntityChooser2 = Y.Base.create("wegas-entitychooser2",
        Y.Widget,
        [Y.WidgetParent, Y.WidgetChild, Y.Wegas.Widget, Y.Wegas.Editable], {
        CONTENT_TEMPLATE: "

",
        initializer: function() {
            this._handlers = [];
            /**
             * hold a ref to the currently selected name
             */
            this.currentTarget = null;
        },
        hideAllOverlay: function() {
            Y.Wegas.Widget.prototype.hideAllOverlay.call(this);
            this._currentWidget && this._currentWidget.hideAllOverlay();
        },
        syncUI: function() {
            var items = (this.get("variable.evaluated") ? (this.get("flatten") ? this.get("variable.evaluated").flatten() : this.get("variable.evaluated").get("items")) : []),
                i, tmp, li,
                entityBox = this.get(CONTENTBOX).one(".chooser-entities"),
                length = items.length, label, getLabel,
                filter = Y.Object.keys(this.get("widgets"));
            entityBox.empty();
            for (i = 0; i < length; i += 1) {
                if (Y.Array.find(filter, function(item) {
                    return item === items[i].get("@class");
                })) {
                    if ((!items[i].getInstance().getAttrs().hasOwnProperty("active") ||
                        items[i].getInstance().get("active")) &&
                        (!items[i].getInstance().getAttrs().hasOwnProperty("enabled") ||
                            items[i].getInstance().get("enabled"))) {
                        getLabel = this.get("widgets")[items[i].get("@class")].getLabel;
                        label = (items[i].get("title") || items[i].get("label"));

                        li = entityBox.appendChild("
" + label + "
");

                        if (getLabel) {
                            label = getLabel(items[i], items[i].get("name"), function(name, the_label) {
                                entityBox.one("[data-name='" + name + "']").setContent(the_label);
                            });
                        }

                        if (this.get("markUnread")) {
                            li.plug(Y.Plugin.MarkAsUnread, {
                                userCounters: this.get("userCounters"),
                                variable: {
                                    name: items[i].get("name")
                                }
                            });
                            li.MarkAsUnread.updateCounter();
                        }
                    }
                }
            }
            if (this.currentTarget) {
                tmp = entityBox.all("[data-name='" + this.currentTarget + "']");
                tmp.addClass(CLASSES.CHOOSEN);
                if (!tmp.size()) { //current target exists but is not rendered anymore
                    Y.later(200, this, function() {
                        this._currentWidget.destroy();
                        this._currentWidget = null;
                        this._currentWidgetType = null;
                    });
                    this.currentTarget = null;
                }
            } else {
                if (this.get("autoSelectFirstUnread")) {
                    Y.later(200, this, this.selectNextUnread);
                }
            }
        },
        selectNextUnread: function() {
            var newTarget;
            if (this.currentTarget) {
                newTarget = this.get("contentBox").one("[data-name='" + this.currentTarget + "'] + .unread");
            } else {
                newTarget = this.get("contentBox").one(".unread");
            }
            if (newTarget) {
                this.select(newTarget);
            }
        },
        select: function(target) {
            var targetName = target.getData("name"),
                targetType = target.getData("type");
            if (this.currentTarget === targetName) { // I'm the choosen one
                return;
            }
            this.genWidget(targetType, targetName);
            this.get(CONTENTBOX).all("." + CLASSES.CHOOSEN).removeClass(CLASSES.CHOOSEN);
            target.addClass(CLASSES.CHOOSEN);
            this.currentTarget = targetName;
        },
        bindUI: function() {
            this.get(CONTENTBOX).delegate("click", function(e) {
                this.selectNextUnread();
            }, ".select-next", this);

            this.get(CONTENTBOX).delegate("click", function(e) {
                this.select(e.target);
            }, ".chooser-entities .chooser-entity", this);
            this._handlers.push(Y.Wegas.Facade.Variable.after("update", this.syncUI, this));
        },
        genWidget: function(type, name) {
            var widgetConfig = this.get("widgets")[type],
                ctx = this, cfg;
            cfg = widgetConfig.widget;

            Y.Wegas.Editable.use(cfg, function(Y) {
                if (ctx._currentWidgetType === type) {
                    ctx._currentWidget.set(widgetConfig.widgetAttr, {
                        name: name
                    });
                } else {
                    cfg[widgetConfig.widgetAttr] = {
                        name: name
                    };
                    Y.Wegas.use(cfg, Y.bind(function() {
                        this._currentWidget && this._currentWidget.destroy();

                        this._currentWidgetType = type;
                        this._currentWidget = Y.Wegas.Widget.create(cfg);
                        this._currentWidget.render(this.get(CONTENTBOX).one(".chooser-widget"));
                        // propagates selected event to the "parent"
                        this._currentWidget.on(["*:message", "*:showOverlay", "*:hideOverlay"], this.fire, this);
                        this._currentWidget.__hackParent = this;
                    }, ctx));
                }
            });
        },
        interceptor: function(e) {
            debugger;
        },
        destructor: function() {
            this._currentWidget && this._currentWidget.destroy();
            Y.Array.each(this._handlers, function(handle) {
                handle.detach();
            });
        }
    },
        {
            ATTRS: {
                variable: {
                    getter: Y.Wegas.Widget.VARIABLEDESCRIPTORGETTER,
                    _inputex: {
                        _type: "variableselect",
                        legend: "Folder",
                        classFilter: ["ListDescriptor"]
                    }
                },
                widgets: {
                    optional: false,
                    _inputex: {
                        _type: "hashlist",
                        keyField: "dataType",
                        valueField: "config",
                        elementType: {
                            type: "combine",
                            fields: [{
                                    name: "dataType",
                                    type: "select",
                                    value: "DialogueDescriptor",
                                    choices: Y.Wegas.persistence.AVAILABLE_TYPES
                                }, {
                                    name: "config",
                                    type: "group",
                                    fields: [{
                                            name: "widget",
                                            type: "group",
                                            value: {
                                                type: "HistoryDialogue"
                                            },
                                            fields: [{
                                                    type: "string",
                                                    name: "type",
                                                    label: "Type"
                                                }]
                                        }, {
                                            name: "widgetAttr",
                                            value: "dialogueVariable",
                                            type: "string"
                                        }, {
                                            name: "getLabel",
                                            value: null,
                                            type: "function"
                                        }
                                    ]
                                }
                            ]
                        }
                    }
                },
                markUnread: {
                    type: "boolean",
                    value: false
                },
                autoSelectFirstUnread: {
                    type: "boolean",
                    value: true,
                    optional: true
                },
                userCounters: {
                    type: "object",
                    value: {},
                    optional: true,
                    _inputex: {
                        type: "hidden"
                    }
                },
                flatten: {
                    type: "boolean",
                    value: "true"
                }
            }
        });
    Y.Wegas.EntityChooser2 = EntityChooser2;

});
<commit_msg>EntityChooser<commit_after>/*
 * Wegas
 * http://wegas.albasim.ch
 *
 * Copyright (c) 2013, 2014, 2015 School of Business and Engineering Vaud, Comem
 * Licensed under the MIT License
 */

/**
 *
 * @fileoverview
 * @author Cyril Junod <cyril.junod at gmail.com>
 */
/*global YUI*/
YUI.add("wegas-entitychooser", function(Y) {
    "use strict";
    var CONTENTBOX = "contentBox", EntityChooser, EntityChooser2,
        CLASSES = {
            CHOOSEN: "chooser-choosen"
        };

    EntityChooser = Y.Base.create("wegas-entitychooser",
        Y.Widget,
        [Y.WidgetChild,
            Y.Wegas.Widget,
            Y.Wegas.Editable],
        {
            CONTENT_TEMPLATE: "

",
            initializer: function() {
                this._handlers = [];
                /**
                 * hold a ref to the currently selected name
                 */
                this.currentTarget = null;
            },
            hideAllOverlay: function() {
                Y.Wegas.Widget.prototype.hideAllOverlay.call(this);
                this._currentWidget && this._currentWidget.hideAllOverlay();
            },
            syncUI: function() {
                var items = (this.get("variable.evaluated") ?
                    (this.get("flatten") ?
                        this.get("variable.evaluated").flatten() :
                        this.get("variable.evaluated").get("items")) :
                    []),
                    i, tmp,
                    entityBox = this.get(CONTENTBOX).one(".chooser-entities"),
                    length = items.length,
                    filter = [];

                entityBox.empty();
                if (this.get("classFilter")) {
                    if (!Y.Lang.isArray(this.get("classFilter"))) {
                        filter.push(this.get("classFilter"));
                    } else {
                        filter = filter.concat(this.get("classFilter"));
                    }
                }

                for (i = 0; i < length; i += 1) {
                    if (filter.length === 0 || Y.Array.find(filter, function(item) {
                        return item === items[i].get("@class");
                    })) {
                        if ((!items[i].getInstance().getAttrs().hasOwnProperty("active") ||
                            items[i].getInstance().get("active")) &&
                            (!items[i].getInstance().getAttrs().hasOwnProperty("enabled") ||
                                items[i].getInstance().get("enabled"))) {
                            entityBox.append("
" +
                                (items[i].get("title") || items[i].get("label")) + "
");
                        }
                    }
                }
                if (this.currentTarget) {
                    tmp = entityBox.all("[data-name='" + this.currentTarget + "']");
                    tmp.addClass(CLASSES.CHOOSEN);
                    if (!tmp.size()) { //current target exists but is not rendered anymore
                        Y.later(200, this, function() {
                            this.widget.destroy();
                            this.widget = null;
                        });
                        this.currentTarget = null;
                    }
                }
            },
            bindUI: function() {
                this.get(CONTENTBOX).delegate("click", function(e) {
                    var targetName = e.target.getData("name");
                    if (this.currentTarget === targetName) { // I'm the choosen one
                        return;
                    }
                    this.genWidget(targetName);
                    this.get(CONTENTBOX).all("." + CLASSES.CHOOSEN).removeClass(CLASSES.CHOOSEN);
                    e.target.addClass(CLASSES.CHOOSEN);
                    this.currentTarget = targetName;
                }, ".chooser-entities .chooser-entity", this);
                this._handlers.push(Y.Wegas.Facade.Variable.after("update", this.syncUI, this));
            },
            genWidget: function(name) {
                var cfg = this.get("widget"),
                    ctx = this;
                Y.Wegas.Editable.use(cfg, function(Y) {
                    if (ctx.widget) {
                        ctx.widget.set(ctx.get("widgetAttr"), {
                            name: name
                        });
                    } else {
                        cfg[ctx.get("widgetAttr")] = {
                            name: name
                        };
                        Y.Wegas.use(cfg, Y.bind(function() {
                            this.widget = Y.Wegas.Widget.create(cfg);
                            this.widget.render(this.get(CONTENTBOX).one(".chooser-widget"));
                            this.widget.on(["*:message", "*:showOverlay", "*:hideOverlay"], this.fire, this); // Event on the loaded
                        }, ctx));
                    }
                });
            },
            destructor: function() {
                this.widget && this.widget.destroy();
                Y.Array.each(this._handlers, function(handle) {
                    handle.detach();
                });
            }

        },
        {
            ATTRS: {
                variable: {
                    getter: Y.Wegas.Widget.VARIABLEDESCRIPTORGETTER,
                    _inputex: {
                        _type: "variableselect",
                        label: "Folder",
                        classFilter: ["ListDescriptor"]
                    }
                },
                widget: {
                    value: {
                        type: "HistoryDialog"
                    },
                    getter: function(v) {
                        return Y.JSON.parse(Y.JSON.stringify(v));
                    },
                    _inputex: {
                        _type: "group",
                        fields: [{
                                type: "string",
                                name: "type",
                                label: "Type"
                            }]
                    }
                },
                widgetAttr: {
                    value: "dialogueVariable",
                    type: "string"
                },
                flatten: {
                    type: "boolean",
                    value: "true"
                },
                classFilter: {
                    type: "array",
                    _inputex: {
                        elementType: {
                            required: true,
                            type: "select",
                            choices: Y.Wegas.persistence.AVAILABLE_TYPES
                        }
                    }
                }
            }
        });
    Y.Wegas.EntityChooser = EntityChooser;

    EntityChooser2 = Y.Base.create("wegas-entitychooser2",
        Y.Widget,
        [Y.WidgetParent, Y.WidgetChild, Y.Wegas.Widget, Y.Wegas.Editable], {
        CONTENT_TEMPLATE: "

",
        initializer: function() {
            this._handlers = [];
            /**
             * hold a ref to the currently selected name
             */
            this.currentTarget = null;
        },
        hideAllOverlay: function() {
            Y.Wegas.Widget.prototype.hideAllOverlay.call(this);
            this._currentWidget && this._currentWidget.hideAllOverlay();
        },
        syncUI: function() {
            var items = (this.get("variable.evaluated") ? (this.get("flatten") ? this.get("variable.evaluated").flatten() : this.get("variable.evaluated").get("items")) : []),
                i, tmp, li,
                entityBox = this.get(CONTENTBOX).one(".chooser-entities"),
                length = items.length, label, getLabel,
                filter = Y.Object.keys(this.get("widgets"));
            entityBox.empty();
            for (i = 0; i < length; i += 1) {
                if (Y.Array.find(filter, function(item) {
                    return item === items[i].get("@class");
                })) {
                    if ((!items[i].getInstance().getAttrs().hasOwnProperty("active") ||
                        items[i].getInstance().get("active")) &&
                        (!items[i].getInstance().getAttrs().hasOwnProperty("enabled") ||
                            items[i].getInstance().get("enabled"))) {
                        getLabel = this.get("widgets")[items[i].get("@class")].getLabel;
                        label = (items[i].get("title") || items[i].get("label"));

                        li = entityBox.appendChild("
" + label + "
");

                        if (getLabel) {
                            label = getLabel(items[i], items[i].get("name"), function(name, the_label) {
                                entityBox.one("[data-name='" + name + "']").setContent(the_label);
                            });
                        }

                        if (this.get("markUnread")) {
                            li.plug(Y.Plugin.MarkAsUnread, {
                                userCounters: this.get("userCounters"),
                                variable: {
                                    name: items[i].get("name")
                                }
                            });
                            li.MarkAsUnread.updateCounter();
                        }
                    }
                }
            }
            if (this.currentTarget) {
                tmp = entityBox.all("[data-name='" + this.currentTarget + "']");
                tmp.addClass(CLASSES.CHOOSEN);
                if (!tmp.size()) { //current target exists but is not rendered anymore
                    Y.later(200, this, function() {
                        this._currentWidget.destroy();
                        this._currentWidget = null;
                        this._currentWidgetType = null;
                    });
                    this.currentTarget = null;
                }
            } else {
                if (this.get("autoSelectFirstUnread")) {
                    Y.later(500, this, this.selectNextUnread);
                }
            }
        },
        selectNextUnread: function() {
            var newTarget;
            if (this.currentTarget) {
                newTarget = this.get("contentBox").one("[data-name='" + this.currentTarget + "'] + .unread");
            } else {
                newTarget = this.get("contentBox").one(".unread");
            }
            if (newTarget) {
                this.select(newTarget);
            }
        },
        select: function(target) {
            var targetName = target.getData("name"),
                targetType = target.getData("type");
            if (this.currentTarget === targetName) { // I'm the choosen one
                return;
            }
            this.genWidget(targetType, targetName);
            this.get(CONTENTBOX).all("." + CLASSES.CHOOSEN).removeClass(CLASSES.CHOOSEN);
            target.addClass(CLASSES.CHOOSEN);
            this.currentTarget = targetName;
        },
        bindUI: function() {
            this.get(CONTENTBOX).delegate("click", function(e) {
                this.selectNextUnread();
            }, ".select-next", this);

            this.get(CONTENTBOX).delegate("click", function(e) {
                this.select(e.target);
            }, ".chooser-entities .chooser-entity", this);
            this._handlers.push(Y.Wegas.Facade.Variable.after("update", this.syncUI, this));
        },
        genWidget: function(type, name) {
            var widgetConfig = this.get("widgets")[type],
                ctx = this, cfg;
            cfg = widgetConfig.widget;

            Y.Wegas.Editable.use(cfg, function(Y) {
                if (ctx._currentWidgetType === type) {
                    ctx._currentWidget.set(widgetConfig.widgetAttr, {
                        name: name
                    });
                } else {
                    cfg[widgetConfig.widgetAttr] = {
                        name: name
                    };
                    Y.Wegas.use(cfg, Y.bind(function() {
                        this._currentWidget && this._currentWidget.destroy();

                        this._currentWidgetType = type;
                        this._currentWidget = Y.Wegas.Widget.create(cfg);
                        this._currentWidget.render(this.get(CONTENTBOX).one(".chooser-widget"));
                        // propagates selected event to the "parent"
                        this._currentWidget.on(["*:message", "*:showOverlay", "*:hideOverlay"], this.fire, this);
                        this._currentWidget.__hackParent = this;
                    }, ctx));
                }
            });
        },
        interceptor: function(e) {
            debugger;
        },
        destructor: function() {
            this._currentWidget && this._currentWidget.destroy();
            Y.Array.each(this._handlers, function(handle) {
                handle.detach();
            });
        }
    },
        {
            ATTRS: {
                variable: {
                    getter: Y.Wegas.Widget.VARIABLEDESCRIPTORGETTER,
                    _inputex: {
                        _type: "variableselect",
                        legend: "Folder",
                        classFilter: ["ListDescriptor"]
                    }
                },
                widgets: {
                    optional: false,
                    _inputex: {
                        _type: "hashlist",
                        keyField: "dataType",
                        valueField: "config",
                        elementType: {
                            type: "combine",
                            fields: [{
                                    name: "dataType",
                                    type: "select",
                                    value: "DialogueDescriptor",
                                    choices: Y.Wegas.persistence.AVAILABLE_TYPES
                                }, {
                                    name: "config",
                                    type: "group",
                                    fields: [{
                                            name: "widget",
                                            type: "group",
                                            value: {
                                                type: "HistoryDialogue"
                                            },
                                            fields: [{
                                                    type: "string",
                                                    name: "type",
                                                    label: "Type"
                                                }]
                                        }, {
                                            name: "widgetAttr",
                                            value: "dialogueVariable",
                                            type: "string"
                                        }, {
                                            name: "getLabel",
                                            value: null,
                                            type: "function"
                                        }
                                    ]
                                }
                            ]
                        }
                    }
                },
                markUnread: {
                    type: "boolean",
                    value: false
                },
                autoSelectFirstUnread: {
                    type: "boolean",
                    value: true,
                    optional: true
                },
                userCounters: {
                    type: "object",
                    value: {},
                    optional: true,
                    _inputex: {
                        type: "hidden"
                    }
                },
                flatten: {
                    type: "boolean",
                    value: "true"
                }
            }
        });
    Y.Wegas.EntityChooser2 = EntityChooser2;

});
<|endoftext|>


<commit_before>
/*!
 * CSS - Evaluator
 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Visitor = require('./')
  , nodes = require('../nodes')
  , Stack = require('../stack')
  , Frame = require('../stack/frame')
  , Scope = require('../stack/scope')
  , utils = require('../utils')
  , bifs = require('../functions')
  , dirname = require('path').dirname
  , fs = require('fs');

/**
 * Initialize a new `Evaluator` with the given `root` Node
 * and the following `options`.
 *
 * Options:
 *
 *   - `compress`  Compress the css output, defaults to false
 *
 * @param {Node} root
 * @api public
 */

var Evaluator = module.exports = function Evaluator(root, options) {
  options = options || {};
  Visitor.call(this, root);
  this.stack = new Stack;
  this.functions = options.functions || {};
  this.paths = options.paths || [];
  this.paths.push(dirname(options.filename || '.'));
  this.stack.push(this.global = new Frame(root));
};

/**
 * Inherit from `Visitor.prototype`.
 */

Evaluator.prototype.__proto__ = Visitor.prototype;

/**
 * Proxy visit to expose node line numbers.
 *
 * @param {Node} node
 * @return {Node}
 * @api public
 */

var visit = Visitor.prototype.visit;
Evaluator.prototype.visit = function(node){
  try {
    this.lineno = node.lineno;
    return visit.call(this, node);
  } catch (err) {
    // TODO: less-lame hack to reference
    // the origin node source input
    err.str = err.str || node.source;
    throw err;
  }
};

/**
 * Evaluate the tree.
 *
 * @return {Node}
 * @api public
 */

Evaluator.prototype.evaluate = function(){
  return this.visit(this.root);
};

/**
 * Visit Root.
 */

Evaluator.prototype.visitRoot = function(block){
  for (var i = 0; i < block.nodes.length; ++i) {
    this.rootIndex = i;
    block.nodes[i] = this.visit(block.nodes[i]);
  }
  return block;
};

/**
 * Visit Block.
 */

Evaluator.prototype.visitBlock = function(block){
  this.stack.push(new Frame(block));
  for (var i = 0, len = block.nodes.length; i < len; ++i) {
    block.nodes[i] = this.visit(block.nodes[i]);
  }
  this.stack.pop();
  return block;
};

/**
 * Visit Color.
 */

Evaluator.prototype.visitColor = function(color){
  return color;
};

/**
 * Visit HSLA.
 */

Evaluator.prototype.visitHSLA = function(hsla){
  return hsla;
};

/**
 * Visit Literal.
 */

Evaluator.prototype.visitLiteral = function(lit){
  return lit;
};

/**
 * Visit Boolean.
 */

Evaluator.prototype.visitBoolean = function(bool){
  return bool;
};

/**
 * Visit Unit.
 */

Evaluator.prototype.visitUnit = function(unit){
  return unit;
};

/**
 * Visit Ident.
 */

Evaluator.prototype.visitIdent = function(id){
  return id;
};

/**
 * Visit String.
 */

Evaluator.prototype.visitString = function(string){
  return string;
};

/**
 * Visit Null.
 */

Evaluator.prototype.visitNull = function(node){
  return node;
};

/**
 * Visit Function.
 */

Evaluator.prototype.visitFunction = function(fn){
  return fn;
};

/**
 * Visit RuleSet.
 */

Evaluator.prototype.visitRuleSet = function(set){
  for (var i = 0, len = set.nodes.length; i < len; ++i) {
    set.nodes[i] = this.visit(set.nodes[i]);
  }
  return set;
};

/**
 * Visit Selector.
 */

Evaluator.prototype.visitSelector = function(selector){
  selector.block = this.visit(selector.block);
  return selector;
};

/**
 * Visit Charset.
 */

Evaluator.prototype.visitCharset = function(charset){
  return charset;
};

/**
 * Visit Call.
 */

Evaluator.prototype.visitCall = function(call){
  // TODO: refactor
  var fn = this.lookupFunction(call.name);

  // Undefined function, render literal css
  if (!fn) {
    call.args = this.visit(call.args);
    return call;
  }

  // First node in expression
  if (fn instanceof nodes.Expression) {
    fn = fn.first;
  }

  var ret, body
    , params = fn.params
    , stack = this.stack;

  // Evaluate arguments
  var _ = this.return;
  this.return = true;
  var args = this.visit(call.args);
  this.return = _;

  // Built-in
  if ('function' == typeof fn) {
    args = args.nodes.map(function(node){
      return node.first;
    });
    body = fn.apply(this, args);
    if (!(body instanceof nodes.Expression)) {
      var expr = new nodes.Expression;
      expr.push(body);
      body = expr;
    }
  // User-defined
  } else if (fn instanceof nodes.Function) {
    body = fn.block.clone();

    // Inject argument scope
    var block = new nodes.Block(body.parent);
    body.parent = block;

    // Inject arguments as locals
    stack.push(new Frame(block));
    params.nodes.forEach(function(node, i){
      var val = args.nodes[i] || node.val;
      node = node.clone();
      node.val = val;
      if (node.val instanceof nodes.Null) {
        throw new Error('argument ' + node + ' required for ' + fn);
      }
      stack.currentFrame.scope.add(node);
    });

    // Evaluate
    body = this.visit(body);
    stack.pop();
  } else {
    throw new Error('tried to call ' + call.name + '(), but cannot call non-function ' + fn.first);
  }

  // Return
  if (this.return) {
    ret = body.nodes[body.nodes.length - 1];
  // Mixin
  } else {
    body.nodes.forEach(function(node){
      stack.currentFrame.block.nodes.push(node);
    });
    ret = nodes.null;
  }

  return ret;
};

/**
 * Visit Variable.
 */

Evaluator.prototype.visitVariable = function(variable){
  // Lookup
  if (nodes.null == variable.val) {
    var val = this.stack.lookup(variable.name);
    if (!val) throw new Error('undefined variable ' + variable);
    return this.visit(val);
  // Assign
  } else {
    variable.val = this.visit(variable.val);
    this.stack.currentFrame.scope.add(variable);
    return variable.val;
  }
};

/**
 * Visit BinOp.
 */

Evaluator.prototype.visitBinOp = function(binop){
  // Special-case "is defined" pseudo binop
  if ('is defined' == binop.op) return this.isDefined(binop.left);

  var _ = this.return;
  this.return = true;
  // Visit operands
  var op = binop.op
    , left = this.visit(binop.left).first
    , right = this.visit(binop.right).first;
  this.return = _;

  // Coercion
  var ignore = ['||', '&&', 'is a'];
  if (!~ignore.indexOf(op)) {
    right = left.coerce(right);
  }

  // Operate
  return this.visit(left.operate(op, right));
};

/**
 * Visit UnaryOp.
 */

Evaluator.prototype.visitUnaryOp = function(unary){
  var op = unary.op
    , node = this.visit(unary.expr).first;

  if ('!' != op) utils.assertType(node, nodes.Unit);

  switch (op) {
    case '-':
      node.val = -node.val;
      break;
    case '+':
      node.val = +node.val;
      break;
    case '~':
      node.val = ~node.val;
      break;
    case '!':
      return node.toBoolean().negate();
  }

  return node;
};

/**
 * Visit TernaryOp.
 */

Evaluator.prototype.visitTernary = function(ternary){
  var ok = this.visit(ternary.cond).toBoolean();
  return nodes.true == ok
    ? this.visit(ternary.trueExpr)
    : this.visit(ternary.falseExpr);
};

/**
 * Visit Expression.
 */

Evaluator.prototype.visitExpression = function(expr){
  for (var i = 0, len = expr.nodes.length; i < len; ++i) {
    expr.nodes[i] = this.visit(expr.nodes[i]);
  }
  return expr;
};

/**
 * Visit Property.
 */

Evaluator.prototype.visitProperty = function(prop){
  var fn = this.stack.lookup(prop.name)
    , call = fn instanceof nodes.Function
    , literal = prop.name == this.callingProperty;

  // Function of the same name
  if (call && !literal) {
    this.callingProperty = prop.name;
    var ret = this.visit(new nodes.Call(prop.name, prop.expr));
    this.callingProperty = null;
    return ret;
  // Regular property
  } else {
    var _ = this.return;
    this.return = true;
    prop.expr = this.visit(prop.expr);
    this.return = _;
  }
  return prop;
};

/**
 * Visit Import.
 */

Evaluator.prototype.visitImport = function(import){
  var found
    , root = this.root
    , i = this.rootIndex
    , stylus = require('../stylus')
    , path = import.path
    , relative = this.importPath;

  // Literal
  if (/\.css$/.test(path)) return import;
  path += '.styl';

  // Lookup
  if (relative) this.paths.push(relative);
  found = utils.lookup(path, this.paths);
  if (relative) this.paths.pop();

  // Throw if import failed
  if (!found) throw new Error('failed to locate @import file ' + path);
  this.importPath = dirname(found);

  // Parse the file
  var str = fs.readFileSync(found, 'utf8')
    , rest = root.nodes.splice(++i, root.nodes.length);

  stylus.parse(str, {
      filename: found
    , root: root
  });

  rest.forEach(function(node){
    root.push(node);
  });

  return nodes.null;
};

/**
 * Lookup function by the given `name`.
 *
 * @param {String} name
 * @return {Function}
 * @api public
 */

Evaluator.prototype.lookupFunction = function(name){
  return this.stack.lookup(name)
    || this.functions[name]
    || bifs[name];
};

/**
 * Check if the given `node` is a variable, and if it is defined.
 *
 * @param {Node} node
 * @return {Boolean}
 * @api private
 */

Evaluator.prototype.isDefined = function(node){
  if (node instanceof nodes.Variable) {
    return nodes.Boolean(this.stack.lookup(node.name));
  } else {
    throw new Error('invalid "is defined" check on non-variable ' + node);
  }
};
<commit_msg>Fixed function retvals when visiting assignment<commit_after>
/*!
 * CSS - Evaluator
 * Copyright(c) 2010 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var Visitor = require('./')
  , nodes = require('../nodes')
  , Stack = require('../stack')
  , Frame = require('../stack/frame')
  , Scope = require('../stack/scope')
  , utils = require('../utils')
  , bifs = require('../functions')
  , dirname = require('path').dirname
  , fs = require('fs');

/**
 * Initialize a new `Evaluator` with the given `root` Node
 * and the following `options`.
 *
 * Options:
 *
 *   - `compress`  Compress the css output, defaults to false
 *
 * @param {Node} root
 * @api public
 */

var Evaluator = module.exports = function Evaluator(root, options) {
  options = options || {};
  Visitor.call(this, root);
  this.stack = new Stack;
  this.functions = options.functions || {};
  this.paths = options.paths || [];
  this.paths.push(dirname(options.filename || '.'));
  this.stack.push(this.global = new Frame(root));
};

/**
 * Inherit from `Visitor.prototype`.
 */

Evaluator.prototype.__proto__ = Visitor.prototype;

/**
 * Proxy visit to expose node line numbers.
 *
 * @param {Node} node
 * @return {Node}
 * @api public
 */

var visit = Visitor.prototype.visit;
Evaluator.prototype.visit = function(node){
  try {
    this.lineno = node.lineno;
    return visit.call(this, node);
  } catch (err) {
    // TODO: less-lame hack to reference
    // the origin node source input
    err.str = err.str || node.source;
    throw err;
  }
};

/**
 * Evaluate the tree.
 *
 * @return {Node}
 * @api public
 */

Evaluator.prototype.evaluate = function(){
  return this.visit(this.root);
};

/**
 * Visit Root.
 */

Evaluator.prototype.visitRoot = function(block){
  for (var i = 0; i < block.nodes.length; ++i) {
    this.rootIndex = i;
    block.nodes[i] = this.visit(block.nodes[i]);
  }
  return block;
};

/**
 * Visit Block.
 */

Evaluator.prototype.visitBlock = function(block){
  this.stack.push(new Frame(block));
  for (var i = 0, len = block.nodes.length; i < len; ++i) {
    block.nodes[i] = this.visit(block.nodes[i]);
  }
  this.stack.pop();
  return block;
};

/**
 * Visit Color.
 */

Evaluator.prototype.visitColor = function(color){
  return color;
};

/**
 * Visit HSLA.
 */

Evaluator.prototype.visitHSLA = function(hsla){
  return hsla;
};

/**
 * Visit Literal.
 */

Evaluator.prototype.visitLiteral = function(lit){
  return lit;
};

/**
 * Visit Boolean.
 */

Evaluator.prototype.visitBoolean = function(bool){
  return bool;
};

/**
 * Visit Unit.
 */

Evaluator.prototype.visitUnit = function(unit){
  return unit;
};

/**
 * Visit Ident.
 */

Evaluator.prototype.visitIdent = function(id){
  return id;
};

/**
 * Visit String.
 */

Evaluator.prototype.visitString = function(string){
  return string;
};

/**
 * Visit Null.
 */

Evaluator.prototype.visitNull = function(node){
  return node;
};

/**
 * Visit Function.
 */

Evaluator.prototype.visitFunction = function(fn){
  return fn;
};

/**
 * Visit RuleSet.
 */

Evaluator.prototype.visitRuleSet = function(set){
  for (var i = 0, len = set.nodes.length; i < len; ++i) {
    set.nodes[i] = this.visit(set.nodes[i]);
  }
  return set;
};

/**
 * Visit Selector.
 */

Evaluator.prototype.visitSelector = function(selector){
  selector.block = this.visit(selector.block);
  return selector;
};

/**
 * Visit Charset.
 */

Evaluator.prototype.visitCharset = function(charset){
  return charset;
};

/**
 * Visit Call.
 */

Evaluator.prototype.visitCall = function(call){
  // TODO: refactor
  var fn = this.lookupFunction(call.name);

  // Undefined function, render literal css
  if (!fn) {
    call.args = this.visit(call.args);
    return call;
  }

  // First node in expression
  if (fn instanceof nodes.Expression) {
    fn = fn.first;
  }

  var ret, body
    , params = fn.params
    , stack = this.stack;

  // Evaluate arguments
  var _ = this.return;
  this.return = true;
  var args = this.visit(call.args);
  this.return = _;

  // Built-in
  if ('function' == typeof fn) {
    args = args.nodes.map(function(node){
      return node.first;
    });
    body = fn.apply(this, args);
    if (!(body instanceof nodes.Expression)) {
      var expr = new nodes.Expression;
      expr.push(body);
      body = expr;
    }
  // User-defined
  } else if (fn instanceof nodes.Function) {
    body = fn.block.clone();

    // Inject argument scope
    var block = new nodes.Block(body.parent);
    body.parent = block;

    // Inject arguments as locals
    stack.push(new Frame(block));
    params.nodes.forEach(function(node, i){
      var val = args.nodes[i] || node.val;
      node = node.clone();
      node.val = val;
      if (node.val instanceof nodes.Null) {
        throw new Error('argument ' + node + ' required for ' + fn);
      }
      stack.currentFrame.scope.add(node);
    });

    // Evaluate
    body = this.visit(body);
    stack.pop();
  } else {
    throw new Error('tried to call ' + call.name + '(), but cannot call non-function ' + fn.first);
  }

  // Return
  if (this.return) {
    ret = body.nodes[body.nodes.length - 1];
  // Mixin
  } else {
    body.nodes.forEach(function(node){
      stack.currentFrame.block.nodes.push(node);
    });
    ret = nodes.null;
  }

  return ret;
};

/**
 * Visit Variable.
 */

Evaluator.prototype.visitVariable = function(variable){
  // Lookup
  if (nodes.null == variable.val) {
    var val = this.stack.lookup(variable.name);
    if (!val) throw new Error('undefined variable ' + variable);
    return this.visit(val);
  // Assign
  } else {
    var _ = this.return;
    this.return = true;
    variable.val = this.visit(variable.val);
    this.return = _;
    this.stack.currentFrame.scope.add(variable);
    return variable.val;
  }
};

/**
 * Visit BinOp.
 */

Evaluator.prototype.visitBinOp = function(binop){
  // Special-case "is defined" pseudo binop
  if ('is defined' == binop.op) return this.isDefined(binop.left);

  var _ = this.return;
  this.return = true;
  // Visit operands
  var op = binop.op
    , left = this.visit(binop.left).first
    , right = this.visit(binop.right).first;
  this.return = _;

  // Coercion
  var ignore = ['||', '&&', 'is a'];
  if (!~ignore.indexOf(op)) {
    right = left.coerce(right);
  }

  // Operate
  return this.visit(left.operate(op, right));
};

/**
 * Visit UnaryOp.
 */

Evaluator.prototype.visitUnaryOp = function(unary){
  var op = unary.op
    , node = this.visit(unary.expr).first;

  if ('!' != op) utils.assertType(node, nodes.Unit);

  switch (op) {
    case '-':
      node.val = -node.val;
      break;
    case '+':
      node.val = +node.val;
      break;
    case '~':
      node.val = ~node.val;
      break;
    case '!':
      return node.toBoolean().negate();
  }

  return node;
};

/**
 * Visit TernaryOp.
 */

Evaluator.prototype.visitTernary = function(ternary){
  var ok = this.visit(ternary.cond).toBoolean();
  return nodes.true == ok
    ? this.visit(ternary.trueExpr)
    : this.visit(ternary.falseExpr);
};

/**
 * Visit Expression.
 */

Evaluator.prototype.visitExpression = function(expr){
  for (var i = 0, len = expr.nodes.length; i < len; ++i) {
    expr.nodes[i] = this.visit(expr.nodes[i]);
  }
  return expr;
};

/**
 * Visit Property.
 */

Evaluator.prototype.visitProperty = function(prop){
  var fn = this.stack.lookup(prop.name)
    , call = fn instanceof nodes.Function
    , literal = prop.name == this.callingProperty;

  // Function of the same name
  if (call && !literal) {
    this.callingProperty = prop.name;
    var ret = this.visit(new nodes.Call(prop.name, prop.expr));
    this.callingProperty = null;
    return ret;
  // Regular property
  } else {
    var _ = this.return;
    this.return = true;
    prop.expr = this.visit(prop.expr);
    this.return = _;
  }
  return prop;
};

/**
 * Visit Import.
 */

Evaluator.prototype.visitImport = function(import){
  var found
    , root = this.root
    , i = this.rootIndex
    , stylus = require('../stylus')
    , path = import.path
    , relative = this.importPath;

  // Literal
  if (/\.css$/.test(path)) return import;
  path += '.styl';

  // Lookup
  if (relative) this.paths.push(relative);
  found = utils.lookup(path, this.paths);
  if (relative) this.paths.pop();

  // Throw if import failed
  if (!found) throw new Error('failed to locate @import file ' + path);
  this.importPath = dirname(found);

  // Parse the file
  var str = fs.readFileSync(found, 'utf8')
    , rest = root.nodes.splice(++i, root.nodes.length);

  stylus.parse(str, {
      filename: found
    , root: root
  });

  rest.forEach(function(node){
    root.push(node);
  });

  return nodes.null;
};

/**
 * Lookup function by the given `name`.
 *
 * @param {String} name
 * @return {Function}
 * @api public
 */

Evaluator.prototype.lookupFunction = function(name){
  return this.stack.lookup(name)
    || this.functions[name]
    || bifs[name];
};

/**
 * Check if the given `node` is a variable, and if it is defined.
 *
 * @param {Node} node
 * @return {Boolean}
 * @api private
 */

Evaluator.prototype.isDefined = function(node){
  if (node instanceof nodes.Variable) {
    return nodes.Boolean(this.stack.lookup(node.name));
  } else {
    throw new Error('invalid "is defined" check on non-variable ' + node);
  }
};
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash-node'),
    utils = require('./utils'),
    storage = require('./storage');

var lichessSri = utils.lichessSri;

var urlsPool = _.range(9021, 9030).map(function(e) {
  return window.socketEndPoint + ':' + e;
});
urlsPool.unshift(window.socketEndPoint);

var strongSocketDefaults = {
  events: {
  },
  params: {
    sri: lichessSri,
    mobile: 1
  },
  options: {
    name: "unnamed",
    pingMaxLag: 7000, // time to wait for pong before reseting the connection
    pingDelay: 1000, // time between pong and ping
    autoReconnectDelay: 1000
  }
};

var StrongSocket = function(url, version, settings) {
  var self = this;
  self.settings = strongSocketDefaults;
  _.merge(self.settings, settings);
  self.url = url;
  self.version = version;
  self.options = self.settings.options;
  self.ws = null;
  self.pingSchedule = null;
  self.connectSchedule = null;
  self.ackableMessages = [];
  self.lastPingTime = self.now();
  self.currentLag = 0;
  self.averageLag = 0;
  self.autoReconnect = true;
  self.tryAnotherUrl = false;
  self.debug('Debug is enabled');
  self.connect();
  window.addEventListener('unload', function() {
    self.destroy();
  });
};

StrongSocket.prototype = {
  connect: function() {
    var self = this;
    self.destroy();
    self.autoReconnect = true;
    var fullUrl = "ws://" + self.baseUrl() + self.url + "?" + utils.serializeQueryParameters(_.defaults(self.settings.params, {
      version: self.version
    }));
    self.debug("connection attempt to " + fullUrl, true);
    try {
      if (window.WebSocket) self.ws = new window.WebSocket(fullUrl);
      else throw "[lila] no websockets available!";

      // if (self.options.debug) window.liws = self.ws;
      self.ws.onerror = function(e) {
        self.onError(e);
      };
      self.ws.onclose = function() {
        if (self.autoReconnect) {
          self.debug('Will autoreconnect in ' + self.options.autoReconnectDelay);
          self.scheduleConnect(self.options.autoReconnectDelay);
        }
      };
      self.ws.onopen = function() {
        self.debug("connected to " + fullUrl, true);
        self.onSuccess();
        self.pingNow();
        var resend = self.ackableMessages;
        self.ackableMessages = [];
        resend.forEach(function(x) { self.send(x.t, x.d); });
      };
      self.ws.onmessage = function(e) {
        var m = JSON.parse(e.data);
        var mData = m.d || [];

        if (m.t === "n") self.pong();
        else self.debug(e.data);

        if (m.t === "b") mData.forEach(function(x) { self.handle(x); });
        else self.handle(m);
      };
    } catch (e) {
      self.onError(e);
    }
    self.scheduleConnect(self.options.pingMaxLag);
  },
  send: function(t, d, o) {
    var self = this;
    var data = d || {},
    options = o || {};
    if (options.ackable)
      self.ackableMessages.push({
        t: t,
        d: d
      });
    var message = JSON.stringify({
      t: t,
      d: data
    });
    self.debug("send " + message);
    try {
      self.ws.send(message);
    } catch (e) {
      self.debug(e);
    }
  },
  sendAckable: function(t, d) {
    this.send(t, d, { ackable: true });
  },
  scheduleConnect: function(delay) {
    var self = this;
    // self.debug('schedule connect ' + delay);
    clearTimeout(self.pingSchedule);
    clearTimeout(self.connectSchedule);
    self.connectSchedule = setTimeout(function() {
      self.tryAnotherUrl = true;
      self.connect();
    }, delay);
  },
  schedulePing: function(delay) {
    var self = this;
    clearTimeout(self.pingSchedule);
    self.pingSchedule = setTimeout(function() {
      self.pingNow();
    }, delay);
  },
  pingNow: function() {
    var self = this;
    clearTimeout(self.pingSchedule);
    clearTimeout(self.connectSchedule);
    try {
      self.ws.send(self.pingData());
      self.lastPingTime = self.now();
    } catch (e) {
      self.debug(e, true);
    }
    self.scheduleConnect(self.options.pingMaxLag);
  },
  pong: function() {
    var self = this;
    clearTimeout(self.connectSchedule);
    self.schedulePing(self.options.pingDelay);
    self.currentLag = self.now() - self.lastPingTime;
    if (!self.averageLag) self.averageLag = self.currentLag;
    else self.averageLag = 0.2 * (self.currentLag - self.averageLag) + self.averageLag;
  },
  pingData: function() {
    return JSON.stringify({
      t: "p",
      v: this.version
    });
  },
  handle: function(m) {
    var self = this;
    if (m.v) {
      if (m.v <= self.version) {
        self.debug("already has event " + m.v);
        return;
      }
      if (m.v > self.version + 1) {
        self.debug("event gap detected from " + self.version + " to " + m.v);
      }
      self.version = m.v;
    }
    switch (m.t || false) {
      case false:
        break;
      case 'ack':
        self.ackableMessages = [];
        break;
      default:
        if (!self.settings.receive || !self.settings.receive(m.t, m.d)) {
          var h = self.settings.events[m.t];
          if (_.isFunction(h)) h(m.d || null);
          else if (!self.options.ignoreUnknownMessages) {
            self.debug('Message not supported ' + JSON.stringify(m));
          }
        }
    }
  },
  now: function() {
    return new Date().getTime();
  },
  debug: function(msg, always) {
    if ((always || this.options.debug) && window.console && console.debug) {
      console.debug("[" + this.options.name + " " + lichessSri + "]", msg);
    }
  },
  destroy: function() {
    clearTimeout(this.pingSchedule);
    clearTimeout(this.connectSchedule);
    this.disconnect();
    this.ws = null;
  },
  disconnect: function() {
    if (this.ws) {
      this.debug("Disconnect", true);
      this.autoReconnect = false;
      this.ws.onerror = function() {};
      this.ws.onclose = function() {};
      this.ws.onopen = function() {};
      this.ws.onmessage = function() {};
      this.ws.close();
    }
  },
  onError: function(e) {
    var self = this;
    self.options.debug = true;
    self.debug('error: ' + JSON.stringify(e));
    self.tryAnotherUrl = true;
    clearTimeout(self.pingSchedule);
  },
  onSuccess: function() {
  },
  baseUrl: function() {
    if (window.socketEndPoint === 'socket.en.lichess.org') {
      var key = 'socket.baseUrl';
      var url = storage.get(key);
      if (!url) {
        url = urlsPool[0];
        storage.set(key, url);
      } else if (this.tryAnotherUrl) {
        this.tryAnotherUrl = false;
        url = urlsPool[(urlsPool.indexOf(url) + 1) % urlsPool.length];
        storage.set(key, url);
      }
      return url;
    }

    return window.socketEndPoint;
  },
  pingInterval: function() {
    return this.options.pingDelay + this.averageLag;
  }
};

module.exports = StrongSocket;
<commit_msg>Don't log pong msg in strongsocket<commit_after>'use strict';

var _ = require('lodash-node'),
    utils = require('./utils'),
    storage = require('./storage');

var lichessSri = utils.lichessSri;

var urlsPool = _.range(9021, 9030).map(function(e) {
  return window.socketEndPoint + ':' + e;
});
urlsPool.unshift(window.socketEndPoint);

var strongSocketDefaults = {
  events: {
  },
  params: {
    sri: lichessSri,
    mobile: 1
  },
  options: {
    name: 'unnamed',
    pingMaxLag: 7000, // time to wait for pong before reseting the connection
    pingDelay: 1000, // time between pong and ping
    autoReconnectDelay: 1000
  }
};

var StrongSocket = function(url, version, settings) {
  var self = this;
  self.settings = strongSocketDefaults;
  _.merge(self.settings, settings);
  self.url = url;
  self.version = version;
  self.options = self.settings.options;
  self.ws = null;
  self.pingSchedule = null;
  self.connectSchedule = null;
  self.ackableMessages = [];
  self.lastPingTime = self.now();
  self.currentLag = 0;
  self.averageLag = 0;
  self.autoReconnect = true;
  self.tryAnotherUrl = false;
  self.debug('Debug is enabled');
  self.connect();
  window.addEventListener('unload', function() {
    self.destroy();
  });
};

StrongSocket.prototype = {
  connect: function() {
    var self = this;
    self.destroy();
    self.autoReconnect = true;
    var fullUrl = 'ws://' + self.baseUrl() + self.url + '?' + utils.serializeQueryParameters(_.defaults(self.settings.params, {
      version: self.version
    }));
    self.debug('connection attempt to ' + fullUrl, true);
    try {
      if (window.WebSocket) self.ws = new window.WebSocket(fullUrl);
      else throw '[lila] no websockets available!';

      // if (self.options.debug) window.liws = self.ws;
      self.ws.onerror = function(e) {
        self.onError(e);
      };
      self.ws.onclose = function() {
        if (self.autoReconnect) {
          self.debug('Will autoreconnect in ' + self.options.autoReconnectDelay);
          self.scheduleConnect(self.options.autoReconnectDelay);
        }
      };
      self.ws.onopen = function() {
        self.debug('connected to ' + fullUrl, true);
        self.onSuccess();
        self.pingNow();
        var resend = self.ackableMessages;
        self.ackableMessages = [];
        resend.forEach(function(x) { self.send(x.t, x.d); });
      };
      self.ws.onmessage = function(e) {
        var m = JSON.parse(e.data);
        var mData = m.d || [];

        if (m.t === 'n')
          self.pong();
        else {
          self.debug(e.data);
          if (m.t === 'b') mData.forEach(function(x) { self.handle(x); });
          else self.handle(m);
        }
      };
    } catch (e) {
      self.onError(e);
    }
    self.scheduleConnect(self.options.pingMaxLag);
  },
  send: function(t, d, o) {
    var self = this;
    var data = d || {},
    options = o || {};
    if (options.ackable)
      self.ackableMessages.push({
        t: t,
        d: d
      });
    var message = JSON.stringify({
      t: t,
      d: data
    });
    self.debug("send " + message);
    try {
      self.ws.send(message);
    } catch (e) {
      self.debug(e);
    }
  },
  sendAckable: function(t, d) {
    this.send(t, d, { ackable: true });
  },
  scheduleConnect: function(delay) {
    var self = this;
    // self.debug('schedule connect ' + delay);
    clearTimeout(self.pingSchedule);
    clearTimeout(self.connectSchedule);
    self.connectSchedule = setTimeout(function() {
      self.tryAnotherUrl = true;
      self.connect();
    }, delay);
  },
  schedulePing: function(delay) {
    var self = this;
    clearTimeout(self.pingSchedule);
    self.pingSchedule = setTimeout(function() {
      self.pingNow();
    }, delay);
  },
  pingNow: function() {
    var self = this;
    clearTimeout(self.pingSchedule);
    clearTimeout(self.connectSchedule);
    try {
      self.ws.send(self.pingData());
      self.lastPingTime = self.now();
    } catch (e) {
      self.debug(e, true);
    }
    self.scheduleConnect(self.options.pingMaxLag);
  },
  pong: function() {
    var self = this;
    clearTimeout(self.connectSchedule);
    self.schedulePing(self.options.pingDelay);
    self.currentLag = self.now() - self.lastPingTime;
    if (!self.averageLag) self.averageLag = self.currentLag;
    else self.averageLag = 0.2 * (self.currentLag - self.averageLag) + self.averageLag;
  },
  pingData: function() {
    return JSON.stringify({
      t: "p",
      v: this.version
    });
  },
  handle: function(m) {
    var self = this;
    if (m.v) {
      if (m.v <= self.version) {
        self.debug("already has event " + m.v);
        return;
      }
      if (m.v > self.version + 1) {
        self.debug("event gap detected from " + self.version + " to " + m.v);
      }
      self.version = m.v;
    }
    switch (m.t || false) {
      case false:
        break;
      case 'ack':
        self.ackableMessages = [];
        break;
      default:
        if (!self.settings.receive || !self.settings.receive(m.t, m.d)) {
          var h = self.settings.events[m.t];
          if (h) h(m.d || null);
          else if (!self.options.ignoreUnknownMessages) {
            self.debug('Message not supported ' + JSON.stringify(m));
          }
        }
    }
  },
  now: function() {
    return new Date().getTime();
  },
  debug: function(msg, always) {
    if ((always || this.options.debug) && window.console && console.debug) {
      console.debug("[" + this.options.name + " " + lichessSri + "]", msg);
    }
  },
  destroy: function() {
    clearTimeout(this.pingSchedule);
    clearTimeout(this.connectSchedule);
    this.disconnect();
    this.ws = null;
  },
  disconnect: function() {
    if (this.ws) {
      this.debug("Disconnect", true);
      this.autoReconnect = false;
      this.ws.onerror = function() {};
      this.ws.onclose = function() {};
      this.ws.onopen = function() {};
      this.ws.onmessage = function() {};
      this.ws.close();
    }
  },
  onError: function(e) {
    var self = this;
    self.options.debug = true;
    self.debug('error: ' + JSON.stringify(e));
    self.tryAnotherUrl = true;
    clearTimeout(self.pingSchedule);
  },
  onSuccess: function() {
  },
  baseUrl: function() {
    if (window.socketEndPoint === 'socket.en.lichess.org') {
      var key = 'socket.baseUrl';
      var url = storage.get(key);
      if (!url) {
        url = urlsPool[0];
        storage.set(key, url);
      } else if (this.tryAnotherUrl) {
        this.tryAnotherUrl = false;
        url = urlsPool[(urlsPool.indexOf(url) + 1) % urlsPool.length];
        storage.set(key, url);
      }
      return url;
    }

    return window.socketEndPoint;
  },
  pingInterval: function() {
    return this.options.pingDelay + this.averageLag;
  }
};

module.exports = StrongSocket;
<|endoftext|>


<commit_before>/**
 * @Author: robin
 * @Date:   2016-08-18 14:18:18
 * @Email:  xin.lin@qunar.com
* @Last modified by:   robin
* @Last modified time: 2016-09-19 15:21:30
 */
var path = require('path'),
    _ = require('lodash'),
    tar = require('tar'),
    fs = require('fs'),
    fsExtra = require('fs-extra'),
    fstream = require('fstream');

var shellUtils = require('./shellUtils'),
    constant = require('./constant'),
    SPLIT = constant.SPLIT,
    arch = process.arch,
    platform = process.platform,
    v8 = /[0-9]+\.[0-9]+/.exec(process.versions.v8)[0];

var utils = module.exports = {
    /**
     * 获取缓存的路径
     * @return {[type]} [description]
     */
    getCachePath: function() {
        var defaultCacheDirectory = process.env.NPM_CACHE_DIR;
        if (defaultCacheDirectory === undefined) {
            var homeDirectory = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
            if (homeDirectory !== undefined) {
                defaultCacheDirectory = path.resolve(homeDirectory, '.npm_cache_share');
            } else {
                defaultCacheDirectory = path.resolve('/tmp', '.npm_cache_share');
            }
        }
        fsExtra.ensureDirSync(defaultCacheDirectory);
        return defaultCacheDirectory;
    },
    /**
     * 获取存储配置的路径
     * @return {path} path for config
     */
    getConfigPath: function() {
        var file = path.resolve(this.getCachePath(), '../.npm_cache_share_config.json');
        if(!fs.existsSync(file)){
            fsExtra.writeJsonSync(file, {});
        }
        return file;
    },
    /**
     * 获得文件后缀
     * @return {[type]} [description]
     */
    getFileExt: function() {
        return '.tar';
    },
    /**
     * 获取服务器端缓存cache的目录
     * @return {path} [description]
     */
    getServerCachePath: function() {
        var dir = path.resolve(process.cwd(), 'npm_cache_share');
        fsExtra.ensureDirSync(dir);
        return dir;
    },
    /**
     * 将参数序列化
     * @param  {Object} opts       options object
     * @param  {Object}map         white list
     * @return {String}            生成 --option 这样的字符串拼接
     */
    toString: function(opts, map) {
        var ops = [];
        _.each(opts || {}, function(v, k) {
            if (map[k]) {
                ops.push('--' + map[k]);
                if (typeof v != 'boolean') {
                    ops.push(v);
                }
            }
        });
        return ops.join(' ');
    },
    /**
     * 压缩处理
     * @param  {String}   dir      需要压缩的文件夹路径
     * @return {stream}
     */
    compress: function(dir) {
        //错误处理
        function onError(err) {
            console.error(err);
        }

        //压缩结束
        function onEnd() {
            console.info('compress done!');
        }

        var packer = tar.Pack({
                noProprietary: true
            })
            .on('error', onError)
            .on('end', onEnd);

        // This must be a "directory"
        return fstream.Reader({
                path: dir,
                type: "Directory"
            })
            .pipe(packer);
    },
    /**
     * 解压处理
     * @param  {String} target       需要解压的文件
     * @param  {String} dir          解压到的目录
     * @param  {Function} callback   回调
     * @return {void}
     */
    extract: function(target, dir, callback) {
        //错误处理
        function onError(err) {
            callback(err);
        }
        //处理结束
        function onEnd() {
            callback();
        }

        var extractor = tar.Extract({
                path: dir
            })
            .on('error', onError)
            .on('end', onEnd);

        fs.createReadStream(target)
            .on('error', onError)
            .pipe(extractor);
    },
    /**
     * 确保一个文件夹存在并且可写入
     * @param  {Path} dir 文件夹路径
     * @return {void}
     */
    ensureDirWriteablSync: function(dir) {
        try {
            fsExtra.ensureDirSync(dir);
            fs.accessSync(dir, fs.W_OK);
        } catch (e) {
            console.error('Cannnot write into ' + dir + ', Please try running this command again as root/Administrator.');
            throw e;
        }
    },
    /**
     * 获取指定路径下的文件一级目录
     * @param {String} p    指定路径
     * @return {JSON}       文件一级目录
     */
    lsDirectory: function(p) {
        var dMap = {};
        shellUtils.ls(p).forEach(function(file) {
            if (file == constant.LIBNAME) return;
            if (shellUtils.test('-f', path.resolve(p, constant.MODULECHECKER, file))) {
                dMap[file] = 1;
            } else {
                dMap[file] = 0;
            }
        });
        return dMap;
    },
    /**
     * 对比依赖和缓存，返回所需模块
     * @param  {Array} dependencies 模块依赖
     * @param  {JSON} cache        模块缓存
     * @return {JSON}
     */
    compareCache: function(dependencies, cache) {
        var news = [];
        _.forEach(dependencies, function(el){
            if (!cache[utils.getModuleName(el.name, el.version)]
            && !cache[utils.getModuleNameForPlatform(el.name, el.version)]) {
                news.push(el);
            }
        });
        return news;
    },
    /**
     * 递归遍历依赖树
     * @param  {JSON}   tree            依赖树
     * @param  {Function} callback      回调
     * @param  {String} modulePath      模块层次
     * @return {void}
     */
    traverseDependencies: function(tree, callback, modulePath) {
        _.each(tree, function(v, k) {
            callback(v, k, modulePath);
            v.dependencies && utils.traverseDependencies(v.dependencies, callback, modulePath ? path.resolve(modulePath, constant.LIBNAME, k) : k);
        });
    },
    /**
     * 获取平台相关参数
     * @return {String}
     */
    getPlatform: function(){
        return platform + '-' + arch + '-v8-' + v8;
    },
    /**
     * 生成和环境相关的名称
     * @param  {String} name         模块名称
     * @param  {String} version      模块版本
     * @param  {String} platform     指定平台
     * @return {String}
     */
    getModuleNameForPlatform: function(name, version, platform) {
        return [name, version, platform || this.getPlatform()].join('@').replace(RegExp('/', 'g'), SPLIT);
    },
    /**
     * 生成带版本的模块名
     * @param  {String} name         模块名称
     * @param  {String} version      模块版本
     * @param  {Object} dependencies 模块依赖
     * @return {String}
     */
    getModuleName: function(name, version, dependencies, modulePath) {
        //for <= node v0.8 use node-waf to build native programe with wscript file
        //for > node v0.8 use node-gyp to build with binding.gyp
        //see also: https://www.npmjs.com/package/node-gyp
        if ((dependencies && dependencies['node-gyp']) ||
            (modulePath && (test('-f', path.resolve(modulePath, 'binding.gyp')) || test('-f', path.resolve(modulePath, 'wscript'))))) {
            return this.getModuleNameForPlatform(name, version);
        }
        return [name, version].join('@').replace(RegExp('/', 'g'), SPLIT);
    },
    /**
     * 从包含环境与版本的包名称中切分出模块名称
     * @param  {string} name 包名称
     * @return {string}      模块名称
     */
    splitModuleName: function(name){
        // 处理形如下方的包名称
        // name => moduleName
        // five@0.0.1 ＝> five
        // five@0.0.1.tar => five
        // fibers@0.1.0@x64-Linux-v8 => fibers
        // @qnpm@@@Qredis@0.0.1 => @qnpm@@@Qredis
        var arr = name.split('@'), moduleName;
        if(arr[0] === ''){
            moduleName = arr.slice(0,-1).join('@');
        } else {
            moduleName = arr[0];
        }
        return moduleName;
    },
    /**
     * 生成包含环境信息的包名称
     * @param  {string} moduleName 形如five@0.0.1
     * @param  {string} env        形如x64-Linux-v8
     * @return {string}            [description]
     */
    joinPackageName: function(moduleName, env){
        return moduleName + '@' + env;
    },
    /**
     * 遍历树
     * @param  {JSON}   tree        树形结构
     * @param  {Function} callback  回调函数
     * @return {void}
     */
    traverseTree: function(tree, callback) {
        _.each(tree, function(v, i) {
            v.children && v.children.length > 0 && utils.traverseTree(v.children, callback);
            callback(v, i, tree.length);
        });
    },
    /**
     * 遍历dependencies生成数组(按照name和version去重)
     * @param  {JSON} dependencies 树形依赖
     * @return {Array}              生成的打平的依赖数组
     */
    dependenciesTreeToArray: function(dependencies){
        var arr = [],
            map = {};
        this.traverseDependencies(dependencies, function(v, k){
            var full = k.replace(RegExp('/', 'g'), SPLIT) + '@' + v.version;
            if(!map[full]){
                map[full] = 1;
                arr.push({
                    name: k,
                    version: v.version,
                    full: full
                });
            }
        });
        return arr;
    },
    /**
     * map转换成array，只有一级
     * @param  {JSON} jsonObj
     * @return {Array}
     */
    toArrayByKey: function(jsonObj) {
        var arrs = [];
        _.each(jsonObj, function(v, k){
            arrs.push(k);
        });
        return arrs;
    }
};
<commit_msg>fix for replace shelljs<commit_after>/**
 * @Author: robin
 * @Date:   2016-08-18 14:18:18
 * @Email:  xin.lin@qunar.com
* @Last modified by:   robin
* @Last modified time: 2016-09-19 15:21:30
 */
var path = require('path'),
    _ = require('lodash'),
    tar = require('tar'),
    fs = require('fs'),
    fsExtra = require('fs-extra'),
    fstream = require('fstream');

var shellUtils = require('./shellUtils'),
    constant = require('./constant'),
    SPLIT = constant.SPLIT,
    arch = process.arch,
    platform = process.platform,
    v8 = /[0-9]+\.[0-9]+/.exec(process.versions.v8)[0];

var utils = module.exports = {
    /**
     * 获取缓存的路径
     * @return {[type]} [description]
     */
    getCachePath: function() {
        var defaultCacheDirectory = process.env.NPM_CACHE_DIR;
        if (defaultCacheDirectory === undefined) {
            var homeDirectory = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
            if (homeDirectory !== undefined) {
                defaultCacheDirectory = path.resolve(homeDirectory, '.npm_cache_share');
            } else {
                defaultCacheDirectory = path.resolve('/tmp', '.npm_cache_share');
            }
        }
        fsExtra.ensureDirSync(defaultCacheDirectory);
        return defaultCacheDirectory;
    },
    /**
     * 获取存储配置的路径
     * @return {path} path for config
     */
    getConfigPath: function() {
        var file = path.resolve(this.getCachePath(), '../.npm_cache_share_config.json');
        if(!fs.existsSync(file)){
            fsExtra.writeJsonSync(file, {});
        }
        return file;
    },
    /**
     * 获得文件后缀
     * @return {[type]} [description]
     */
    getFileExt: function() {
        return '.tar';
    },
    /**
     * 获取服务器端缓存cache的目录
     * @return {path} [description]
     */
    getServerCachePath: function() {
        var dir = path.resolve(process.cwd(), 'npm_cache_share');
        fsExtra.ensureDirSync(dir);
        return dir;
    },
    /**
     * 将参数序列化
     * @param  {Object} opts       options object
     * @param  {Object}map         white list
     * @return {String}            生成 --option 这样的字符串拼接
     */
    toString: function(opts, map) {
        var ops = [];
        _.each(opts || {}, function(v, k) {
            if (map[k]) {
                ops.push('--' + map[k]);
                if (typeof v != 'boolean') {
                    ops.push(v);
                }
            }
        });
        return ops.join(' ');
    },
    /**
     * 压缩处理
     * @param  {String}   dir      需要压缩的文件夹路径
     * @return {stream}
     */
    compress: function(dir) {
        //错误处理
        function onError(err) {
            console.error(err);
        }

        //压缩结束
        function onEnd() {
            console.info('compress done!');
        }

        var packer = tar.Pack({
                noProprietary: true
            })
            .on('error', onError)
            .on('end', onEnd);

        // This must be a "directory"
        return fstream.Reader({
                path: dir,
                type: "Directory"
            })
            .pipe(packer);
    },
    /**
     * 解压处理
     * @param  {String} target       需要解压的文件
     * @param  {String} dir          解压到的目录
     * @param  {Function} callback   回调
     * @return {void}
     */
    extract: function(target, dir, callback) {
        //错误处理
        function onError(err) {
            callback(err);
        }
        //处理结束
        function onEnd() {
            callback();
        }

        var extractor = tar.Extract({
                path: dir
            })
            .on('error', onError)
            .on('end', onEnd);

        fs.createReadStream(target)
            .on('error', onError)
            .pipe(extractor);
    },
    /**
     * 确保一个文件夹存在并且可写入
     * @param  {Path} dir 文件夹路径
     * @return {void}
     */
    ensureDirWriteablSync: function(dir) {
        try {
            fsExtra.ensureDirSync(dir);
            fs.accessSync(dir, fs.W_OK);
        } catch (e) {
            console.error('Cannnot write into ' + dir + ', Please try running this command again as root/Administrator.');
            throw e;
        }
    },
    /**
     * 获取指定路径下的文件一级目录
     * @param {String} p    指定路径
     * @return {JSON}       文件一级目录
     */
    lsDirectory: function(p) {
        var dMap = {};
        shellUtils.ls(p).forEach(function(file) {
            if (file == constant.LIBNAME) return;
            if (shellUtils.test('-f', path.resolve(p, constant.MODULECHECKER, file))) {
                dMap[file] = 1;
            } else {
                dMap[file] = 0;
            }
        });
        return dMap;
    },
    /**
     * 对比依赖和缓存，返回所需模块
     * @param  {Array} dependencies 模块依赖
     * @param  {JSON} cache        模块缓存
     * @return {JSON}
     */
    compareCache: function(dependencies, cache) {
        var news = [];
        _.forEach(dependencies, function(el){
            if (!cache[utils.getModuleName(el.name, el.version)]
            && !cache[utils.getModuleNameForPlatform(el.name, el.version)]) {
                news.push(el);
            }
        });
        return news;
    },
    /**
     * 递归遍历依赖树
     * @param  {JSON}   tree            依赖树
     * @param  {Function} callback      回调
     * @param  {String} modulePath      模块层次
     * @return {void}
     */
    traverseDependencies: function(tree, callback, modulePath) {
        _.each(tree, function(v, k) {
            callback(v, k, modulePath);
            v.dependencies && utils.traverseDependencies(v.dependencies, callback, modulePath ? path.resolve(modulePath, constant.LIBNAME, k) : k);
        });
    },
    /**
     * 获取平台相关参数
     * @return {String}
     */
    getPlatform: function(){
        return platform + '-' + arch + '-v8-' + v8;
    },
    /**
     * 生成和环境相关的名称
     * @param  {String} name         模块名称
     * @param  {String} version      模块版本
     * @param  {String} platform     指定平台
     * @return {String}
     */
    getModuleNameForPlatform: function(name, version, platform) {
        return [name, version, platform || this.getPlatform()].join('@').replace(RegExp('/', 'g'), SPLIT);
    },
    /**
     * 生成带版本的模块名
     * @param  {String} name         模块名称
     * @param  {String} version      模块版本
     * @param  {Object} dependencies 模块依赖
     * @return {String}
     */
    getModuleName: function(name, version, dependencies, modulePath) {
        //for <= node v0.8 use node-waf to build native programe with wscript file
        //for > node v0.8 use node-gyp to build with binding.gyp
        //see also: https://www.npmjs.com/package/node-gyp
        if ((dependencies && dependencies['node-gyp']) ||
            (modulePath && (shellUtils.test('-f', path.resolve(modulePath, 'binding.gyp')) || shellUtils.test('-f', path.resolve(modulePath, 'wscript'))))) {
            return this.getModuleNameForPlatform(name, version);
        }
        return [name, version].join('@').replace(RegExp('/', 'g'), SPLIT);
    },
    /**
     * 从包含环境与版本的包名称中切分出模块名称
     * @param  {string} name 包名称
     * @return {string}      模块名称
     */
    splitModuleName: function(name){
        // 处理形如下方的包名称
        // name => moduleName
        // five@0.0.1 ＝> five
        // five@0.0.1.tar => five
        // fibers@0.1.0@x64-Linux-v8 => fibers
        // @qnpm@@@Qredis@0.0.1 => @qnpm@@@Qredis
        var arr = name.split('@'), moduleName;
        if(arr[0] === ''){
            moduleName = arr.slice(0,-1).join('@');
        } else {
            moduleName = arr[0];
        }
        return moduleName;
    },
    /**
     * 生成包含环境信息的包名称
     * @param  {string} moduleName 形如five@0.0.1
     * @param  {string} env        形如x64-Linux-v8
     * @return {string}            [description]
     */
    joinPackageName: function(moduleName, env){
        return moduleName + '@' + env;
    },
    /**
     * 遍历树
     * @param  {JSON}   tree        树形结构
     * @param  {Function} callback  回调函数
     * @return {void}
     */
    traverseTree: function(tree, callback) {
        _.each(tree, function(v, i) {
            v.children && v.children.length > 0 && utils.traverseTree(v.children, callback);
            callback(v, i, tree.length);
        });
    },
    /**
     * 遍历dependencies生成数组(按照name和version去重)
     * @param  {JSON} dependencies 树形依赖
     * @return {Array}              生成的打平的依赖数组
     */
    dependenciesTreeToArray: function(dependencies){
        var arr = [],
            map = {};
        this.traverseDependencies(dependencies, function(v, k){
            var full = k.replace(RegExp('/', 'g'), SPLIT) + '@' + v.version;
            if(!map[full]){
                map[full] = 1;
                arr.push({
                    name: k,
                    version: v.version,
                    full: full
                });
            }
        });
        return arr;
    },
    /**
     * map转换成array，只有一级
     * @param  {JSON} jsonObj
     * @return {Array}
     */
    toArrayByKey: function(jsonObj) {
        var arrs = [];
        _.each(jsonObj, function(v, k){
            arrs.push(k);
        });
        return arrs;
    }
};
<|endoftext|>


<commit_before>/**
 *
 * Link Style (JS)
 *
 * @author Takuto Yanagida @ Space-Time Inc.
 * @version 2019-06-06
 *
 * The function 'isExternalUrl' can be overwritten as follows:
 * <script>ST.isExternalUrl = function (url) { return true; }</script>
 *
 */

window.ST = window['ST'] || {};

(function (NS) {

	const TARGET_SELECTOR = '.stile';
	const TARGET_SELECTOR_ANCHOR = '.stile-anchor';
	const TARGET_SELECTOR_ANCHOR_EXTERNAL = '.stile-anchor-external';

	const PERMITTED_CLASSES = ['alignleft', 'aligncenter', 'alignright', 'size-thumbnail', 'size-small', 'size-medium-small', 'size-medium', 'size-medium-large', 'size-medium_large', 'size-large', 'size-full'];
	const EXT_TABLE = { doc: 'word', docx: 'word', xls: 'excel', xlsx: 'excel', ppt: 'powerpoint', pptx: 'powerpoint', pdf: 'pdf' };

	NS.addInitializer(2, () => {
		let as = document.querySelectorAll(TARGET_SELECTOR + ' a');
		modifyAnchorStyle(as);
		as = document.querySelectorAll(TARGET_SELECTOR_ANCHOR + ' a');
		modifyAnchorStyle(as);
		as = document.querySelectorAll(TARGET_SELECTOR_ANCHOR_EXTERNAL + ' a');
		modifyAnchorStyleExternal(as);
	});

	// -------------------------------------------------------------------------

	function modifyAnchorStyle(as) {
		for (let i = 0; i < as.length; i += 1) {
			const a = as[i];
			if (isImageLink(a)) {
				NS.addStile(a, 'link-image');
				continue;
			}
			if (isEmpty(a)) {
				const url = a.getAttribute('href');
				if (isExternal(url)) NS.addStile(a, 'link-external');
				continue;
			}
			if (!isSimple(a)) continue;
			NS.addStile(a, 'link-simple');
			const url = a.getAttribute('href');
			if (isUrlLink(a, url)) {
				NS.addStile(a, 'link-url');
			}
			if (isExternal(url)) {
				NS.addStile(a, 'link-external');
			} else if (isAnchor(url)) {
				NS.addStile(a, 'link-anchor');
			}
			addFileType(a);
		}
	}

	function modifyAnchorStyleExternal(as) {
		for (let i = 0; i < as.length; i += 1) {
			const a = as[i];
			const url = a.getAttribute('href');
			if (isExternal(url)) {
				NS.addStile(a, 'link-external');
			}
		}
	}

	// -------------------------------------------------------------------------

	function isAnchor(url) {
		const pos = url.indexOf('#');
		if (pos === -1) return false;
		const id = url.substr(pos + 1);
		const tar = document.getElementById(id);
		return tar !== null;
	}

	function isSimple(a) {
		if (a.className) return false;
		const cs = a.childNodes;
		if (cs.length === 0) return false;
		for (let i = 0; i < cs.length; i += 1) {
			if (cs[i].className) return false;
		}
		for (let i = 0; i < cs.length; i += 1) {
			const tn = cs[i].tagName;
			if (tn === 'BR') continue;
			if (tn && isInlineElement(cs[i])) continue;
			if (tn) return false;
		}
		return true;
	}

	function isEmpty(a) {
		if (a.className) return false;
		const cs = a.childNodes;
		return (cs.length === 0);
	}

	function isInlineElement(elm) {
		const d = getComputedStyle(elm).display;
		return d.indexOf('inline') !== -1;
	}

	function isUrlLink(a, url) {
		const cs = a.childNodes;
		if (cs.length === 0) return false;
		return a.innerHTML.trim() === url;
	}

	function isImageLink(a) {
		if (a.className) {
			const cs = a.className.split(' ');
			for (let i = 0; i < cs.length; i += 1) {
				if (PERMITTED_CLASSES.indexOf(cs[i]) === -1) return false;
			}
		}
		const cs = a.childNodes;
		if (cs.length === 0) return false;
		let success = false;
		for (let i = 0; i < cs.length; i += 1) {
			const tn = cs[i].tagName;
			if (success === false && tn === 'IMG') {
				success = true;
				continue;
			}
			if (tn) return false;
		}
		return success;
	}

	// -------------------------------------------------------------------------

	// Private function
	function isExternal(url) { return NS.isExternalUrl(url); }

	// Exported function
	function isExternalUrl(url) {
		if (url === null || url === '') return false;
		if (url.indexOf(location.protocol + '//' + location.host) === 0) return false;
		if (url.match(/^https?:\/\//)) return true;
		if (url.match(/^\/\//)) return true;
		return false;
	}

	// Export the function
	NS.isExternalUrl = isExternalUrl;

	// -------------------------------------------------------------------------

	function addFileType(a) {
		let url = a.getAttribute('href');
		if (url.length > 0 && url[url.length - 1] === '/') return;
		const dom = url.indexOf('//');
		if (dom !== -1) {
			const fs = url.indexOf('/', dom + 2);
			if (fs === -1) return;
			url = url.substr(fs + 1);
		}
		const idx = url.lastIndexOf('.');
		if (idx === -1) return;
		const ext = url.substr(idx + 1);

		const type = EXT_TABLE[ext];
		if (type) {
			NS.addStile(a, 'link-file');
			NS.addStile(a, 'link-file-' + type);
		}
	}

})(window.ST);
<commit_msg>Refactoring and minor improvement<commit_after>/**
 *
 * Link Style (JS)
 *
 * @author Takuto Yanagida @ Space-Time Inc.
 * @version 2019-06-07
 *
 * The function 'isExternalUrl' can be overwritten as follows:
 * <script>ST.isExternalUrl = function (url) { return true; }</script>
 *
 */

window.ST = window['ST'] || {};

(function (NS) {

	const SEL_TARGET = '.stile';
	const SEL_TARGET_ANCHOR = '.stile-anchor';
	const SEL_TARGET_ANCHOR_EXTERNAL = '.stile-anchor-external';

	const PERMITTED_CLASSES = ['alignleft', 'aligncenter', 'alignright', 'size-thumbnail', 'size-small', 'size-medium-small', 'size-medium', 'size-medium-large', 'size-medium_large', 'size-large', 'size-full'];
	const EXT_TABLE = { doc: 'word', docx: 'word', xls: 'excel', xlsx: 'excel', ppt: 'powerpoint', pptx: 'powerpoint', pdf: 'pdf' };

	NS.addInitializer(2, () => {
		let as = document.querySelectorAll(SEL_TARGET + ' a');
		modifyAnchorStyle(as);
		as = document.querySelectorAll(SEL_TARGET_ANCHOR + ' a');
		modifyAnchorStyle(as);
		as = document.querySelectorAll(SEL_TARGET_ANCHOR_EXTERNAL + ' a');
		modifyAnchorStyleExternal(as);
	});

	// -------------------------------------------------------------------------

	function modifyAnchorStyle(as) {
		for (let i = 0; i < as.length; i += 1) {
			const a = as[i];
			if (isImageLink(a)) {
				NS.addStile(a, 'link-image');
				continue;
			}
			if (isEmpty(a)) {
				const url = a.getAttribute('href');
				if (isExternal(url)) NS.addStile(a, 'link-external');
				continue;
			}
			if (isSimple(a)) {
				NS.addStile(a, 'link-simple');

				const url = a.getAttribute('href');
				if (isAnchor(url)) {
					NS.addStile(a, 'link-anchor');
				} else {
					if (isUrlLink(a, url)) NS.addStile(a, 'link-url');
					if (isExternal(url)) NS.addStile(a, 'link-external');
				}
				addFileType(a, url);
			}
		}
	}

	function modifyAnchorStyleExternal(as) {
		for (let i = 0; i < as.length; i += 1) {
			const a = as[i];
			const url = a.getAttribute('href');
			if (isExternal(url)) {
				NS.addStile(a, 'link-external');
			}
		}
	}

	// -------------------------------------------------------------------------

	function isImageLink(a) {
		if (a.className) {
			const cs = a.className.split(' ');
			for (let i = 0; i < cs.length; i += 1) {
				if (PERMITTED_CLASSES.indexOf(cs[i]) === -1) return false;
			}
		}
		const cs = a.childNodes;
		if (cs.length === 0) return false;
		let success = false;
		for (let i = 0; i < cs.length; i += 1) {
			const tn = cs[i].tagName;
			if (success === false && tn === 'IMG') {
				success = true;
				continue;
			}
			if (tn) return false;
		}
		return success;
	}

	function isEmpty(a) {
		if (a.className) return false;
		const cs = a.childNodes;
		return (cs.length === 0);
	}

	function isSimple(a) {
		if (a.className) return false;
		const cs = a.childNodes;
		if (cs.length === 0) return false;
		for (let i = 0; i < cs.length; i += 1) {
			if (cs[i].className) return false;
		}
		for (let i = 0; i < cs.length; i += 1) {
			const tn = cs[i].tagName;
			if (tn === 'BR') continue;
			if (tn && isInlineElement(cs[i])) continue;
			if (tn) return false;
		}
		return true;
	}

	function isAnchor(url) {
		const pos = url.indexOf('#');
		if (pos === -1) return false;
		const id = url.substr(pos + 1);
		const tar = document.getElementById(id);
		return tar !== null;
	}

	function isUrlLink(a, url) {
		const cs = a.childNodes;
		if (cs.length === 0) return false;
		return a.innerHTML.trim() === url;
	}

	function isInlineElement(elm) {
		const d = getComputedStyle(elm).display;
		return d.indexOf('inline') !== -1;
	}

	// -------------------------------------------------------------------------

	// Private function
	function isExternal(url) { return NS.isExternalUrl(url); }

	// Exported function
	function isExternalUrl(url) {
		if (url === null || url === '') return false;
		if (url.indexOf(location.protocol + '//' + location.host) === 0) return false;
		if (url.match(/^https?:\/\//)) return true;
		if (url.match(/^\/\//)) return true;
		return false;
	}

	// Export the function
	NS.isExternalUrl = isExternalUrl;

	// -------------------------------------------------------------------------

	function addFileType(a, url) {
		if (url.length > 0 && url[url.length - 1] === '/') return;
		const dom = url.indexOf('//');
		if (dom !== -1) {
			const fs = url.indexOf('/', dom + 2);
			if (fs === -1) return;
			url = url.substr(fs + 1);
		}
		const idx = url.lastIndexOf('.');
		if (idx === -1) return;
		const ext = url.substr(idx + 1);

		const type = EXT_TABLE[ext];
		if (type) {
			NS.addStile(a, 'link-file');
			NS.addStile(a, 'link-file-' + type);
		}
	}

})(window.ST);
<|endoftext|>


<commit_before>/* Flash a message. */
function flash(message, type, scrollToTop) {
	var $container = $('#messages');
	var $message = $('

');

    if (!type) {
        type = 'info';
    }

    if (type === 'error') {
        type = 'danger';
    }

	$message.addClass('alert').addClass('alert-' + type);
	$message.text(message);
	$message.hide();

	$container.prepend($message);
	$message.slideDown('slow');

	if (scrollToTop !== false)
		scrollToTop();
}

/* Clear the message area. */
function clearflash() {
	$('#messages .alert').slideUp('slow', function() {
		$(this).remove();
	});
}

/* Move up to the flash container if any messages were flashed. */
$(function() {
	if ($('#messages > div.alert').length > 0) {
		scrollToTop();
	}
});

/* Scroll to the message box. */
function scrollToTop() {
	$('html, body').animate({
		scrollTop: $('body').offset().top
	}, 'fast');
}
<commit_msg>Fix scroll to top when flash<commit_after>/* Flash a message. */
function flash(message, type, scroll) {
	var $container = $('#messages');
	var $message = $('

');

    if (!type) {
        type = 'info';
    }

    if (type === 'error') {
        type = 'danger';
    }

	$message.addClass('alert').addClass('alert-' + type);
	$message.text(message);
	$message.hide();

	$container.prepend($message);
	$message.slideDown('slow');

	if (scroll !== false)
		scrollToTop();
}

/* Clear the message area. */
function clearflash() {
	$('#messages .alert').slideUp('slow', function() {
		$(this).remove();
	});
}

/* Move up to the flash container if any messages were flashed. */
$(function() {
	if ($('#messages > div.alert').length > 0) {
		scrollToTop();
	}
});

/* Scroll to the message box. */
function scrollToTop() {
	$('html, body').animate({
		scrollTop: $('body').offset().top
	}, 'fast');
}
<|endoftext|>


<commit_before>/*global RuleStorage, Utils, jQuery */
/*eslint no-new-func:0*/
"use strict";

/* A single Rule */
var Rule = function() {
  this.prettyPrint = function() {
    var clone = jQuery.extend({}, this);
    delete clone.matcher;
    return JSON.stringify(clone, null, 2);
  };

  this.prettyPrintHtml = function() {
    return this.prettyPrint().replace(/\n/,"
");
  };
};

Rule.create = function(options) {
  var rule = new Rule();
  Object.keys(options).forEach(function(key) {
    rule[key] = options[key];
  });
  rule.matcher = new RegExp(rule.url);
  return rule;
};

/* Multiple Rules */
var Rules = {
  cache: null,
  ruleCount: 0,
  matchesForUrl: function(url) {
    var rules = this;
    return new Promise(function (resolve) {
      rules.load().then(function(rules) {
        var matchingRules = rules.filter(function (rule) {
          return url.match(rule.matcher);
        });
        resolve(matchingRules);
      });
    });
  },
  load: function() {
    var that = this;
    return new Promise(function (resolve) {
      if(that.cache) {
        Utils.log("Rules.load resolved using " + that.cache.length + " cache entries");
        resolve(that.cache);
      }
      RuleStorage.loadRules().then(function (rulesCode) {
        var rules = [];
        if(rulesCode) {
          // remove wrapper
          var rulesCodeMatches = rulesCode.match(/^.*?(\[[\s\S]*\];)$/m);

          // This should not happen:
          if(!rulesCodeMatches[1]) {
            resolve(rules);
          }

          // Now we go into hell ...
          // TODO: remove unsafe-eval from manifest and use a chrome sandbox iframe
          var ruleCode = "return " + rulesCodeMatches[1].replace(/\\n/g,"");
          var ruleFunction = new Function(ruleCode);
          that.ruleCount = 0;

          rules = ruleFunction().map(function (ruleJson) {
            that.ruleCount += 1;
            return Rule.create(ruleJson);
          });
        }
        that.cache = rules;
        resolve(rules);
      });
    });
  }
};

<commit_msg>add Rules.format()<commit_after>/*global RuleStorage, Utils, jQuery, js_beautify */
/*eslint no-new-func:0*/
"use strict";

/* A single Rule */
var Rule = function() {
  this.prettyPrint = function() {
    var clone = jQuery.extend({}, this);
    delete clone.matcher;
    return JSON.stringify(clone, null, 2);
  };

  this.prettyPrintHtml = function() {
    return this.prettyPrint().replace(/\n/,"
");
  };
};

Rule.create = function(options) {
  var rule = new Rule();
  Object.keys(options).forEach(function(key) {
    rule[key] = options[key];
  });
  rule.matcher = new RegExp(rule.url);
  return rule;
};

/* Multiple Rules */
var Rules = {
  cache: null,
  ruleCount: 0,
  matchesForUrl: function(url) {
    var rules = this;
    return new Promise(function (resolve) {
      rules.load().then(function(rules) {
        var matchingRules = rules.filter(function (rule) {
          return url.match(rule.matcher);
        });
        resolve(matchingRules);
      });
    });
  },
  load: function() {
    var that = this;
    return new Promise(function (resolve) {
      if(that.cache) {
        Utils.log("Rules.load resolved using " + that.cache.length + " cache entries");
        resolve(that.cache);
      }
      RuleStorage.loadRules().then(function (rulesCode) {
        var rules = [];
        if(rulesCode) {
          // remove wrapper
          var rulesCodeMatches = rulesCode.match(/^.*?(\[[\s\S]*\];)$/m);

          // This should not happen:
          if(!rulesCodeMatches[1]) {
            resolve(rules);
          }

          // Now we go into hell ...
          // TODO: remove unsafe-eval from manifest and use a chrome sandbox iframe
          var ruleCode = "return " + rulesCodeMatches[1].replace(/\\n/g,"");
          var ruleFunction = new Function(ruleCode);
          that.ruleCount = 0;

          rules = ruleFunction().map(function (ruleJson) {
            that.ruleCount += 1;
            return Rule.create(ruleJson);
          });
        }
        that.cache = rules;
        resolve(rules);
      });
    });
  },
  format: function(rulesCodeString) {
    // Prettify code a little
    var prettyCode = js_beautify(rulesCodeString, {
      "indent_size": 2,
      "indent_char": " ",
      "preserve_newlines": false,
      "brace_style": "expand",
      "space_before_conditional": true,
      "unescape_strings": false
    });
    if(/\}\];$/.test(prettyCode)) {
      prettyCode = prettyCode.replace(/\}\];$/, "}\n];");
    }
    return prettyCode;
  }
};

<|endoftext|>