<commit_before>/**
 * 简单选色控件
 *
 * Created by GUY on 2015/11/16.
 * @class BI.ColorPickerEditor
 * @extends BI.Widget
 */
BI.HexColorPickerEditor = BI.inherit(BI.Widget, {

    constants: {
        RGB_WIDTH: 36,
        HEX_WIDTH: 70,
        HEX_PREFIX_POSITION: 1
    },

    props: {
        baseCls: "bi-color-picker-editor",
        height: 30
    },

    render: function () {
        var self = this, o = this.options, c = this.constants;
        this.storeValue = {};
        var RGB = BI.createItems([{text: "R"}, {text: "G"}, {text: "B"}], {
            type: "bi.label",
            cls: "color-picker-editor-label",
            height: 20
        });

        var checker = function (v) {
            return BI.isNumeric(v) && (v | 0) >= 0 && (v | 0) <= 255;
        };
        var Ws = BI.map(BI.range(0, 3), function () {
            return {
                type: "bi.small_text_editor",
                cls: "color-picker-editor-input",
                validationChecker: checker,
                errorText: BI.i18nText("BI-Color_Picker_Error_Text"),
                allowBlank: true,
                value: 255,
                width: c.RGB_WIDTH,
                height: 20,
                listeners: [{
                    eventName: BI.TextEditor.EVENT_CHANGE,
                    action: function () {
                        self._checkEditors();
                        if (checker(self.storeValue.r) && checker(self.storeValue.g) && checker(self.storeValue.b)) {
                            self.colorShow.element.css("background-color", self.getValue());
                            self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
                        }
                    }
                }]
            };
        });

        return {
            type: "bi.absolute",
            items: [{
                el: {
                    type: "bi.vertical",
                    tgap: 10,
                    items: [{
                        type: 'bi.vertical_adapt',
                        columnSize: [0.5, 'fill'],
                        height: 24,
                        items: [{
                            type: "bi.color_picker_show_button",
                            cls: "trans-color-icon",
                            height: 22,
                            title: BI.i18nText("BI-Transparent_Color"),
                            text: BI.i18nText("BI-Transparent_Color"),
                            listeners: [{
                                eventName: BI.ColorChooserShowButton.EVENT_CHANGE,
                                action: function () {
                                    if (this.isSelected()) {
                                        self.lastColor = self.getValue();
                                        self.setValue("transparent");
                                    } else {
                                        if (self.lastColor === "transparent") {
                                            self.lastColor = "";
                                        }
                                        self.setValue(self.lastColor || "#ffffff");
                                    }
                                    if ((self.R.isValid() && self.G.isValid() && self.B.isValid()) ||
                                        self._isEmptyRGB()) {
                                        self.colorShow.element.css("background-color", self.getValue());
                                        self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
                                    }
                                }
                            }],
                            ref: function (_ref) {
                                self.transparent = _ref;
                            }
                        }, {
                            el: {
                                type: "bi.color_picker_show_button",
                                cls: "auto-color-icon",
                                height: 22,
                                title: BI.i18nText("BI-Basic_Auto"),
                                text: BI.i18nText("BI-Basic_Auto"),
                                listeners: [{
                                    eventName: BI.ColorChooserShowButton.EVENT_CHANGE,
                                    action: function () {
                                        if (this.isSelected()) {
                                            self.lastColor = self.getValue();
                                            self.setValue("");
                                        } else {
                                            self.setValue(self.lastColor || "#ffffff");
                                        }
                                        if ((self.R.isValid() && self.G.isValid() && self.B.isValid()) || self._isEmptyRGB()) {
                                            self.colorShow.element.css("background-color", self.getValue());
                                            self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
                                        }
                                    }
                                }],
                                ref: function (_ref) {
                                    self.none = _ref;
                                }
                            },
                            lgap: 10,
                        }]
                    }, {
                        type: "bi.vertical_adapt",
                        rgap: 5,
                        items: [{
                            el: {
                                type: "bi.layout",
                                cls: "color-picker-editor-display bi-card bi-border",
                                height: 16,
                                width: 16,
                                ref: function (_ref) {
                                    self.colorShow = _ref;
                                }
                            },
                            width: 16
                        }, {
                            type: "bi.label",
                            text: "#",
                            width: 10
                        }, {
                            type: "bi.small_text_editor",
                            ref: function (_ref) {
                                self.hexEditor = _ref;
                            },
                            cls: "color-picker-editor-input",
                            validationChecker: this._hexChecker,
                            allowBlank: true,
                            errorText: BI.i18nText("BI-Color_Picker_Error_Text_Hex"),
                            width: c.HEX_WIDTH,
                            height: 20,
                            listeners: [{
                                eventName: "EVENT_CHANGE",
                                action: function () {
                                    self._checkHexEditor();
                                    if (checker(self.storeValue.r) && checker(self.storeValue.g) && checker(self.storeValue.b)) {
                                        self.colorShow.element.css("background-color", self.getValue());
                                        self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
                                    }

                                }
                            }]
                        }, RGB[0], {
                            el: BI.extend(Ws[0], {
                                ref: function (_ref) {
                                    self.R = _ref
                                }
                            }),
                            width: c.RGB_WIDTH
                        }, RGB[1], {
                            el: BI.extend(Ws[1], {
                                ref: function (_ref) {
                                    self.G = _ref
                                }
                            }),
                            width: c.RGB_WIDTH
                        }, RGB[2], {
                            el: BI.extend(Ws[2], {
                                ref: function (_ref) {
                                    self.B = _ref
                                }
                            }),
                            width: c.RGB_WIDTH
                        }]
                    }]
                },
                left: 0,
                right: 0,
                top: 0,
                bottom: 0
            }]
        };
    },

    _hexChecker: function (v) {
        return /^[0-9a-fA-F]{6}$/.test(v);
    },

    _checkEditors: function () {
        if(BI.isEmptyString(this.R.getValue())) {
            this.R.setValue(0);
        }
        if(BI.isEmptyString(this.G.getValue())) {
            this.G.setValue(0);
        }
        if(BI.isEmptyString(this.B.getValue())) {
            this.B.setValue(0);
        }
        this.storeValue = {
            r: this.R.getValue() || 0,
            g: this.G.getValue() || 0,
            b: this.B.getValue() || 0
        };
        this.hexEditor.setValue(this.getValue().slice(this.constants.HEX_PREFIX_POSITION));
    },

    _isEmptyRGB: function () {
        return BI.isEmptyString(this.storeValue.r) && BI.isEmptyString(this.storeValue.g) && BI.isEmptyString(this.storeValue.b);
    },

    _checkHexEditor: function () {
        if (BI.isEmptyString(this.hexEditor.getValue())) {
            this.hexEditor.setValue("000000");
        }
        var json = BI.DOM.rgb2json(BI.DOM.hex2rgb("#" + this.hexEditor.getValue()));
        this.storeValue = {
            r: json.r || 0,
            g: json.g || 0,
            b: json.b || 0,
        };
        this.R.setValue(this.storeValue.r);
        this.G.setValue(this.storeValue.g);
        this.B.setValue(this.storeValue.b);
    },

    _showPreColor: function (color) {
        if (color === "") {
            this.colorShow.element.css("background-color", "").removeClass("trans-color-background").addClass("auto-color-normal-background");
        } else if (color === "transparent") {
            this.colorShow.element.css("background-color", "").removeClass("auto-color-normal-background").addClass("trans-color-background");
        } else {
            this.colorShow.element.css({"background-color": color}).removeClass("auto-color-normal-background").removeClass("trans-color-background");
        }
    },

    _setEnable: function (enable) {
        BI.ColorPickerEditor.superclass._setEnable.apply(this, arguments);
        if (enable === true) {
            this.element.removeClass("base-disabled disabled");
        } else if (enable === false) {
            this.element.addClass("base-disabled disabled");
        }
    },

    setValue: function (color) {
        if (color === "transparent") {
            this.transparent.setSelected(true);
            this.none.setSelected(false);
            this._showPreColor("transparent");
            this.R.setValue("");
            this.G.setValue("");
            this.B.setValue("");
            this.hexEditor.setValue("");
            this.storeValue = {
                r: "",
                g: "",
                b: ""
            };
            return;
        }
        if (!color) {
            color = "";
            this.none.setSelected(true);
        } else {
            this.none.setSelected(false);
        }
        this.transparent.setSelected(false);
        this._showPreColor(color);
        var json = BI.DOM.rgb2json(BI.DOM.hex2rgb(color));
        this.storeValue = {
            r: BI.isNull(json.r) ? "" : json.r,
            g: BI.isNull(json.g) ? "" : json.g,
            b: BI.isNull(json.b) ? "" : json.b
        };
        this.R.setValue(this.storeValue.r);
        this.G.setValue(this.storeValue.g);
        this.B.setValue(this.storeValue.b);
        this.hexEditor.setValue(color.slice(this.constants.HEX_PREFIX_POSITION));
    },

    getValue: function () {
        if (this._isEmptyRGB() && this.transparent.isSelected()) {
            return "transparent";
        }
        return BI.DOM.rgb2hex(BI.DOM.json2rgb({
            r: this.storeValue.r,
            g: this.storeValue.g,
            b: this.storeValue.b
        }));
    }
});
BI.HexColorPickerEditor.EVENT_CHANGE = "EVENT_CHANGE";
BI.shortcut("bi.hex_color_picker_editor", BI.HexColorPickerEditor);<commit_msg>Pull request #2094: 无JIRA任务 颜色选择器popup自适应布局<commit_after>/**
 * 简单选色控件
 *
 * Created by GUY on 2015/11/16.
 * @class BI.ColorPickerEditor
 * @extends BI.Widget
 */
BI.HexColorPickerEditor = BI.inherit(BI.Widget, {

    constants: {
        RGB_WIDTH: 36,
        HEX_WIDTH: 70,
        HEX_PREFIX_POSITION: 1
    },

    props: {
        baseCls: "bi-color-picker-editor",
        height: 30
    },

    render: function () {
        var self = this, o = this.options, c = this.constants;
        this.storeValue = {};
        var RGB = BI.createItems([{text: "R"}, {text: "G"}, {text: "B"}], {
            type: "bi.label",
            cls: "color-picker-editor-label",
            height: 20
        });

        var checker = function (v) {
            return BI.isNumeric(v) && (v | 0) >= 0 && (v | 0) <= 255;
        };
        var Ws = BI.map(BI.range(0, 3), function () {
            return {
                type: "bi.small_text_editor",
                cls: "color-picker-editor-input",
                validationChecker: checker,
                errorText: BI.i18nText("BI-Color_Picker_Error_Text"),
                allowBlank: true,
                value: 255,
                width: c.RGB_WIDTH,
                height: 20,
                listeners: [{
                    eventName: BI.TextEditor.EVENT_CHANGE,
                    action: function () {
                        self._checkEditors();
                        if (checker(self.storeValue.r) && checker(self.storeValue.g) && checker(self.storeValue.b)) {
                            self.colorShow.element.css("background-color", self.getValue());
                            self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
                        }
                    }
                }]
            };
        });

        return {
            type: "bi.absolute",
            items: [{
                el: {
                    type: "bi.vertical",
                    tgap: 10,
                    items: [{
                        type: 'bi.vertical_adapt',
                        columnSize: [0.5, 'fill'],
                        height: 24,
                        items: [{
                            type: "bi.color_picker_show_button",
                            cls: "trans-color-icon",
                            height: 22,
                            title: BI.i18nText("BI-Transparent_Color"),
                            text: BI.i18nText("BI-Transparent_Color"),
                            listeners: [{
                                eventName: BI.ColorChooserShowButton.EVENT_CHANGE,
                                action: function () {
                                    if (this.isSelected()) {
                                        self.lastColor = self.getValue();
                                        self.setValue("transparent");
                                    } else {
                                        if (self.lastColor === "transparent") {
                                            self.lastColor = "";
                                        }
                                        self.setValue(self.lastColor || "#ffffff");
                                    }
                                    if ((self.R.isValid() && self.G.isValid() && self.B.isValid()) ||
                                        self._isEmptyRGB()) {
                                        self.colorShow.element.css("background-color", self.getValue());
                                        self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
                                    }
                                }
                            }],
                            ref: function (_ref) {
                                self.transparent = _ref;
                            }
                        }, {
                            el: {
                                type: "bi.color_picker_show_button",
                                cls: "auto-color-icon",
                                height: 22,
                                title: BI.i18nText("BI-Basic_Auto"),
                                text: BI.i18nText("BI-Basic_Auto"),
                                listeners: [{
                                    eventName: BI.ColorChooserShowButton.EVENT_CHANGE,
                                    action: function () {
                                        if (this.isSelected()) {
                                            self.lastColor = self.getValue();
                                            self.setValue("");
                                        } else {
                                            self.setValue(self.lastColor || "#ffffff");
                                        }
                                        if ((self.R.isValid() && self.G.isValid() && self.B.isValid()) || self._isEmptyRGB()) {
                                            self.colorShow.element.css("background-color", self.getValue());
                                            self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
                                        }
                                    }
                                }],
                                ref: function (_ref) {
                                    self.none = _ref;
                                }
                            },
                            lgap: 10,
                        }]
                    }, {
                        el: {
                            type: "bi.vertical_adapt",
                            columnSize: [16, 10, 'fill', 12, c.RGB_WIDTH, 12, c.RGB_WIDTH, 12, c.RGB_WIDTH],

                            rgap: 5,
                            items: [{
                                el: {
                                    type: "bi.layout",
                                    cls: "color-picker-editor-display bi-card bi-border",
                                    height: 16,
                                    width: 16,
                                    ref: function (_ref) {
                                        self.colorShow = _ref;
                                    }
                                },
                                width: 16
                            }, {
                                type: "bi.label",
                                text: "#",
                                width: 10
                            }, {
                                type: "bi.small_text_editor",
                                ref: function (_ref) {
                                    self.hexEditor = _ref;
                                },
                                cls: "color-picker-editor-input",
                                validationChecker: this._hexChecker,
                                allowBlank: true,
                                errorText: BI.i18nText("BI-Color_Picker_Error_Text_Hex"),
                                width: c.HEX_WIDTH,
                                height: 20,
                                listeners: [{
                                    eventName: "EVENT_CHANGE",
                                    action: function () {
                                        self._checkHexEditor();
                                        if (checker(self.storeValue.r) && checker(self.storeValue.g) && checker(self.storeValue.b)) {
                                            self.colorShow.element.css("background-color", self.getValue());
                                            self.fireEvent(BI.ColorPickerEditor.EVENT_CHANGE);
                                        }

                                    }
                                }]
                            }, RGB[0], {
                                el: BI.extend(Ws[0], {
                                    ref: function (_ref) {
                                        self.R = _ref
                                    }
                                }),
                                width: c.RGB_WIDTH
                            }, RGB[1], {
                                el: BI.extend(Ws[1], {
                                    ref: function (_ref) {
                                        self.G = _ref
                                    }
                                }),
                                width: c.RGB_WIDTH
                            }, RGB[2], {
                                el: BI.extend(Ws[2], {
                                    ref: function (_ref) {
                                        self.B = _ref
                                    }
                                }),
                                rgap: -5,
                                width: c.RGB_WIDTH
                            }]
                        }
                    }]
                },
                left: 0,
                right: 0,
                top: 0,
                bottom: 0
            }]
        };
    },

    _hexChecker: function (v) {
        return /^[0-9a-fA-F]{6}$/.test(v);
    },

    _checkEditors: function () {
        if(BI.isEmptyString(this.R.getValue())) {
            this.R.setValue(0);
        }
        if(BI.isEmptyString(this.G.getValue())) {
            this.G.setValue(0);
        }
        if(BI.isEmptyString(this.B.getValue())) {
            this.B.setValue(0);
        }
        this.storeValue = {
            r: this.R.getValue() || 0,
            g: this.G.getValue() || 0,
            b: this.B.getValue() || 0
        };
        this.hexEditor.setValue(this.getValue().slice(this.constants.HEX_PREFIX_POSITION));
    },

    _isEmptyRGB: function () {
        return BI.isEmptyString(this.storeValue.r) && BI.isEmptyString(this.storeValue.g) && BI.isEmptyString(this.storeValue.b);
    },

    _checkHexEditor: function () {
        if (BI.isEmptyString(this.hexEditor.getValue())) {
            this.hexEditor.setValue("000000");
        }
        var json = BI.DOM.rgb2json(BI.DOM.hex2rgb("#" + this.hexEditor.getValue()));
        this.storeValue = {
            r: json.r || 0,
            g: json.g || 0,
            b: json.b || 0,
        };
        this.R.setValue(this.storeValue.r);
        this.G.setValue(this.storeValue.g);
        this.B.setValue(this.storeValue.b);
    },

    _showPreColor: function (color) {
        if (color === "") {
            this.colorShow.element.css("background-color", "").removeClass("trans-color-background").addClass("auto-color-normal-background");
        } else if (color === "transparent") {
            this.colorShow.element.css("background-color", "").removeClass("auto-color-normal-background").addClass("trans-color-background");
        } else {
            this.colorShow.element.css({"background-color": color}).removeClass("auto-color-normal-background").removeClass("trans-color-background");
        }
    },

    _setEnable: function (enable) {
        BI.ColorPickerEditor.superclass._setEnable.apply(this, arguments);
        if (enable === true) {
            this.element.removeClass("base-disabled disabled");
        } else if (enable === false) {
            this.element.addClass("base-disabled disabled");
        }
    },

    setValue: function (color) {
        if (color === "transparent") {
            this.transparent.setSelected(true);
            this.none.setSelected(false);
            this._showPreColor("transparent");
            this.R.setValue("");
            this.G.setValue("");
            this.B.setValue("");
            this.hexEditor.setValue("");
            this.storeValue = {
                r: "",
                g: "",
                b: ""
            };
            return;
        }
        if (!color) {
            color = "";
            this.none.setSelected(true);
        } else {
            this.none.setSelected(false);
        }
        this.transparent.setSelected(false);
        this._showPreColor(color);
        var json = BI.DOM.rgb2json(BI.DOM.hex2rgb(color));
        this.storeValue = {
            r: BI.isNull(json.r) ? "" : json.r,
            g: BI.isNull(json.g) ? "" : json.g,
            b: BI.isNull(json.b) ? "" : json.b
        };
        this.R.setValue(this.storeValue.r);
        this.G.setValue(this.storeValue.g);
        this.B.setValue(this.storeValue.b);
        this.hexEditor.setValue(color.slice(this.constants.HEX_PREFIX_POSITION));
    },

    getValue: function () {
        if (this._isEmptyRGB() && this.transparent.isSelected()) {
            return "transparent";
        }
        return BI.DOM.rgb2hex(BI.DOM.json2rgb({
            r: this.storeValue.r,
            g: this.storeValue.g,
            b: this.storeValue.b
        }));
    }
});
BI.HexColorPickerEditor.EVENT_CHANGE = "EVENT_CHANGE";
BI.shortcut("bi.hex_color_picker_editor", BI.HexColorPickerEditor);<|endoftext|>


<commit_before>var _ = require('underscore');
var async = require("async");

var config = require('./config');
var logger = require('./logger');

var MongoClient = require('mongodb').MongoClient;

var processCollection = function(name, err, collection, options, callback) {
    if (err) {
        logger.error("unable to fetch '" + name + "' collection", err);
        callback(true);
        return;
    }
    logger.info("successfully fetched '" + name + "' collection");
    var tasks = [];
    if (options.indexes) {
        _(options.indexes).each(function(index) {
            tasks.push(function(cb) {
                collection.ensureIndex(index.keys, index.options, function(err, indexName) {
                    if (err) {
                        logger.error("unable to create index on '" + name + "' collection", index, err);
                        return cb(err);
                    }
                    logger.info("have '" + name + "' index: " + indexName);
                    cb(null);
                });
            });
        });
    }
    if (options.idPrefix) {
        tasks.push(function(cb) {
            var idName = options.idPrefix + "id";
            module.exports.countersCollect.update({_id: idName}, {$setOnInsert: {seq: 0}}, {w: 1, upsert: true}, function(err, result) {
                if (err) {
                    logger.error("unable to create counter for " + idName, index, err);
                    return cb(err);
                }
                logger.info("have counter for " + idName);
                cb(null);
            });
        });
    }

    async.series(tasks, function(err, results) {
        if (err) return callback(err);
        callback(null);
    });
};

var db; // module-level alias for module.exports.db

module.exports = {
    db: null,               // the database handle
    countersCollect: null,  // counters collection
    uCollect: null,         // users collection
    sCollect: null,         // submissions collection
    qiCollect: null,        // question instances collection
    statsCollect: null,     // statistics collection
    tCollect: null,         // tests collection
    tiCollect: null,        // test instances collection
    accessCollect: null,    // accesses collection
    pullCollect: null,      // pulls collection
    dCollect: null,         // deleted collection
    fCollect: null,         // finishes collection

    init: function(callback) {
        MongoClient.connect(config.dbAddress, function(err, locDb) {
            if (err) {
                callback("unable to connect to database at address " + config.dbAddress + ": " + err);
                return;
            }
            logger.info("successfully connected to database");
            module.exports.db = db = locDb;
            async.series([
                    function(cb) {
                        db.collection('counters', function(err, collection) {
                            module.exports.countersCollect = collection;
                            var options = {};
                            processCollection('counters', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('users', function(err, collection) {
                            module.exports.uCollect = collection;
                            var options = {
                                indexes: [
                                {keys: {uid: 1}, options: {unique: true}},
                                ],
                            };
                            processCollection('users', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('submissions', function(err, collection) {
                            module.exports.sCollect = collection;
                            var options = {
                                idPrefix: "s",
                                indexes: [
                                {keys: {sid: 1}, options: {unique: true}},
                                {keys: {uid: 1}, options: {}},
                                {keys: {qiid: 1}, options: {}},
                                ],
                            };
                            processCollection('submissions', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('qInstances', function(err, collection) {
                            module.exports.qiCollect = collection;
                            var options = {
                                idPrefix: "qi",
                                indexes: [
                                {keys: {qiid: 1}, options: {unique: true}},
                                {keys: {uid: 1}, options: {}},
                                {keys: {tiid: 1}, options: {}},
                                ],
                            };
                            processCollection('qInstances', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('statistics', function(err, collection) {
                            module.exports.statsCollect = collection;
                            var options = {};
                            processCollection('statistics', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('tests', function(err, collection) {
                            module.exports.tCollect = collection;
                            var options = {
                                indexes: [
                                {keys: {tid: 1}, options: {unique: true}},
                                ],
                            };
                            processCollection('tests', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('tInstances', function(err, collection) {
                            module.exports.tiCollect = collection;
                            var options = {
                                idPrefix: "ti",
                                indexes: [
                                {keys: {tiid: 1}, options: {unique: true}},
                                {keys: {uid: 1}, options: {}},
                                {keys: {tid: 1}, options: {}},
                                ],
                            };
                            processCollection('tInstances', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('accesses', function(err, collection) {
                            module.exports.accessCollect = collection;
                            var options = {};
                            processCollection('accesses', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('pulls', function(err, collection) {
                            module.exports.pullCollect = collection;
                            var options = {
                                idPrefix: "p",
                                indexes: [
                                {keys: {pid: 1}, options: {unique: true}},
                                {keys: {pullHash: 1}, options: {}},
                                ],
                            };
                            processCollection('pulls', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('deleted', function(err, collection) {
                            module.exports.dCollect = collection;
                            var options = {
                                idPrefix: "d",
                                indexes: [
                                {keys: {did: 1}, options: {unique: true}},
                                ],
                            };
                            processCollection('deleted', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('finishes', function(err, collection) {
                            module.exports.fCollect = collection;
                            var options = {
                                idPrefix: "f",
                                indexes: [
                                {keys: {fid: 1}, options: {unique: true}},
                                ],
                            };
                            processCollection('finishes', err, collection, options, cb);
                        });
                    },
                ], function(err) {
                    if (err) {
                        callback("error loading DB collections");
                    } else {
                        logger.info("Successfully loaded all DB collections");
                        callback(null);
                    }
                });
        });
    },

    newID: function(type, callback) {
        var query = {_id: type};
        var sort = [['_id', 1]];
        var doc = {$inc: {seq: 1}};
        module.exports.countersCollect.findAndModify(query, sort, doc, {w: 1, new: true}, function(err, item) {
            if (err) return callback(err);
            var id = type.slice(0, type.length - 2) + item.value.seq;
            callback(null, id);
        });
    },
};
<commit_msg>index was not defined at this location<commit_after>var _ = require('underscore');
var async = require("async");

var config = require('./config');
var logger = require('./logger');

var MongoClient = require('mongodb').MongoClient;

var processCollection = function(name, err, collection, options, callback) {
    if (err) {
        logger.error("unable to fetch '" + name + "' collection", err);
        callback(true);
        return;
    }
    logger.info("successfully fetched '" + name + "' collection");
    var tasks = [];
    if (options.indexes) {
        _(options.indexes).each(function(index) {
            tasks.push(function(cb) {
                collection.ensureIndex(index.keys, index.options, function(err, indexName) {
                    if (err) {
                        logger.error("unable to create index on '" + name + "' collection", index, err);
                        return cb(err);
                    }
                    logger.info("have '" + name + "' index: " + indexName);
                    cb(null);
                });
            });
        });
    }
    if (options.idPrefix) {
        tasks.push(function(cb) {
            var idName = options.idPrefix + "id";
            module.exports.countersCollect.update({_id: idName}, {$setOnInsert: {seq: 0}}, {w: 1, upsert: true}, function(err, result) {
                if (err) {
                    logger.error("unable to create counter for " + idName, err);
                    return cb(err);
                }
                logger.info("have counter for " + idName);
                cb(null);
            });
        });
    }

    async.series(tasks, function(err, results) {
        if (err) return callback(err);
        callback(null);
    });
};

var db; // module-level alias for module.exports.db

module.exports = {
    db: null,               // the database handle
    countersCollect: null,  // counters collection
    uCollect: null,         // users collection
    sCollect: null,         // submissions collection
    qiCollect: null,        // question instances collection
    statsCollect: null,     // statistics collection
    tCollect: null,         // tests collection
    tiCollect: null,        // test instances collection
    accessCollect: null,    // accesses collection
    pullCollect: null,      // pulls collection
    dCollect: null,         // deleted collection
    fCollect: null,         // finishes collection

    init: function(callback) {
        MongoClient.connect(config.dbAddress, function(err, locDb) {
            if (err) {
                callback("unable to connect to database at address " + config.dbAddress + ": " + err);
                return;
            }
            logger.info("successfully connected to database");
            module.exports.db = db = locDb;
            async.series([
                    function(cb) {
                        db.collection('counters', function(err, collection) {
                            module.exports.countersCollect = collection;
                            var options = {};
                            processCollection('counters', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('users', function(err, collection) {
                            module.exports.uCollect = collection;
                            var options = {
                                indexes: [
                                {keys: {uid: 1}, options: {unique: true}},
                                ],
                            };
                            processCollection('users', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('submissions', function(err, collection) {
                            module.exports.sCollect = collection;
                            var options = {
                                idPrefix: "s",
                                indexes: [
                                {keys: {sid: 1}, options: {unique: true}},
                                {keys: {uid: 1}, options: {}},
                                {keys: {qiid: 1}, options: {}},
                                ],
                            };
                            processCollection('submissions', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('qInstances', function(err, collection) {
                            module.exports.qiCollect = collection;
                            var options = {
                                idPrefix: "qi",
                                indexes: [
                                {keys: {qiid: 1}, options: {unique: true}},
                                {keys: {uid: 1}, options: {}},
                                {keys: {tiid: 1}, options: {}},
                                ],
                            };
                            processCollection('qInstances', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('statistics', function(err, collection) {
                            module.exports.statsCollect = collection;
                            var options = {};
                            processCollection('statistics', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('tests', function(err, collection) {
                            module.exports.tCollect = collection;
                            var options = {
                                indexes: [
                                {keys: {tid: 1}, options: {unique: true}},
                                ],
                            };
                            processCollection('tests', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('tInstances', function(err, collection) {
                            module.exports.tiCollect = collection;
                            var options = {
                                idPrefix: "ti",
                                indexes: [
                                {keys: {tiid: 1}, options: {unique: true}},
                                {keys: {uid: 1}, options: {}},
                                {keys: {tid: 1}, options: {}},
                                ],
                            };
                            processCollection('tInstances', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('accesses', function(err, collection) {
                            module.exports.accessCollect = collection;
                            var options = {};
                            processCollection('accesses', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('pulls', function(err, collection) {
                            module.exports.pullCollect = collection;
                            var options = {
                                idPrefix: "p",
                                indexes: [
                                {keys: {pid: 1}, options: {unique: true}},
                                {keys: {pullHash: 1}, options: {}},
                                ],
                            };
                            processCollection('pulls', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('deleted', function(err, collection) {
                            module.exports.dCollect = collection;
                            var options = {
                                idPrefix: "d",
                                indexes: [
                                {keys: {did: 1}, options: {unique: true}},
                                ],
                            };
                            processCollection('deleted', err, collection, options, cb);
                        });
                    },
                    function(cb) {
                        db.collection('finishes', function(err, collection) {
                            module.exports.fCollect = collection;
                            var options = {
                                idPrefix: "f",
                                indexes: [
                                {keys: {fid: 1}, options: {unique: true}},
                                ],
                            };
                            processCollection('finishes', err, collection, options, cb);
                        });
                    },
                ], function(err) {
                    if (err) {
                        callback("error loading DB collections");
                    } else {
                        logger.info("Successfully loaded all DB collections");
                        callback(null);
                    }
                });
        });
    },

    newID: function(type, callback) {
        var query = {_id: type};
        var sort = [['_id', 1]];
        var doc = {$inc: {seq: 1}};
        module.exports.countersCollect.findAndModify(query, sort, doc, {w: 1, new: true}, function(err, item) {
            if (err) return callback(err);
            var id = type.slice(0, type.length - 2) + item.value.seq;
            callback(null, id);
        });
    },
};
<|endoftext|>


<commit_before>var TBNotify = {};

TBNotify.lastSeenObjectId = localStorage['lastSeenObjectId'];

/**
 * Request the activity stream.
 * Relies on the user being authenticated on teambox.com already.
 */
TBNotify.fetchActivity = function() {
    var xhr = new XMLHttpRequest();

    xhr.open("GET", "https://teambox.com/api/1/activities");

    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.responseText.indexOf('<!DOCTYPE html>') > -1) {
                // Not logged in apparently.
                chrome.browserAction.setIcon({path: 'icon_notloggedin.png'});
                chrome.browserAction.setTitle({title: 'Error: Not logged in to teambox.com'});
                // Die.
                return;
            }

            // If we're authenticated, reset to default settings.
            chrome.browserAction.setIcon({path: 'icon.png'});
            chrome.browserAction.setTitle({title: 'Teambox Notifier'});

            var response = JSON.parse(xhr.responseText);

            TBNotify.processActivityResponse(response);
        }
    };

    xhr.send();
};

TBNotify.fetchCallback = function() {

};

/**
 * Process the activity stream data.
 * Update / notify with result.
 */
TBNotify.processActivityResponse = function(data) {
    if (data.objects[0] && data.objects[0].id != TBNotify.lastEncounteredObjectId) {
        var itemsBeforeLast = 0;
        var objects = data.objects;
        var encountered = false;

        // We get a big array of items for reference, index them on their IDs
        var referenceItems = {};
        for (var iter = 0, item; item = data.references[iter]; iter++) {
            referenceItems[item.id] = item;
        }

        for (var iter = 0, item; item = objects[iter]; iter++) {
            if (item.id == TBNotify.lastSeenObjectId) {
                break;
            }

            if (TBNotify.lastEncounteredObjectId == item.id) {
                encountered = true;
            }

            // If we are still before the last encountered / notified item,
            // then let's notify!
            if (!encountered) {
                var refItem = referenceItems[item.target_id];
                var user = referenceItems[item.user_id];

                // We found the reference item, hooray!
                if (refItem) {

                    var notificationType = '';
                    var notificationBody = '';

                    // Special handling of certain activities.
                    // Falls back to generic handling
                    if (refItem.type == "Person") {
                        var userObj = referenceItems[refItem.user_id];
                        var projectObj = referenceItems[item.project_id];

                        notificationType = 'Person added to Project';
                        notificationBody = userObj.first_name + ' ' + userObj.last_name + ' was added to ' + projectObj.name;
                    } else {
                        notificationType = refItem.type;
                        notificationBody = refItem.body || refItem.name;
                    }

                    // Meh, let's scope this so I can just use notification for all
                    // of them and they can just quickly reference back to it.
                    // I hate closures. This is nasty.
                    (function() {
                        var notification = webkitNotifications.createNotification(
                            user.avatar_url,
                            refItem.type,
                            refItem.body || refItem.name
                        );

                        notification.show();

                        setTimeout(function() {
                            notification.cancel();
                        }, 10000);
                    })();
                }
            }

            itemsBeforeLast++;
        }

        if (itemsBeforeLast) {
            chrome.browserAction.setBadgeText({text: "" + itemsBeforeLast});
        } else {
            chrome.browserAction.setBadgeText({text: ""});
        }

        // We don't want to re-notify for the same update.
        TBNotify.lastEncounteredObjectId = data.objects[0].id;
    }
};

/**
 * Handler for when the user clicks on the icon in the bar.
 */
TBNotify.handleButtonClicked = function() {
    chrome.browserAction.setBadgeText({"text": ""});
    localStorage['lastSeenObjectId'] = TBNotify.lastSeenObjectId = TBNotify.lastEncounteredObjectId;
};

chrome.browserAction.onClicked.addListener(TBNotify.handleButtonClicked);

// Initial load fetch.
TBNotify.fetchActivity();
// Update every 30 seconds.
window.setInterval(TBNotify.fetchActivity, 60000);
<commit_msg>Didn't completely finish the last commit to handle Person types.<commit_after>var TBNotify = {};

TBNotify.lastSeenObjectId = localStorage['lastSeenObjectId'];

/**
 * Request the activity stream.
 * Relies on the user being authenticated on teambox.com already.
 */
TBNotify.fetchActivity = function() {
    var xhr = new XMLHttpRequest();

    xhr.open("GET", "https://teambox.com/api/1/activities");

    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.responseText.indexOf('<!DOCTYPE html>') > -1) {
                // Not logged in apparently.
                chrome.browserAction.setIcon({path: 'icon_notloggedin.png'});
                chrome.browserAction.setTitle({title: 'Error: Not logged in to teambox.com'});
                // Die.
                return;
            }

            // If we're authenticated, reset to default settings.
            chrome.browserAction.setIcon({path: 'icon.png'});
            chrome.browserAction.setTitle({title: 'Teambox Notifier'});

            var response = JSON.parse(xhr.responseText);

            TBNotify.processActivityResponse(response);
        }
    };

    xhr.send();
};

TBNotify.fetchCallback = function() {

};

/**
 * Process the activity stream data.
 * Update / notify with result.
 */
TBNotify.processActivityResponse = function(data) {
    if (data.objects[0] && data.objects[0].id != TBNotify.lastEncounteredObjectId) {
        var itemsBeforeLast = 0;
        var objects = data.objects;
        var encountered = false;

        // We get a big array of items for reference, index them on their IDs
        var referenceItems = {};
        for (var iter = 0, item; item = data.references[iter]; iter++) {
            referenceItems[item.id] = item;
        }

        for (var iter = 0, item; item = objects[iter]; iter++) {
            if (item.id == TBNotify.lastSeenObjectId) {
                break;
            }

            if (TBNotify.lastEncounteredObjectId == item.id) {
                encountered = true;
            }

            // If we are still before the last encountered / notified item,
            // then let's notify!
            if (!encountered) {
                var refItem = referenceItems[item.target_id];
                var user = referenceItems[item.user_id];

                // We found the reference item, hooray!
                if (refItem) {

                    var notificationType = '';
                    var notificationBody = '';

                    // Special handling of certain activities.
                    // Falls back to generic handling
                    if (refItem.type == "Person") {
                        var userObj = referenceItems[refItem.user_id];
                        var projectObj = referenceItems[item.project_id];

                        notificationType = 'Person added to Project';
                        notificationBody = userObj.first_name + ' ' + userObj.last_name + ' was added to ' + projectObj.name;
                    } else {
                        notificationType = refItem.type;
                        notificationBody = refItem.body || refItem.name;
                    }

                    // Meh, let's scope this so I can just use notification for all
                    // of them and they can just quickly reference back to it.
                    // I hate closures. This is nasty.
                    (function() {
                        var notification = webkitNotifications.createNotification(
                            user.avatar_url,
                            notificationType,
                            notificationBody
                        );

                        notification.show();

                        setTimeout(function() {
                            notification.cancel();
                        }, 10000);
                    })();
                }
            }

            itemsBeforeLast++;
        }

        if (itemsBeforeLast) {
            chrome.browserAction.setBadgeText({text: "" + itemsBeforeLast});
        } else {
            chrome.browserAction.setBadgeText({text: ""});
        }

        // We don't want to re-notify for the same update.
        TBNotify.lastEncounteredObjectId = data.objects[0].id;
    }
};

/**
 * Handler for when the user clicks on the icon in the bar.
 */
TBNotify.handleButtonClicked = function() {
    chrome.browserAction.setBadgeText({"text": ""});
    localStorage['lastSeenObjectId'] = TBNotify.lastSeenObjectId = TBNotify.lastEncounteredObjectId;
};

chrome.browserAction.onClicked.addListener(TBNotify.handleButtonClicked);

// Initial load fetch.
TBNotify.fetchActivity();
// Update every 30 seconds.
window.setInterval(TBNotify.fetchActivity, 60000);
<|endoftext|>


<commit_before>/*
 * LocalStore key => "gh_search_text"
 *
 * FORMAT
 *      "gh_search_text" : {
 *          "settings" : {
 *              "background_match" : "COLOR"
 *          },
 *          "text_search" : {
 *              "value" :  "SEARCH_STRING",
 *              "matches" : [ "FILENAME", "FILENAME" ]
 *          }
 *      }
 *
 * */

function init()
{
    // check if background color is stored
    if (window.localStorage.getItem("gh_text_search") === null)
    {
        // create it empty
        window.localStorage.setItem("gh_text_search", "");
        var data = {};
        // init background color if it not exists
        data["settings"] = {"background_match": "#fff0b3"};
        data["text_search"] = {};
        // TODO add history
        window.localStorage.setItem("gh_text_search" , JSON.stringify(data))

    }

}

colorPicker.addEventListener("change", watchColorPicker, false);

<commit_msg>popup get 'default color' if not storage exists<commit_after>/*
 * LocalStore key => "gh_search_text"
 *
 * FORMAT
 *      "gh_search_text" : {
 *          "settings" : {
 *              "background_match" : "COLOR"
 *          },
 *          "text_search" : {
 *              "value" :  "SEARCH_STRING",
 *              "matches" : [ "FILENAME", "FILENAME" ]
 *          }
 *      }
 *
 * */

function init()
{
    // check if background color is stored
    if (window.localStorage.getItem("gh_text_search") === null)
    {
        // create it empty
        window.localStorage.setItem("gh_text_search", "");
        var data = {};
        // init background color if it not exists
        data["settings"] = {"background_match": "#fff0b3"};
        data["text_search"] = {};
        // TODO add history
        window.localStorage.setItem("gh_text_search" , JSON.stringify(data))

    }

}

<|endoftext|>


<commit_before>define('app/controllers/scripts', ['app/controllers/base_array', 'app/models/script'],
    //
    //  Scripts Controller
    //
    //  @returns Class
    //
    function (BaseArrayController, ScriptModel) {

        'use strict';

        return BaseArrayController.extend({

            model: ScriptModel,

            addScript: function (args) {

                var that = this;
                that.set('addingScript', true);
                Mist.ajax.POST('/scripts', {
                    'name': args.script.name,
                    'exec_type': args.script.type.value,
                    'location_type': args.script.source.value,
                    'entrypoint': args.script.entryPoint,
                    'script': args.script.type.value == 'inline' ? args.script.script : args.script.url
                }).success(function (script) {
                    that._addObject(script);
                }).error(function (message) {
                    Mist.notificationController.notify(message);
                }).complete(function (success) {
                    that.set('addingScript', false);
                    if (args.callback)
                        args.callback(success);
                })
            },

            runScript: function (args) {
                var that = this;
                that.set('runningScript', true);
                Mist.ajax.POST('/scripts/' + args.script.script.id, {
                    'machine_id': args.script.machine.id,
                    'backend_id': args.script.machine.backend.id,
                    'params': encodeURIComponent(args.script.parameters)
                }).error(function (message) {
                    Mist.notificationController.notify(message);
                }).complete(function (success) {
                    that.set('runningScript', false);
                    if (args.callback)
                        args.callback(success);
                });
            },

            getRequestedScript: function() {
                if (this.scriptRequest) {
                    return this.getObject(this.scriptRequest);
                }
            },
        });
    }
);
<commit_msg>Remove encode uri component<commit_after>define('app/controllers/scripts', ['app/controllers/base_array', 'app/models/script'],
    //
    //  Scripts Controller
    //
    //  @returns Class
    //
    function (BaseArrayController, ScriptModel) {

        'use strict';

        return BaseArrayController.extend({

            model: ScriptModel,

            addScript: function (args) {

                var that = this;
                that.set('addingScript', true);
                Mist.ajax.POST('/scripts', {
                    'name': args.script.name,
                    'exec_type': args.script.type.value,
                    'location_type': args.script.source.value,
                    'entrypoint': args.script.entryPoint,
                    'script': args.script.type.value == 'inline' ? args.script.script : args.script.url
                }).success(function (script) {
                    that._addObject(script);
                }).error(function (message) {
                    Mist.notificationController.notify(message);
                }).complete(function (success) {
                    that.set('addingScript', false);
                    if (args.callback)
                        args.callback(success);
                })
            },

            runScript: function (args) {
                var that = this;
                that.set('runningScript', true);
                Mist.ajax.POST('/scripts/' + args.script.script.id, {
                    'machine_id': args.script.machine.id,
                    'backend_id': args.script.machine.backend.id,
                    'params': args.script.parameters
                }).error(function (message) {
                    Mist.notificationController.notify(message);
                }).complete(function (success) {
                    that.set('runningScript', false);
                    if (args.callback)
                        args.callback(success);
                });
            },

            getRequestedScript: function() {
                if (this.scriptRequest) {
                    return this.getObject(this.scriptRequest);
                }
            },
        });
    }
);
<|endoftext|>


<commit_before>chrome.runtime.connectNative("enslite");
var transferUrl = "";

chrome.windows.onRemoved.addListener(function(windowId){
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://localhost:31313/resolver/", false);
    xhr.send();
});

function sleep(milliseconds, bithost) {
	// synchronous XMLHttpRequests from Chrome extensions are not blocking event handlers. That's why we use this
	// pretty little sleep function to try to get the IP of a .bit domain before the request times out.
	var start = new Date().getTime();
	for (var i = 0; i < 1e7; i++) {
		if (((new Date().getTime() - start) > milliseconds) || (sessionStorage.getItem(bithost) != null)){
			break;
		}
	}
}

// run script when a request is about to occur
chrome.webRequest.onBeforeRequest.addListener(function (details) {
	// get the parts of the url (hostname, port) by creating an 'a' element
	var parser = document.createElement('a');
	parser.href = details.url;

	// Make sure the domain ends with .bit.
	var tld = parser.hostname.slice(-3);
	if (tld != 'eth') {
		return;
	};

	var bithost = parser.hostname;
	var port = (parser.protocol == "https:" ? "443" : "80");
	var access = (parser.protocol == "https:" ? "HTTPS" : "PROXY");

	// This .eth domain is not in cache, get the IP from the resolver
	var xhr = new XMLHttpRequest();
	var url = "http://localhost:31313/resolver/"+bithost;
	// synchronous XMLHttpRequest is actually asynchronous
	// check out https://developer.chrome.com/extensions/webRequest
	xhr.open("GET", url, false);
	xhr.onreadystatechange = function() {
		if (xhr.readyState == 4) {
			// Get the ip address returned from the DNS proxy server.
			var bitip = xhr.responseText;
			// store the IP for .bit hostname in the local cache which is reset on each browser restart
			sessionStorage.setItem(bithost, bitip);
		}
	}
	xhr.send();
	// block the request until the new proxy settings are set. Block for up to two seconds.
	sleep(2000, bithost);
    // 503 means were syncing with the ethereum network. Show loading screen.
    if (xhr.status == 503) {
        transferUrl = details.url;
        return {redirectUrl: chrome.extension.getURL("sync.html")};
    }

	// Get the IP from the session storage.
	var bitip = sessionStorage.getItem(bithost);
	var config = {
		mode: "pac_script",
		pacScript: {
			data: "function FindProxyForURL(url, host) {\n" +
			"  if (dnsDomainIs(host, '"+bithost+"'))\n" +
			"    return '"+access+" "+bitip+":"+port+"';\n" +
			"  return 'DIRECT';\n" +
			"}"
		}
	};
	chrome.proxy.settings.set({value: config, scope: 'regular'},function() {});
	console.log('IP '+bitip+' for '+bithost+' found, config is changed: '+JSON.stringify(config));

}, { urls: ["<all_urls>"] }, ["blocking"]);
<commit_msg>Update comments in background.js<commit_after>chrome.runtime.connectNative("enslite");
var transferUrl = "";

chrome.windows.onRemoved.addListener(function(windowId){
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://localhost:31313/resolver/", false);
    xhr.send();
});

function sleep(milliseconds, bithost) {
	// synchronous XMLHttpRequests from Chrome extensions are not blocking event handlers. That's why we use this
	// pretty little sleep function to try to get the IP of a .eth domain before the request times out.
	var start = new Date().getTime();
	for (var i = 0; i < 1e7; i++) {
		if (((new Date().getTime() - start) > milliseconds) || (sessionStorage.getItem(bithost) != null)){
			break;
		}
	}
}

// run script when a request is about to occur
chrome.webRequest.onBeforeRequest.addListener(function (details) {
	// get the parts of the url (hostname, port) by creating an 'a' element
	var parser = document.createElement('a');
	parser.href = details.url;

	// Make sure the domain ends with .eth.
	var tld = parser.hostname.slice(-3);
	if (tld != 'eth') {
		return;
	};

	var bithost = parser.hostname;
	var port = (parser.protocol == "https:" ? "443" : "80");
	var access = (parser.protocol == "https:" ? "HTTPS" : "PROXY");

	// This .eth domain is not in cache, get the IP from the resolver
	var xhr = new XMLHttpRequest();
	var url = "http://localhost:31313/resolver/"+bithost;
	// synchronous XMLHttpRequest is actually asynchronous
	// check out https://developer.chrome.com/extensions/webRequest
	xhr.open("GET", url, false);
	xhr.onreadystatechange = function() {
		if (xhr.readyState == 4) {
			// Get the ip address returned from the DNS proxy server.
			var bitip = xhr.responseText;
			// store the IP for .eth hostname in the local cache which is reset on each browser restart
			sessionStorage.setItem(bithost, bitip);
		}
	}
	xhr.send();
	// block the request until the new proxy settings are set. Block for up to two seconds.
	sleep(2000, bithost);
        // 503 means were syncing with the ethereum network. Show loading screen.
        if (xhr.status == 503) {
            transferUrl = details.url;
            return {redirectUrl: chrome.extension.getURL("sync.html")};
        }

	// Get the IP from the session storage.
	var bitip = sessionStorage.getItem(bithost);
	var config = {
		mode: "pac_script",
		pacScript: {
			data: "function FindProxyForURL(url, host) {\n" +
			"  if (dnsDomainIs(host, '"+bithost+"'))\n" +
			"    return '"+access+" "+bitip+":"+port+"';\n" +
			"  return 'DIRECT';\n" +
			"}"
		}
	};
	chrome.proxy.settings.set({value: config, scope: 'regular'},function() {});
	console.log('IP '+bitip+' for '+bithost+' found, config is changed: '+JSON.stringify(config));

}, { urls: ["<all_urls>"] }, ["blocking"]);
<|endoftext|>


<commit_before>"use strict";

var ls = localStorage;
var iteration = 0;
var intervalId = null;

var mainLoop = function(force) {
  console.log("\n#" + iteration);

  if (force || iteration % UPDATE_CANTINAS_INTERVAL === 0)
    updateCantinas();
  if (force || iteration % UPDATE_NEWS_INTERVAL === 0)
    updateAffiliationNews('1');
  if (ls.showAffiliation2 === 'true')
    if (force || iteration % UPDATE_NEWS_INTERVAL === 0)
      updateAffiliationNews('2');

  // No reason to count to infinity
  if (10000 < iteration)
    iteration = 0;
  else
    iteration++;
}

var updateAffiliation = function(callback) {
  console.log('updateAffiliation');
  // Fetch
  Affiliation.get(ls.affiliationKey1, function() {
    // Run relevant background updates
    if (ls.useBigscreen !== 'true') {
      if (Affiliation.org[ls.affiliationKey1].hw) {
        updateStatusAndMeetings();
        updateCoffeeSubscription();
      }
    }
    // Callback
    if (typeof callback === 'function') callback();
  });
};

var updateStatusAndMeetings = function(force, callback) {
  console.log('updateStatusAndMeetings');

  // Get meeting data
  var meeting = ls.meetingString;

  // Get status data
  var strings = JSON.parse(ls.statusStrings);
  var statusCode = strings.statusCode;
  var statusTitle = strings.statusTitle;
  var statusMessage = strings.statusMessage;
}

var updateCoffeeSubscription = function(callback) {
  console.log('updateCoffeeSubscription');
  // Get
  var coffeeData = JSON.parse(ls.coffeeData);
  // Hope for the best
  try {
    var date = coffeeData.date;
    var pots = coffeeData.pots;

    // No coffee yields pots=0 and date=null
    if (pots && date) {

      // Parse that date
      date = new Date(date);
      var age = Coffee.minuteDiff(date);

      // Check for NaN here
      if (!isNaN(pots) && !isNaN(age)) {
        // New pot number?
        ls.coffeePots = pots;
      }
    }
    if (typeof callback === 'function') callback();
  }
  catch (e) {
    console.error(e);
  }
}

var updateCantinas = function(callback) {
  console.log('updateCantinas');
  // Fetch
  Cantina.get(ls.cantina1, function(result1) {
    Cantina.get(ls.cantina2, function(result2) {
      // Save
      ls.cantina1Data = JSON.stringify(result1);
      ls.cantina2Data = JSON.stringify(result2);
      // Callback
      if (typeof callback === 'function') callback();
    });
  });
}

var updateAffiliationNews = function(number, callback) {
  console.log('updateAffiliationNews'+number);
  // Get affiliation object
  var affiliationKey = ls['affiliationKey'+number];
  var affiliationObject = Affiliation.org[affiliationKey];
  if (affiliationObject) {
    // Get more news than needed to check for old news that have been updated
    var newsLimit = 10;
    News.get(affiliationObject, newsLimit, function(items) {
      // Error message, log it maybe
      if (typeof items === 'string') {
        console.error(items);
      }
      // Empty news items, don't count
      else if (items.length === 0) {
        // Do nothing
      }
      // News is here! NEWS IS HERE! FRESH FROM THE PRESS!
      else {
        saveAndCountNews(items, number);
        fetchAndStoreImageLinks(number);
      }
      if (typeof callback === 'function') callback();
    });
  }
  else {
    console.error('Chosen affiliation "' + ls['affiliationKey'+number] + '" is not known');
    if (typeof callback === 'function') callback();
  }
}

var saveAndCountNews = function(items, number) {
  var feedItems = 'affiliationNews' + number;
  var newsList = 'affiliationNewsList' + number;
  var unreadCount = 'affiliationUnreadCount' + number;

  ls[feedItems] = JSON.stringify(items);
  var list = JSON.parse(ls[newsList]);
  ls[unreadCount] = News.countNewsAndNotify(items, list);
  ls[newsList] = News.refreshNewsList(items);
}

var fetchAndStoreImageLinks = function(number) {
  var key = ls['affiliationKey'+number];
  var newsList = JSON.parse(ls['affiliationNewsList'+number]);
  // If the organization has it's own getImage function, use it
  if (Affiliation.org[key].getImage !== undefined) {
    for (var i in newsList) {
      var link = newsList[i];
      // It's important to get the link from the callback within the function below,
      // not the above code, - because of race conditions mixing up the news posts, async ftw.
      Affiliation.org[key].getImage(link, function(link, image) {
        if (null !== image[0]) {
          var storedImages = JSON.parse(ls.storedImages);
          storedImages[link] = image[0];
          ls.storedImages = JSON.stringify(storedImages);
        }
      });
    }
  }
  // If the organization has it's own getImages (plural) function, use it
  if (Affiliation.org[key].getImages !== undefined) {
    Affiliation.org[key].getImages(newsList, function(links, images) {
      var storedImages = JSON.parse(ls.storedImages);
      for (var i in links) {
        if (null !== images[i]) {
          storedImages[links[i]] = images[i];
        }
      }
      ls.storedImages = JSON.stringify(storedImages);
    });
  }
}

//
// Load Affiliation Icon
// (executes itself once)
//

var loadAffiliationIcon = function() {
  var key = ls.affiliationKey1;
  // Set badge icon
  var icon = Affiliation.org[key].icon;
  Browser.setIcon(icon);
  // Set badge title
  var name = Affiliation.org[key].name;
  Browser.setTitle(name + ' Notiwall');
}();

//
// Keep Awake
// (executes itself once)
//

(function keepAwake() {
  // Keep this computer and its display turned on.
  chrome.power.requestKeepAwake("display");
}());

// Document ready, go!
$(document).ready(function() {
  // Clear values that should start empty
  Affiliation.clearAffiliationData();

  // Check if both current affiliations still exist, reset if not
  var keys = Object.keys(Affiliation.org);
  Defaults.resetAffiliationsIfNotExist(ls.affiliationKey1, ls.affiliationKey2, keys);

  // If just installed, wait 5 secs to let things load
  var delayOpening = 0;
  if (Number(ls.installTime) + 5000 > Date.now()) {
    delayOpening = 5000;
  }
  setTimeout(function() {
    // Open the desired Notiwall on startup
    if (ls.whichScreen === 'infoscreen') {
      Browser.openTab('infoscreen.html');
      Analytics.trackEvent('loadInfoscreen');
    }
    else if (ls.whichScreen === 'officescreen') {
      Browser.openTab('officescreen.html');
      Analytics.trackEvent('loadOfficescreen');
    }
    else {
      console.error('No recognised Notiwall. localStorage.whichScreen was "' + ls.whichScreen + '"');
    }
  }, delayOpening);

  // Send some basic statistics once a day
  setInterval( function() {
    // App version is interesting
    Analytics.trackEvent('appVersion', Browser.getAppVersion() + ' @ ' + Browser.name);
    // Affiliation is also interesting, in contrast to the popup some of these are inactive users
    // To find inactive user count, subtract these stats from popup stats
    if (ls.showAffiliation2 !== 'true') {
      Analytics.trackEvent('singleAffiliation', ls.affiliationKey1);
      Analytics.trackEvent('affiliation1', ls.affiliationKey1);
    }
    else {
      Analytics.trackEvent('doubleAffiliation', ls.affiliationKey1 + ' - ' + ls.affiliationKey2);
      Analytics.trackEvent('affiliation1', ls.affiliationKey1);
      Analytics.trackEvent('affiliation2', ls.affiliationKey2);
    }
  }, 1000 * 60 * 60 * 24);

  // Enter main loop, keeping everything up-to-date
  var stayUpdated = function(now) {
    console.log(ONLINE_MESSAGE);
    var loopTimeout = (DEBUG ? BACKGROUND_LOOP_DEBUG : BACKGROUND_LOOP);
    // Schedule for repetition
    intervalId = setInterval( function() {
      mainLoop();
    }, loopTimeout);
    // Run once right now (just wait 2 secs to avoid network-change errors)
    var timeout = (now ? 0 : 2000);
    setTimeout( function() {
      mainLoop(true);
    }, timeout);
  };
  // When offline, mainloop is stopped to decrease power consumption
  window.addEventListener('online', stayUpdated);
  window.addEventListener('offline', function() {
    console.log(OFFLINE_MESSAGE);
    clearInterval(intervalId);
  });

  if (navigator.onLine) {
    // If Online, go ahead and start the stayUpdated-function
    stayUpdated(true);
  }
  else {
    // If offline, run mainloop once, it fetches error messages
    // Keep in mind: this here is at program startup, we have to get something to display
    mainLoop();
  }
});
<commit_msg>background: utilised new news system, removed counting of news and such from Notifier<commit_after>"use strict";

var ls = localStorage;
var iteration = 0;
var intervalId = null;

var mainLoop = function(force) {
  console.log("\n#" + iteration);

  if (force || iteration % UPDATE_CANTINAS_INTERVAL === 0)
    updateCantinas();
  if (force || iteration % UPDATE_NEWS_INTERVAL === 0)
    updateAffiliationNews('1');
  if (ls.showAffiliation2 === 'true')
    if (force || iteration % UPDATE_NEWS_INTERVAL === 0)
      updateAffiliationNews('2');

  // No reason to count to infinity
  if (10000 < iteration)
    iteration = 0;
  else
    iteration++;
}

//
// Status (office status, meetings, and servant for affiliation)
//

var updateAffiliation = function(callback) {
  console.log('updateAffiliation');
  // Fetch
  Affiliation.get(ls.affiliationKey1, function() {
    // Run relevant background updates
    if (ls.useBigscreen !== 'true') {
      if (Affiliation.org[ls.affiliationKey1].hw) {
        updateStatusAndMeetings();
        updateCoffeeSubscription();
      }
    }
    // Callback
    if (typeof callback === 'function') callback();
  });
};

var updateStatusAndMeetings = function(force, callback) {
  console.log('updateStatusAndMeetings');

  // Get meeting data
  var meeting = ls.meetingString;

  // Get status data
  var strings = JSON.parse(ls.statusStrings);
  var statusCode = strings.statusCode;
  var statusTitle = strings.statusTitle;
  var statusMessage = strings.statusMessage;
}

//
// Coffee
//

var updateCoffeeSubscription = function(callback) {
  console.log('updateCoffeeSubscription');
  // Get
  var coffeeData = JSON.parse(ls.coffeeData);
  // Hope for the best
  try {
    var date = coffeeData.date;
    var pots = coffeeData.pots;

    // No coffee yields pots=0 and date=null
    if (pots && date) {

      // Parse that date
      date = new Date(date);
      var age = Coffee.minuteDiff(date);

      // Check for NaN here
      if (!isNaN(pots) && !isNaN(age)) {
        // New pot number?
        ls.coffeePots = pots;
      }
    }
    if (typeof callback === 'function') callback();
  }
  catch (e) {
    console.error(e);
  }
}

//
// Cantina
//

var updateCantinas = function(callback) {
  console.log('updateCantinas');
  // Fetch
  Cantina.get(ls.cantina1, function(result1) {
    Cantina.get(ls.cantina2, function(result2) {
      // Save
      ls.cantina1Data = JSON.stringify(result1);
      ls.cantina2Data = JSON.stringify(result2);
      // Callback
      if (typeof callback === 'function') callback();
    });
  });
}

//
// Affiliation news
//

var updateAffiliationNews = function(number, callback) {
  console.log('updateAffiliationNews'+number);
  // Get affiliation
  var affiliationKey = ls['affiliationKey'+number];
  var affiliation = Affiliation.org[affiliationKey];
  // Get news for this affiliation
  if (affiliation) {
    News.get(affiliation, function(posts) {
      // Error message, log it maybe
      if (typeof posts === 'string') {
        console.error(posts);
      }
      // Empty news posts, don't count
      else if (posts.length === 0) {
        // Do nothing (Notifier nullifies news count here)
      }
      // News is here! NEWS IS HERE! FRESH FROM THE PRESS!
      else {
        saveNews(posts, number);
      }
      if (typeof callback === 'function') callback();
    });
  }
  else {
    console.error('Chosen affiliation "' + affiliationKey + '" is not known');
    if (typeof callback === 'function') callback();
  }
};

var saveNews = function(items, number) {
  ls['affiliationNews' + number] = JSON.stringify(items);
  ls['affiliationNewsList' + number] = News.refreshNewsList(items);
};

//
// Load Affiliation Icon
// (executes itself once)
//

var loadAffiliationIcon = function() {
  var key = ls.affiliationKey1;
  // Set badge icon
  var icon = Affiliation.org[key].icon;
  Browser.setIcon(icon);
  // Set badge title
  var name = Affiliation.org[key].name;
  Browser.setTitle(name + ' Notiwall');
}();

//
// Keep Awake
// (executes itself once)
//

(function keepAwake() {
  // Keep this computer and its display turned on.
  chrome.power.requestKeepAwake("display");
}());

// Document ready, go!
$(document).ready(function() {
  // Clear values that should start empty
  Affiliation.clearAffiliationData();

  // Check if both current affiliations still exist, reset if not
  var keys = Object.keys(Affiliation.org);
  Defaults.resetAffiliationsIfNotExist(ls.affiliationKey1, ls.affiliationKey2, keys);

  // If just installed, wait 5 secs to let things load
  var delayOpening = 0;
  if (Number(ls.installTime) + 5000 > Date.now()) {
    delayOpening = 5000;
  }
  setTimeout(function() {
    // Open the desired Notiwall on startup
    if (ls.whichScreen === 'infoscreen') {
      Browser.openTab('infoscreen.html');
      Analytics.trackEvent('loadInfoscreen');
    }
    else if (ls.whichScreen === 'officescreen') {
      Browser.openTab('officescreen.html');
      Analytics.trackEvent('loadOfficescreen');
    }
    else {
      console.error('No recognised Notiwall. localStorage.whichScreen was "' + ls.whichScreen + '"');
    }
  }, delayOpening);

  // Send some basic statistics once a day
  setInterval( function() {
    // App version is interesting
    Analytics.trackEvent('appVersion', Browser.getAppVersion() + ' @ ' + Browser.name);
    // Affiliation is also interesting, in contrast to the popup some of these are inactive users
    // To find inactive user count, subtract these stats from popup stats
    if (ls.showAffiliation2 !== 'true') {
      Analytics.trackEvent('singleAffiliation', ls.affiliationKey1);
      Analytics.trackEvent('affiliation1', ls.affiliationKey1);
    }
    else {
      Analytics.trackEvent('doubleAffiliation', ls.affiliationKey1 + ' - ' + ls.affiliationKey2);
      Analytics.trackEvent('affiliation1', ls.affiliationKey1);
      Analytics.trackEvent('affiliation2', ls.affiliationKey2);
    }
  }, 1000 * 60 * 60 * 24);

  // Enter main loop, keeping everything up-to-date
  var stayUpdated = function(now) {
    console.log(ONLINE_MESSAGE);
    var loopTimeout = (DEBUG ? BACKGROUND_LOOP_DEBUG : BACKGROUND_LOOP);
    // Schedule for repetition
    intervalId = setInterval( function() {
      mainLoop();
    }, loopTimeout);
    // Run once right now (just wait 2 secs to avoid network-change errors)
    var timeout = (now ? 0 : 2000);
    setTimeout( function() {
      mainLoop(true);
    }, timeout);
  };
  // When offline, mainloop is stopped to decrease power consumption
  window.addEventListener('online', stayUpdated);
  window.addEventListener('offline', function() {
    console.log(OFFLINE_MESSAGE);
    clearInterval(intervalId);
  });

  if (navigator.onLine) {
    // If Online, go ahead and start the stayUpdated-function
    stayUpdated(true);
  }
  else {
    // If offline, run mainloop once, it fetches error messages
    // Keep in mind: this here is at program startup, we have to get something to display
    mainLoop();
  }
});
<|endoftext|>


<commit_before>var lastFocusedWindowID = 1;

chrome.tabs.onActivated.addListener(function(info) {
	chrome.tabs.get(info.tabId, function(tab) {
		updateTab(tab);
	});
});

chrome.tabs.onUpdated.addListener(function(tabID, changeInfo, tab) {
	if (tab.active) {
		updateTab(tab);
	}
});

chrome.windows.onFocusChanged.addListener(function(windowID) {
	if (windowID == -1) {
		return;
	}

	chrome.windows.get(windowID, { populate: true }, function(window) {
		if (window.type == 'normal') {
			lastFocusedWindowID = windowID;
		}

		console.log("Last focused window is now ID " + window.id);

		updateWindow(window);
	});
});

chrome.contextMenus.removeAll(function() {
	chrome.contextMenus.create({
	    "title": "Tiles Options",
	    "documentUrlPatterns": [chrome.extension.getURL("/") + "*"],
	    "contexts": ["page", "link"],
	    "onclick" : function() {
	    	goToOptionsPage(true)
	    }
	});
});

function goToOptionsPage(newTab) {
	var optionsURL = chrome.extension.getURL("options/options.html");

	if (newTab) {
		chrome.tabs.create({
			url: optionsURL
		});
	} else {
		chrome.tabs.getCurrent(function(tab) {
			chrome.tabs.update(tab.id, { url : optionsURL })
		});
	}
}

chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
	console.log('Message received: ' + request.message);

	if (request.message == "getUrl") {
		getFocusedTab(function(tab) {
			if (tab) {
				sendResponse({ url: tab.url });

				console.log('Sent URL – ' + tab.url);
			}
		});
	} else if (request.message == "saved") {
		getFocusedTab(function(tab) {
			if (tab) {
				setPopup(false, false, tab.id);
				changeIcon(true, null, tab.id);

				sendResponse({message: "success"});

				console.log('Sent success');

				updateAllWindows();
			}
		});
	} else if (request.message == "delete") {
		console.log('Deleting...');

		function deleteSiteCallback() {
			if (!request.url) {
				setPopup(true, false, tab.id);
				changeIcon(false, null, tab.id);
			}

			sendResponse({message: "deleted"});

			console.log('Sent deleted');

			updateAllWindows();
		}

		if (request.url == undefined) {
			console.log("Deleting site " + tab.url);
			deleteSite(tab.url, deleteSiteCallback);
		} else {
			console.log("Deleting site " + request.url);
			deleteSite(request.url, deleteSiteCallback);
		}
	}

	return true;
});

function getFocusedTab(callback) {
	chrome.windows.get(lastFocusedWindowID, { populate: true }, function(window) {
		if (window && window.type != 'normal') {
			return callback(null);
		}

		var tabs = window.tabs;

		for (var j = 0; j < tabs.length; j++) {
			if (tabs[j].active) {
				return callback(tabs[j]);
			}
		}
	});
}

function setPopup(save, error, tabID) {
	var details = {};

	if (error) {
		details.popup = 'browserAction/error.html';
	} else {
		if (save) {
			details.popup = 'browserAction/save.html';
		} else {
			details.popup = 'browserAction/delete.html';
		}
	}

	details.tabId = tabID;

	chrome.browserAction.setPopup(details);

	// console.log('Popup is set to ' + details.popup);
}

function updateWindow(window) {
	if (window && window.type == 'normal') {
		var tabs = window.tabs;

		for (var i = 0; i < tabs.length; i++) {
			if (tabs[i].active) {
				if (window.focused) {
					console.log("Window ID " + window.id + " is focused on active tab on ID: " + tabs[i].id + " which is at " + tabs[i].url);
				} else {
					console.log("Window ID " + window.id + " is has active tab ID: " + tabs[i].id + " which is at " + tabs[i].url);
				}

				updateTab(tabs[i]);

				break;
			}
		}
	}
}

function updateAllWindows() {
	chrome.windows.getAll({ populate: true }, function(windows) {
		console.log('Updating all windows...');

		for (var i = 0; i < windows.length; i++) {
			updateWindow(windows[i]);
		}
	});
}

function updateTab(tab) {
	console.log("Updating tab ID: " + tab.id + " which is at " + tab.url);

	if (isChromeURL(tab.url)) {
		setPopup(false, true, tab.id);
	} else {
		siteExists(tab.url, function(exists) {
			setPopup(!exists, false, tab.id);

			changeIcon(exists, null, tab.id);
		});
	}
}

function isChromeURL(url) {
	return url.substring(0, 6) == 'chrome';
}

function siteExists(url, callback) {
	getSites(function(sites) {
		if (sites) {
			for (var i = 0; i < sites.length; i++) {
				if (sites[i].url == url) {
					return callback(true);
				}
			}
		}

		return callback(false);
	});
}

function changeIcon(colors, callback, tabID) {
	var details = {};

	if (colors) {
		details.path = '../icons/icon-bitty.png';
	} else {
		details.path = '../icons/icon-bitty-gray.png';
	}

	console.log("Changing icon for " + tabID + " to " + details.path);

	details.tabId = tabID;

	chrome.browserAction.setIcon(details, callback);
}<commit_msg>Fix delete popover.<commit_after>var lastFocusedWindowID = 1;

chrome.tabs.onActivated.addListener(function(info) {
	chrome.tabs.get(info.tabId, function(tab) {
		updateTab(tab);
	});
});

chrome.tabs.onUpdated.addListener(function(tabID, changeInfo, tab) {
	if (tab.active) {
		updateTab(tab);
	}
});

chrome.windows.onFocusChanged.addListener(function(windowID) {
	if (windowID == -1) {
		return;
	}

	chrome.windows.get(windowID, { populate: true }, function(window) {
		if (window.type == 'normal') {
			lastFocusedWindowID = windowID;
		}

		console.log("Last focused window is now ID " + window.id);

		updateWindow(window);
	});
});

chrome.contextMenus.removeAll(function() {
	chrome.contextMenus.create({
	    "title": "Tiles Options",
	    "documentUrlPatterns": [chrome.extension.getURL("/") + "*"],
	    "contexts": ["page", "link"],
	    "onclick" : function() {
	    	goToOptionsPage(true)
	    }
	});
});

function goToOptionsPage(newTab) {
	var optionsURL = chrome.extension.getURL("options/options.html");

	if (newTab) {
		chrome.tabs.create({
			url: optionsURL
		});
	} else {
		chrome.tabs.getCurrent(function(tab) {
			chrome.tabs.update(tab.id, { url : optionsURL })
		});
	}
}

chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
	console.log('Message received: ' + request.message);

	if (request.message == "getUrl") {
		getFocusedTab(function(tab) {
			if (tab) {
				sendResponse({ url: tab.url });

				console.log('Sent URL – ' + tab.url);
			}
		});
	} else if (request.message == "saved") {
		getFocusedTab(function(tab) {
			if (tab) {
				setPopup(false, false, tab.id);
				changeIcon(true, null, tab.id);

				sendResponse({message: "success"});

				console.log('Sent success');

				updateAllWindows();
			}
		});
	} else if (request.message == "delete") {
		console.log('Deleting...');

		getFocusedTab(function(tab) {
			if (!tab) {
				return;
			}

			function deleteSiteCallback() {
				if (!request.url) {
					setPopup(true, false, tab.id);
					changeIcon(false, null, tab.id);
				}

				sendResponse({message: "deleted"});

				console.log('Sent deleted');

				updateAllWindows();
			}

			if (request.url == undefined) {
				console.log("Deleting site " + tab.url);
				deleteSite(tab.url, deleteSiteCallback);
			} else {
				console.log("Deleting site " + request.url);
				deleteSite(request.url, deleteSiteCallback);
			}
		});
	}

	return true;
});

function getFocusedTab(callback) {
	chrome.windows.get(lastFocusedWindowID, { populate: true }, function(window) {
		if (window && window.type != 'normal') {
			return callback(null);
		}

		var tabs = window.tabs;

		for (var j = 0; j < tabs.length; j++) {
			if (tabs[j].active) {
				return callback(tabs[j]);
			}
		}
	});
}

function setPopup(save, error, tabID) {
	var details = {};

	if (error) {
		details.popup = 'browserAction/error.html';
	} else {
		if (save) {
			details.popup = 'browserAction/save.html';
		} else {
			details.popup = 'browserAction/delete.html';
		}
	}

	details.tabId = tabID;

	chrome.browserAction.setPopup(details);

	// console.log('Popup is set to ' + details.popup);
}

function updateWindow(window) {
	if (window && window.type == 'normal') {
		var tabs = window.tabs;

		for (var i = 0; i < tabs.length; i++) {
			if (tabs[i].active) {
				if (window.focused) {
					console.log("Window ID " + window.id + " is focused on active tab on ID: " + tabs[i].id + " which is at " + tabs[i].url);
				} else {
					console.log("Window ID " + window.id + " is has active tab ID: " + tabs[i].id + " which is at " + tabs[i].url);
				}

				updateTab(tabs[i]);

				break;
			}
		}
	}
}

function updateAllWindows() {
	chrome.windows.getAll({ populate: true }, function(windows) {
		console.log('Updating all windows...');

		for (var i = 0; i < windows.length; i++) {
			updateWindow(windows[i]);
		}
	});
}

function updateTab(tab) {
	console.log("Updating tab ID: " + tab.id + " which is at " + tab.url);

	if (isChromeURL(tab.url)) {
		setPopup(false, true, tab.id);
	} else {
		siteExists(tab.url, function(exists) {
			setPopup(!exists, false, tab.id);

			changeIcon(exists, null, tab.id);
		});
	}
}

function isChromeURL(url) {
	return url.substring(0, 6) == 'chrome';
}

function siteExists(url, callback) {
	getSites(function(sites) {
		if (sites) {
			for (var i = 0; i < sites.length; i++) {
				if (sites[i].url == url) {
					return callback(true);
				}
			}
		}

		return callback(false);
	});
}

function changeIcon(colors, callback, tabID) {
	var details = {};

	if (colors) {
		details.path = '../icons/icon-bitty.png';
	} else {
		details.path = '../icons/icon-bitty-gray.png';
	}

	console.log("Changing icon for " + tabID + " to " + details.path);

	details.tabId = tabID;

	chrome.browserAction.setIcon(details, callback);
}<|endoftext|>