<commit_before>module("Discourse.Post");

var buildPost = function(args) {
  return Discourse.Post.create(_.merge({
    id: 1,
    can_delete: true,
    version: 1
  }, args || {}));
};

test('defaults', function() {
  var post = Discourse.Post.create({id: 1});
  blank(post.get('deleted_at'), "it has no deleted_at by default");
  blank(post.get('deleted_by'), "there is no deleted_by by default");
});

test('new_user', function() {
  var post = Discourse.Post.create({trust_level: 0});
  ok(post.get('new_user'), "post is from a new user");

  post.set('trust_level', 1);
  ok(!post.get('new_user'), "post is no longer from a new user");
});

test('firstPost', function() {
  var post = Discourse.Post.create({post_number: 1});
  ok(post.get('firstPost'), "it's the first post");

  post.set('post_number', 10);
  ok(!post.get('firstPost'), "post is no longer the first post");
});

test('updateFromPost', function() {
  var post = Discourse.Post.create({
    post_number: 1,
    raw: 'hello world'
  });

  post.updateFromPost(Discourse.Post.create({
    raw: 'different raw',
    wat: function() { return 123; }
  }));

  equal(post.get('raw'), "different raw", "raw field updated");
});

test('hasHistory', function() {
  var post = Discourse.Post.create({id: 1});
  ok(!post.get('hasHistory'), 'posts without versions have no history');
  post.set('version', 1);
  ok(!post.get('hasHistory'), 'posts with one version have no history');
  post.set('version', 2);
  ok(post.get('hasHistory'), 'posts with more than one version have a history');
});

test('destroy by staff', function() {
  var user = Discourse.User.create({username: 'staff', staff: true});
  var post = buildPost({user: user});

  this.stub(Discourse, 'ajax');
  post.destroy(user);

  present(post.get('deleted_at'), "it has a `deleted_at` field.");
  equal(post.get('deleted_by'), user, "it has the user in the `deleted_by` field");
  ok(Discourse.ajax.calledOnce, "it made an AJAX call");

  post.recover();
  blank(post.get('deleted_at'), "it clears `deleted_at` when recovering");
  blank(post.get('deleted_by'), "it clears `deleted_by` when recovering");

});

test('destroy by non-staff', function() {
  var originalCooked = "this is the original cooked value";
  var user = Discourse.User.create({username: 'evil trout'});
  var post = buildPost({user: user, cooked: originalCooked});

  this.stub(Discourse, 'ajax');
  post.destroy(user);

  ok(!post.get('can_delete'), "the post can't be deleted again in this session");
  ok(post.get('cooked') !== originalCooked, "the cooked content changed");
  equal(post.get('version'), 2, "the version number increased");
  ok(Discourse.ajax.calledOnce, "it made an AJAX call");
});

<commit_msg>fix spec<commit_after>module("Discourse.Post");

var buildPost = function(args) {
  return Discourse.Post.create(_.merge({
    id: 1,
    can_delete: true,
    version: 1
  }, args || {}));
};

test('defaults', function() {
  var post = Discourse.Post.create({id: 1});
  blank(post.get('deleted_at'), "it has no deleted_at by default");
  blank(post.get('deleted_by'), "there is no deleted_by by default");
});

test('new_user', function() {
  var post = Discourse.Post.create({trust_level: 0});
  ok(post.get('new_user'), "post is from a new user");

  post.set('trust_level', 1);
  ok(!post.get('new_user'), "post is no longer from a new user");
});

test('firstPost', function() {
  var post = Discourse.Post.create({post_number: 1});
  ok(post.get('firstPost'), "it's the first post");

  post.set('post_number', 10);
  ok(!post.get('firstPost'), "post is no longer the first post");
});

test('updateFromPost', function() {
  var post = Discourse.Post.create({
    post_number: 1,
    raw: 'hello world'
  });

  post.updateFromPost(Discourse.Post.create({
    raw: 'different raw',
    wat: function() { return 123; }
  }));

  equal(post.get('raw'), "different raw", "raw field updated");
});

test('hasHistory', function() {
  var post = Discourse.Post.create({id: 1});
  ok(!post.get('hasHistory'), 'posts without versions have no history');
  post.set('version', 1);
  ok(!post.get('hasHistory'), 'posts with one version have no history');
  post.set('version', 2);
  ok(post.get('hasHistory'), 'posts with more than one version have a history');
});

test('destroy by staff', function() {
  var user = Discourse.User.create({username: 'staff', staff: true});
  var post = buildPost({user: user});

  this.stub(Discourse, 'ajax').returns(new Em.Deferred());
  post.destroy(user);

  present(post.get('deleted_at'), "it has a `deleted_at` field.");
  equal(post.get('deleted_by'), user, "it has the user in the `deleted_by` field");
  ok(Discourse.ajax.calledOnce, "it made an AJAX call");

  post.recover();
  blank(post.get('deleted_at'), "it clears `deleted_at` when recovering");
  blank(post.get('deleted_by'), "it clears `deleted_by` when recovering");

});

test('destroy by non-staff', function() {
  var originalCooked = "this is the original cooked value";
  var user = Discourse.User.create({username: 'evil trout'});
  var post = buildPost({user: user, cooked: originalCooked});

  this.stub(Discourse, 'ajax');
  post.destroy(user);

  ok(!post.get('can_delete'), "the post can't be deleted again in this session");
  ok(post.get('cooked') !== originalCooked, "the cooked content changed");
  equal(post.get('version'), 2, "the version number increased");
  ok(Discourse.ajax.calledOnce, "it made an AJAX call");
});

<|endoftext|>


<commit_before>
// visualize/themes

define(function() {

    var themesById = {},
        themes,
        chart = dw.backend.currentChart;

    function init(_themes) {
        _.each(_themes, function(theme) {
            themesById[theme.id] = theme;
            themesById[theme.id].__loaded = false;
        });
        themes = _themes;
    }

    /*
     * loads the currently selected theme and all its parent themes
     * returns a promise that is resolved as the loading is complete
     */
    function load() {
        var dfd = $.Deferred(),
            themeid = $('#select-theme').val(),
            theme = themesById[themeid],
            needed = [themeid];

        while (theme['extends']) {
            needed.unshift(theme['extends']);
            theme = themesById[theme['extends']];
        }
        function loadNext() {
            if (needed.length > 0) {
                var next = themesById[needed.shift()];
                if (!next.__loaded) {
                    next.__loaded = true;
                    $.getScript(next.__static_path + '/' + next.id + '.js', loadNext);
                } else {
                    loadNext();
                }
            } else {
                dw.backend.fire('theme-loaded');
                dfd.resolve();
                showThemeColors();
            }
        }
        loadNext();
        return dfd.promise();
    }

    function showThemeColors() {
        var themeid = $('#select-theme').val(),
            customColors = $('#palette-colors'),
        chart.set('metadata.publish.background', theTheme.colors.background);
        chart.set('metadata.publish.contextBg', theTheme.colors.contextBackground);
        chart.set('metadata.publish.text', theTheme.colors.text);

        var colors = theTheme.colors.palette.slice().concat(theTheme.colors.secondary || []);
        customColors.data('colors', colors.join(','));
    }

    return {
        init: init,
        load: load,
        all: function() { return themes; },
        updateUI: showThemeColors
    };

});<commit_msg>removed code for old color picker<commit_after>
// visualize/themes

define(function() {

    var themesById = {},
        themes,
        chart = dw.backend.currentChart;

    function init(_themes) {
        _.each(_themes, function(theme) {
            themesById[theme.id] = theme;
            themesById[theme.id].__loaded = false;
        });
        themes = _themes;
    }

    /*
     * loads the currently selected theme and all its parent themes
     * returns a promise that is resolved as the loading is complete
     */
    function load() {
        var dfd = $.Deferred(),
            themeid = $('#select-theme').val(),
            theme = themesById[themeid],
            needed = [themeid];

        while (theme['extends']) {
            needed.unshift(theme['extends']);
            theme = themesById[theme['extends']];
        }
        function loadNext() {
            if (needed.length > 0) {
                var next = themesById[needed.shift()];
                if (!next.__loaded) {
                    next.__loaded = true;
                    $.getScript(next.__static_path + '/' + next.id + '.js', loadNext);
                } else {
                    loadNext();
                }
            } else {
                dw.backend.fire('theme-loaded');
                dfd.resolve();
                showThemeColors();
            }
        }
        loadNext();
        return dfd.promise();
    }

    function showThemeColors() {
        var theme_id = chart.get('theme'),
            theme = dw.theme(theme_id);

        chart.set('metadata.publish.background', theme.colors.background);
        chart.set('metadata.publish.contextBg', theme.colors.contextBackground);
        chart.set('metadata.publish.text', theme.colors.text);

    }

    return {
        init: init,
        load: load,
        all: function() { return themes; },
        updateUI: showThemeColors
    };

});<|endoftext|>


<commit_before>const _ = require('lodash')
const joi = require('joi')
const runtimeConfig = require('../../models/runtimeConfig')
const joiValidate = require('../../utils/joiValidate')

const bodySchema = {
  active: joi.boolean(),
  recording: joi.boolean(),
  dump: joi.boolean(),
  sleep: joi.number().integer().max(60),
  disabledProjects: joi.array().items(
    joi.string().lowercase().token().max(64),
    joi.any().strip()
  )
}

module.exports = function * () {
  const body = joiValidate(this.request.body, bodySchema)
  body.disabledProjects = _.sortedUniq(body.disabledProjects || [])
  yield runtimeConfig.set(body)
  this.body = null
}
<commit_msg>fix(config): sleep can not be lower than zero<commit_after>const _ = require('lodash')
const joi = require('joi')
const runtimeConfig = require('../../models/runtimeConfig')
const joiValidate = require('../../utils/joiValidate')

const bodySchema = {
  active: joi.boolean(),
  recording: joi.boolean(),
  dump: joi.boolean(),
  sleep: joi.number().integer().max(60),
  disabledProjects: joi.array().items(
    joi.string().lowercase().token().max(64),
    joi.any().strip()
  )
}

module.exports = function * () {
  const body = joiValidate(this.request.body, bodySchema)
  if (body.disabledProjects !== undefined) {
    body.disabledProjects = _.sortedUniq(body.disabledProjects || [])
  }
  if (body.sleep !== undefined) {
    body.sleep = body.sleep < 0 ? 0 : body.sleep
  }
  yield runtimeConfig.set(body)
  this.body = null
}
<|endoftext|>


<commit_before>//-------------------------------------------------------------------------------
// Annotations
//-------------------------------------------------------------------------------

//@Export('bugpack-registry.BugPackRegistryBuilder')

//@Require('Class')
//@Require('List')
//@Require('Map')
//@Require('Obj')
//@Require('Proxy')
//@Require('Set')
//@Require('buganno.BugAnno')
//@Require('bugflow.BugFlow')
//@Require('bugfs.BugFs')
//@Require('bugfs.FileFinder')
//@Require('bugpack-registry.BugPackRegistry')
//@Require('bugpack-registry.BugPackRegistryEntry')

//-------------------------------------------------------------------------------
// Context
//-------------------------------------------------------------------------------

require('bugpack').context("*", function(bugpack) {

    //-------------------------------------------------------------------------------
    // BugPack
    //-------------------------------------------------------------------------------

    var Class                               = bugpack.require('Class');
    var List                                = bugpack.require('List');
    var Map                                 = bugpack.require('Map');
    var Obj                                 = bugpack.require('Obj');
    var Proxy                               = bugpack.require('Proxy');
    var Set                                 = bugpack.require('Set');
    var BugAnno                             = bugpack.require('buganno.BugAnno');
    var BugFlow                             = bugpack.require('bugflow.BugFlow');
    var BugFs                               = bugpack.require('bugfs.BugFs');
    var FileFinder                          = bugpack.require('bugfs.FileFinder');
    var BugPackRegistry                     = bugpack.require('bugpack-registry.BugPackRegistry');
    var BugPackRegistryEntry                = bugpack.require('bugpack-registry.BugPackRegistryEntry');

    //-------------------------------------------------------------------------------
    // Simplify References
    //-------------------------------------------------------------------------------

    var $series                             = BugFlow.$series;
    var $task                               = BugFlow.$task;

    //-------------------------------------------------------------------------------
    // Declare Class
    //-------------------------------------------------------------------------------

    /**
     * @class
     * @extends {Obj}
     */
    var BugPackRegistryBuilder = Class.extend(Obj, {

        _name: "bugpack-registry.BugPackRegistryBuilder",

        //-------------------------------------------------------------------------------
        // Constructor
        //-------------------------------------------------------------------------------

        /**
         * @constructs
         */
        _constructor: function() {

            this._super();

            //-------------------------------------------------------------------------------
            // Private Properties
            //-------------------------------------------------------------------------------

            /**
             * @private
             * @type {BugAnno}
             */
            this.bugAnno    = new BugAnno();
        },

        //-------------------------------------------------------------------------------
        // Public Methods
        //-------------------------------------------------------------------------------

        /**
         * @param {(string | Path)} registryRoot
         * @param {Array.<(string | RegExp)>} ignorePatterns
         * @param {function(Throwable, BugPackRegistry=)} callback
         */
        buildRegistry: function(registryRoot, ignorePatterns, callback) {
            var _this               = this;
            var registryRootPath    = BugFs.path(registryRoot);
            var bugPackRegistry     = null;
            var fileFinder          = new FileFinder([".*\\.js"], ignorePatterns);
            var filePaths           = null;
            $series([
                $task(function(flow) {
                    fileFinder.scan([registryRootPath], function(error, _filePaths) {
                        if (!error) {
                            filePaths = _filePaths;
                        }
                        flow.complete(error);
                    });
                }),
                $task(function(flow) {
                    _this.bugAnno.parse(filePaths, function(error, annotationRegistryLibrary) {
                        if (!error) {
                            bugPackRegistry = _this.generateBugPackRegistry(registryRootPath, annotationRegistryLibrary);
                        }
                        flow.complete(error);
                    });
                })
            ]).execute(function(error) {
                if (!error) {
                    callback(null, bugPackRegistry);
                } else {
                    callback(error);
                }
            });
        },

        /**
         * @param {function(Throwable=)} callback
         */
        deinitialize: function(callback) {
            this.bugAnno.deinitialize(callback);
        },

        /**
         * @param {function(Throwable=)} callback
         */
        initialize: function(callback) {
            this.bugAnno.initialize(callback);
        },

        //-------------------------------------------------------------------------------
        // Private Methods
        //-------------------------------------------------------------------------------

        /**
         * @private
         * @param {AnnotationRegistry} annotationRegistry
         * @return {boolean}
         */
        findAutoload: function(annotationRegistry) {
            var annotationList = annotationRegistry.getAnnotationList();
            for (var i = 0, size = annotationList.getCount(); i < size; i++) {
                var annotation = annotationList.getAt(i);
                var type = annotation.getAnnotationType();
                if (type === "Autoload") {
                    return true;
                }
            }
            return false;
        },

        /**
         * @private
         * @param {AnnotationRegistry} annotationRegistry
         * @return {Set.<string>}
         */
        findExportSet: function(annotationRegistry) {
            var exportSet = new Set();
            annotationRegistry.getAnnotationList().forEach(function(annotation) {
                var type = annotation.getAnnotationType();
                var argumentList = annotation.getArgumentList();
                if (type === 'Export') {
                    var exportName = argumentList.getAt(0);
                    exportSet.add(exportName);
                }
            });
            return exportSet;
        },

        /**
         * @private
         * @param {AnnotationRegistry} annotationRegistry
         * @return {Set.<string>}
         */
        findRequireSet: function(annotationRegistry) {
            var requireSet = new Set();
            annotationRegistry.getAnnotationList().forEach(function(annotation) {
                var type = annotation.getAnnotationType();
                var argumentList = annotation.getArgumentList();
                if (type === 'Require') {
                    var requireName = argumentList.getAt(0);
                    requireSet.add(requireName);
                }
            });
            return requireSet;
        },

        /**
         * @private
         * @param {Path} registryRootPath
         * @param {AnnotationRegistryLibrary} annotationRegistryLibrary
         * @return {BugPackRegistry}
         */
        generateBugPackRegistry: function(registryRootPath, annotationRegistryLibrary) {
            var _this = this;
            var bugPackRegistry = new BugPackRegistry(registryRootPath);
            annotationRegistryLibrary.getAnnotationRegistryList().forEach(function(annotationRegistry) {
                _this.generateBugPackRegistryEntry(bugPackRegistry, annotationRegistry);
            });

            return bugPackRegistry;
        },

        /**
         * @private
         * @param {BugPackRegistry} bugPackRegistry
         * @param {AnnotationRegistry} annotationRegistry
         */
        generateBugPackRegistryEntry: function(bugPackRegistry, annotationRegistry) {
            var bugPackRegistryEntry = new BugPackRegistryEntry(bugPackRegistry,
                BugFs.path(BugFs.relativePath(bugPackRegistry.getRegistryRootPath(), annotationRegistry.getFilePath())));
            var exportSet   = this.findExportSet(annotationRegistry);
            var requireSet  = this.findRequireSet(annotationRegistry);
            var autoload    = this.findAutoload(annotationRegistry);
            bugPackRegistryEntry.addAllExports(exportSet);
            bugPackRegistryEntry.addAllRequires(requireSet);
            bugPackRegistryEntry.setAutoload(autoload);
            bugPackRegistry.addRegistryEntry(bugPackRegistryEntry);
        }
    });

    //-------------------------------------------------------------------------------
    // Private Static Variables
    //-------------------------------------------------------------------------------

    /**
     * @static
     * @private
     * @type {BugPackRegistryBuilder}
     */
    BugPackRegistryBuilder.instance = null;

    //-------------------------------------------------------------------------------
    // Public Static Methods
    //-------------------------------------------------------------------------------

    /**
     * @static
     * @return {BugPackRegistryBuilder}
     */
    BugPackRegistryBuilder.getInstance = function() {
        if (BugPackRegistryBuilder.instance === null) {
            BugPackRegistryBuilder.instance = new BugPackRegistryBuilder();
        }
        return BugPackRegistryBuilder.instance;
    };

    //-------------------------------------------------------------------------------
    // Static Proxy
    //-------------------------------------------------------------------------------

    Proxy.proxy(BugPackRegistryBuilder, Proxy.method(BugPackRegistryBuilder.getInstance), [
        "buildRegistry",
        "deinitialize",
        "initialize"
    ]);

    //-------------------------------------------------------------------------------
    // Exports
    //-------------------------------------------------------------------------------

    bugpack.export('bugpack-registry.BugPackRegistryBuilder', BugPackRegistryBuilder);
});
<commit_msg>Comment changes<commit_after>//-------------------------------------------------------------------------------
// Annotations
//-------------------------------------------------------------------------------

//@Export('bugpack-registry.BugPackRegistryBuilder')

//@Require('Class')
//@Require('List')
//@Require('Map')
//@Require('Obj')
//@Require('Proxy')
//@Require('Set')
//@Require('buganno.BugAnno')
//@Require('bugflow.BugFlow')
//@Require('bugfs.BugFs')
//@Require('bugfs.FileFinder')
//@Require('bugpack-registry.BugPackRegistry')
//@Require('bugpack-registry.BugPackRegistryEntry')

//-------------------------------------------------------------------------------
// Context
//-------------------------------------------------------------------------------

require('bugpack').context("*", function(bugpack) {

    //-------------------------------------------------------------------------------
    // BugPack
    //-------------------------------------------------------------------------------

    var Class                               = bugpack.require('Class');
    var List                                = bugpack.require('List');
    var Map                                 = bugpack.require('Map');
    var Obj                                 = bugpack.require('Obj');
    var Proxy                               = bugpack.require('Proxy');
    var Set                                 = bugpack.require('Set');
    var BugAnno                             = bugpack.require('buganno.BugAnno');
    var BugFlow                             = bugpack.require('bugflow.BugFlow');
    var BugFs                               = bugpack.require('bugfs.BugFs');
    var FileFinder                          = bugpack.require('bugfs.FileFinder');
    var BugPackRegistry                     = bugpack.require('bugpack-registry.BugPackRegistry');
    var BugPackRegistryEntry                = bugpack.require('bugpack-registry.BugPackRegistryEntry');

    //-------------------------------------------------------------------------------
    // Simplify References
    //-------------------------------------------------------------------------------

    var $series                             = BugFlow.$series;
    var $task                               = BugFlow.$task;

    //-------------------------------------------------------------------------------
    // Declare Class
    //-------------------------------------------------------------------------------

    /**
     * @class
     * @extends {Obj}
     */
    var BugPackRegistryBuilder = Class.extend(Obj, {

        _name: "bugpack-registry.BugPackRegistryBuilder",

        //-------------------------------------------------------------------------------
        // Constructor
        //-------------------------------------------------------------------------------

        /**
         * @constructs
         */
        _constructor: function() {

            this._super();

            //-------------------------------------------------------------------------------
            // Private Properties
            //-------------------------------------------------------------------------------

            /**
             * @private
             * @type {BugAnno}
             */
            this.bugAnno    = new BugAnno();
        },

        //-------------------------------------------------------------------------------
        // Public Methods
        //-------------------------------------------------------------------------------

        /**
         * @param {(string | Path)} registryRoot
         * @param {Array.<(string | RegExp)>} ignorePatterns
         * @param {function(Throwable, BugPackRegistry=)} callback
         */
        buildRegistry: function(registryRoot, ignorePatterns, callback) {
            var _this               = this;
            var registryRootPath    = BugFs.path(registryRoot);
            var bugPackRegistry     = null;
            var fileFinder          = new FileFinder([".*\\.js"], ignorePatterns);
            var filePaths           = null;
            $series([
                $task(function(flow) {
                    fileFinder.scan([registryRootPath], function(error, _filePaths) {
                        if (!error) {
                            filePaths = _filePaths;
                        }
                        flow.complete(error);
                    });
                }),
                $task(function(flow) {
                    _this.bugAnno.parse(filePaths, function(error, annotationRegistryLibrary) {
                        if (!error) {
                            bugPackRegistry = _this.generateBugPackRegistry(registryRootPath, annotationRegistryLibrary);
                        }
                        flow.complete(error);
                    });
                })
            ]).execute(function(error) {
                if (!error) {
                    callback(null, bugPackRegistry);
                } else {
                    callback(error);
                }
            });
        },

        /**
         * @param {function(Throwable=)} callback
         */
        deinitialize: function(callback) {
            this.bugAnno.deinitialize(callback);
        },

        /**
         * @param {function(Throwable=)} callback
         */
        initialize: function(callback) {
            this.bugAnno.initialize(callback);
        },

        //-------------------------------------------------------------------------------
        // Private Methods
        //-------------------------------------------------------------------------------

        /**
         * @private
         * @param {AnnotationRegistry} annotationRegistry
         * @return {boolean}
         */
        findAutoload: function(annotationRegistry) {
            var annotationList = annotationRegistry.getAnnotationList();
            for (var i = 0, size = annotationList.getCount(); i < size; i++) {
                var annotation = annotationList.getAt(i);
                var type = annotation.getAnnotationType();
                if (type === "Autoload") {
                    return true;
                }
            }
            return false;
        },

        /**
         * @private
         * @param {AnnotationRegistry} annotationRegistry
         * @return {Set.<string>}
         */
        findExportSet: function(annotationRegistry) {
            var exportSet = new Set();
            annotationRegistry.getAnnotationList().forEach(function(annotation) {
                var type = annotation.getAnnotationType();
                var argumentList = annotation.getArgumentList();
                if (type === 'Export') {
                    var exportName = argumentList.getAt(0);
                    exportSet.add(exportName);
                }
            });
            return exportSet;
        },

        /**
         * @private
         * @param {AnnotationRegistry} annotationRegistry
         * @return {Set.<string>}
         */
        findRequireSet: function(annotationRegistry) {
            var requireSet = new Set();
            annotationRegistry.getAnnotationList().forEach(function(annotation) {
                var type = annotation.getAnnotationType();
                var argumentList = annotation.getArgumentList();
                if (type === 'Require') {
                    var requireName = argumentList.getAt(0);
                    requireSet.add(requireName);
                }
            });
            return requireSet;
        },

        /**
         * @private
         * @param {Path} registryRootPath
         * @param {AnnotationRegistryLibrary} annotationRegistryLibrary
         * @return {BugPackRegistry}
         */
        generateBugPackRegistry: function(registryRootPath, annotationRegistryLibrary) {
            var _this = this;
            var bugPackRegistry = new BugPackRegistry(registryRootPath);
            annotationRegistryLibrary.getAnnotationRegistryList().forEach(function(annotationRegistry) {
                _this.generateBugPackRegistryEntry(bugPackRegistry, annotationRegistry);
            });

            return bugPackRegistry;
        },

        /**
         * @private
         * @param {BugPackRegistry} bugPackRegistry
         * @param {AnnotationRegistry} annotationRegistry
         */
        generateBugPackRegistryEntry: function(bugPackRegistry, annotationRegistry) {
            var bugPackRegistryEntry = new BugPackRegistryEntry(bugPackRegistry,
                BugFs.path(BugFs.relativePath(bugPackRegistry.getRegistryRootPath(), annotationRegistry.getFilePath())));
            var exportSet   = this.findExportSet(annotationRegistry);
            var requireSet  = this.findRequireSet(annotationRegistry);
            var autoload    = this.findAutoload(annotationRegistry);
            bugPackRegistryEntry.addAllExports(exportSet);
            bugPackRegistryEntry.addAllRequires(requireSet);
            bugPackRegistryEntry.setAutoload(autoload);
            bugPackRegistry.addRegistryEntry(bugPackRegistryEntry);
        }
    });

    //-------------------------------------------------------------------------------
    // Private Static Properties
    //-------------------------------------------------------------------------------

    /**
     * @static
     * @private
     * @type {BugPackRegistryBuilder}
     */
    BugPackRegistryBuilder.instance = null;

    //-------------------------------------------------------------------------------
    // Public Static Methods
    //-------------------------------------------------------------------------------

    /**
     * @static
     * @return {BugPackRegistryBuilder}
     */
    BugPackRegistryBuilder.getInstance = function() {
        if (BugPackRegistryBuilder.instance === null) {
            BugPackRegistryBuilder.instance = new BugPackRegistryBuilder();
        }
        return BugPackRegistryBuilder.instance;
    };

    //-------------------------------------------------------------------------------
    // Static Proxy
    //-------------------------------------------------------------------------------

    Proxy.proxy(BugPackRegistryBuilder, Proxy.method(BugPackRegistryBuilder.getInstance), [
        "buildRegistry",
        "deinitialize",
        "initialize"
    ]);

    //-------------------------------------------------------------------------------
    // Exports
    //-------------------------------------------------------------------------------

    bugpack.export('bugpack-registry.BugPackRegistryBuilder', BugPackRegistryBuilder);
});
<|endoftext|>


<commit_before>YUI.add('target-action-events', function (Y) {

    var Events = function (emitter) {
        this.emmitter = emitter;
        this.events = {};
    };

    Events.prototype = {
        /* Calls any subscribed actions to a target's action
         * @param {string} target The target.
         * @param {string} targetAction The target's action.
         */
        fire: function (target, targetAction) {
            var subscribedActions = this.events[target] && this.events[target][targetAction],
                i,
                actionArguments = [
                    {
                        target: target,
                        targetAction: targetAction,
                        emitter: this.emitter
                    }
                ];
            Array.prototype.push.apply(actionArguments, Array.prototype.slice.call(arguments, 0).splice(2, arguments.length - 2));
            if (subscribedActions) {
                for (i = 0; i < subscribedActions.length; i++) {
                    subscribedActions[i].apply(this, actionArguments);
                }
            }
        },

        /* Sets an action subscribed to the targets specified
         * @param {object} targets The targets to listen to.
         * @param {function} subscribedActon The action that subscribes to the specified targets.
         * @return {Event.subscription} The subscription to the targets subscribed to. This object
         * can be used to unsubscribe from the targets.
         */
        subscribe: function (targets, subscribedAction) {
            var pipeline = this.pipeline,
                i,
                target,
                targetAction,
                subscription = new Events.Subscription(this.events, targets, subscribedAction);

            for (target in targets) {
                this.events[target] = this.events[target] || {};
                for (i = 0; i < targets[target].length; i++) {
                    targetAction = targets[target][i];
                    this.events[target][targetAction] = this.events[target][targetAction] || [];
                    this.events[target][targetAction].push(subscribedAction);
                }
            }

            return subscription;
        }
    };

    Events.Subscription = function (events, targets, subscribedAction) {
        this.events = events;
        this.targets = targets;
        this.subscribedAction = subscribedAction;
    };

    Events.Subscription.prototype = {
        /* Unsubscribes from all target actions in the subscription by removing
         * the subscribed action
         */
        unsubscribe: function () {
            var targetId,
                i,
                targetAction,
                actionArray,
                subscribedActionIndex;
            for (target in this.targets) {
                for (i = 0; i < this.targets[target].length; i++) {
                    targetAction = this.targets[target][i];
                    subscribedActions = this.events[target][targetAction];
                    subscribedActionIndex = subscribedActions.indexOf(this.subscribedAction);
                    if (subscribedActionIndex !== -1) {
                        subscribedActions.splice(subscribedActionIndex, 1);
                    }
                }
            }
        }
    };

    Y.namespace('Pipeline').Events = Events;
}, '0.0.1', {
    requires: [
    ]
});
<commit_msg>Simplified getting arguments.<commit_after>YUI.add('target-action-events', function (Y) {

    var Events = function (emitter) {
        this.emmitter = emitter;
        this.events = {};
    };

    Events.prototype = {
        /* Calls any subscribed actions to a target's action
         * @param {string} target The target.
         * @param {string} targetAction The target's action.
         */
        fire: function (target, targetAction) {
            var subscribedActions = this.events[target] && this.events[target][targetAction],
                i,
                actionArguments = [
                    {
                        target: target,
                        targetAction: targetAction,
                        emitter: this.emitter
                    }
                ];
            Array.prototype.push.apply(actionArguments, Array.prototype.slice.call(arguments, 0).slice(2));
            if (subscribedActions) {
                for (i = 0; i < subscribedActions.length; i++) {
                    subscribedActions[i].apply(this, actionArguments);
                }
            }
        },

        /* Sets an action subscribed to the targets specified
         * @param {object} targets The targets to listen to.
         * @param {function} subscribedActon The action that subscribes to the specified targets.
         * @return {Event.subscription} The subscription to the targets subscribed to. This object
         * can be used to unsubscribe from the targets.
         */
        subscribe: function (targets, subscribedAction) {
            var pipeline = this.pipeline,
                i,
                target,
                targetAction,
                subscription = new Events.Subscription(this.events, targets, subscribedAction);

            for (target in targets) {
                this.events[target] = this.events[target] || {};
                for (i = 0; i < targets[target].length; i++) {
                    targetAction = targets[target][i];
                    this.events[target][targetAction] = this.events[target][targetAction] || [];
                    this.events[target][targetAction].push(subscribedAction);
                }
            }

            return subscription;
        }
    };

    Events.Subscription = function (events, targets, subscribedAction) {
        this.events = events;
        this.targets = targets;
        this.subscribedAction = subscribedAction;
    };

    Events.Subscription.prototype = {
        /* Unsubscribes from all target actions in the subscription by removing
         * the subscribed action
         */
        unsubscribe: function () {
            var targetId,
                i,
                targetAction,
                actionArray,
                subscribedActionIndex;
            for (target in this.targets) {
                for (i = 0; i < this.targets[target].length; i++) {
                    targetAction = this.targets[target][i];
                    subscribedActions = this.events[target][targetAction];
                    subscribedActionIndex = subscribedActions.indexOf(this.subscribedAction);
                    if (subscribedActionIndex !== -1) {
                        subscribedActions.splice(subscribedActionIndex, 1);
                    }
                }
            }
        }
    };

    Y.namespace('Pipeline').Events = Events;
}, '0.0.1', {
    requires: [
    ]
});
<|endoftext|>


<commit_before>var pull = require('pull-stream')
var crypto = require('crypto')
var Reconnect = require('pull-reconnect')

function Hash (onHash) {
  var hash = crypto.createHash('sha256')
  return pull.through(function (data) {
    hash.update(
        'string' === typeof data
      ? new Buffer(data, 'utf8')
      : data
    )
  }, function (err) {
    if(err && !onHash) throw err
    onHash && onHash(err, '&'+hash.digest('base64')+'.sha256')
  })
}
var createClient = require('ssb-client')

module.exports = function () {
  var sbot = null
  var rec = Reconnect(function (isConn) {
    createClient(function (err, _sbot) {
      if(err) return isConn(err)
      sbot = _sbot
      sbot.on('closed', function () {
        sbot = null
        isConn(new Error('closed'))
      })
      isConn()
    })
  })

  return {
    sbot_blobs_add: rec.sink(function (cb) {
      return pull(
        Hash(cb),
        sbot.blobs.add()
      )
    }),
    sbot_links: rec.source(function (query) {
      return sbot.links(query)
    }),
    sbot_links2: rec.source(function (query) {
      return sbot.links2.read(query)
    }),
    sbot_log: rec.source(function (opts) {
      return sbot.createLogStream(opts)
    }),
    sbot_user_feed: rec.source(function (opts) {
      return sbot.createUserStream(opts)
    }),
    sbot_get: rec.async(function (key, cb) {
      sbot.get(key, cb)
    }),
    sbot_publish: rec.async(function (msg, cb) {
      sbot.publish(msg, cb)
    }),
    sbot_whoami: rec.async(function (cb) {
      sbot.whoami(cb)
    })
  }
}

<commit_msg>log errors when connecting<commit_after>var pull = require('pull-stream')
var crypto = require('crypto')
var Reconnect = require('pull-reconnect')

function Hash (onHash) {
  var hash = crypto.createHash('sha256')
  return pull.through(function (data) {
    hash.update(
        'string' === typeof data
      ? new Buffer(data, 'utf8')
      : data
    )
  }, function (err) {
    if(err && !onHash) throw err
    onHash && onHash(err, '&'+hash.digest('base64')+'.sha256')
  })
}
var createClient = require('ssb-client')

module.exports = function () {
  var sbot = null
  var rec = Reconnect(function (isConn) {
    createClient(function (err, _sbot) {
      if(err) return {console.error(err.stack); isConn(err)}
      sbot = _sbot
      sbot.on('closed', function () {
        sbot = null
        isConn(new Error('closed'))
      })
      isConn()
    })
  })

  return {
    sbot_blobs_add: rec.sink(function (cb) {
      return pull(
        Hash(cb),
        sbot.blobs.add()
      )
    }),
    sbot_links: rec.source(function (query) {
      return sbot.links(query)
    }),
    sbot_links2: rec.source(function (query) {
      return sbot.links2.read(query)
    }),
    sbot_log: rec.source(function (opts) {
      return sbot.createLogStream(opts)
    }),
    sbot_user_feed: rec.source(function (opts) {
      return sbot.createUserStream(opts)
    }),
    sbot_get: rec.async(function (key, cb) {
      sbot.get(key, cb)
    }),
    sbot_publish: rec.async(function (msg, cb) {
      sbot.publish(msg, cb)
    }),
    sbot_whoami: rec.async(function (cb) {
      sbot.whoami(cb)
    })
  }
}

<|endoftext|>


<commit_before>define(["Test", "Tone/core/Draw", "Tone/core/Tone"],
	function (Test, Draw, Tone) {

	describe("Draw", function(){

		//replace rAF since test is not executed in focus
		var originalRAF = window.requestAnimationFrame;

		window.requestAnimationFrame = function(cb){
			setTimeout(cb, 16);
		};

		after(function(){
			//re-set rAF
			window.requestAnimationFrame = originalRAF;
		});

		it ("can schedule a callback at a AudioContext time", function(done){
			var scheduledTime = Tone.now() + 0.2;
			Draw.schedule(function(){
				expect(Tone.now()).to.be.closeTo(scheduledTime, 0.05);
				done();
			}, scheduledTime);
		});

		it ("can schedule multiple callbacks", function(done){
			var callbackCount = 0;
			var firstEvent = Tone.now() + 0.1;
			Draw.schedule(function(){
				callbackCount++;
				expect(Tone.now()).to.be.closeTo(firstEvent, 0.05);
			}, firstEvent);

			var thirdEvent = Tone.now() + 0.3;
			Draw.schedule(function(){
				callbackCount++;
				expect(Tone.now()).to.be.closeTo(thirdEvent, 0.05);
				expect(callbackCount).to.equal(3);
				done();
			}, thirdEvent);

			var secondEvent = Tone.now() + 0.2;
			Draw.schedule(function(){
				callbackCount++;
				expect(Tone.now()).to.be.closeTo(secondEvent, 0.05);
			}, secondEvent);
		});

		it ("can cancel scheduled events", function(done){
			var callbackCount = 0;
			Draw.schedule(function(){
				callbackCount++;
			}, Tone.now() + 0.1);

			Draw.schedule(function(){
				throw new Error("should not call this method");
			}, Tone.now() + 0.2);

			Draw.schedule(function(){
				throw new Error("should not call this method");
			}, Tone.now() + 0.25);

			//cancel the second and third events
			Draw.cancel(Tone.now() + 0.15);

			//schedule another one after
			Draw.schedule(function(){
				callbackCount++;
				expect(callbackCount).to.equal(2);
				done();
			}, Tone.now() + 0.3);

		});
	});
});
<commit_msg>Revert "replacing rAF for tests bc CI test is not executed in focus"<commit_after>define(["Test", "Tone/core/Draw", "Tone/core/Tone"],
	function (Test, Draw, Tone) {

	describe("Draw", function(){

		it ("can schedule a callback at a AudioContext time", function(done){
			var scheduledTime = Tone.now() + 0.2;
			Draw.schedule(function(){
				expect(Tone.now()).to.be.closeTo(scheduledTime, 0.05);
				done();
			}, scheduledTime);
		});

		it ("can schedule multiple callbacks", function(done){
			var callbackCount = 0;
			var firstEvent = Tone.now() + 0.1;
			Draw.schedule(function(){
				callbackCount++;
				expect(Tone.now()).to.be.closeTo(firstEvent, 0.05);
			}, firstEvent);

			var thirdEvent = Tone.now() + 0.3;
			Draw.schedule(function(){
				callbackCount++;
				expect(Tone.now()).to.be.closeTo(thirdEvent, 0.05);
				expect(callbackCount).to.equal(3);
				done();
			}, thirdEvent);

			var secondEvent = Tone.now() + 0.2;
			Draw.schedule(function(){
				callbackCount++;
				expect(Tone.now()).to.be.closeTo(secondEvent, 0.05);
			}, secondEvent);
		});

		it ("can cancel scheduled events", function(done){
			var callbackCount = 0;
			Draw.schedule(function(){
				callbackCount++;
			}, Tone.now() + 0.1);

			Draw.schedule(function(){
				throw new Error("should not call this method");
			}, Tone.now() + 0.2);

			Draw.schedule(function(){
				throw new Error("should not call this method");
			}, Tone.now() + 0.25);

			//cancel the second and third events
			Draw.cancel(Tone.now() + 0.15);

			//schedule another one after
			Draw.schedule(function(){
				callbackCount++;
				expect(callbackCount).to.equal(2);
				done();
			}, Tone.now() + 0.3);

		});
	});
});<|endoftext|>


<commit_before>/* global chrome, lazyload */

function pad(num) { return num < 10 ? '0' + num : num; }

function toHumanLength(secs) {
  var mins = Math.floor(secs / 60);
  var hours = mins ? Math.floor(mins / 60) : 0;
  secs = secs % 60;
  mins = mins % 60;
  if (hours) { mins = pad(mins); }
  return (hours ? hours + ':' : '') + mins + ':' + pad(secs);
}

var now = Date.now();
var timeFormats = [
  [60, 'seconds', 1],
  [120, '1 minute ago'],
  [3600, 'minutes', 60],
  [7200, '1 hour ago'],
  [86400, 'hours', 3600],
  [172800, '1 day ago'],
  [604800, 'days', 86400],
  [1209600, 'Last week'],
  [2419200, 'weeks', 604800],
  [4838400, 'Last month'],
  [29030400, 'months', 2419200],
  [58060800, 'Last year'],
  [2903040000, 'years', 29030400]
];
function timeAgo(timestamp) {
  var seconds = (now - timestamp) / 1000;
  for (var i = 0, len = timeFormats.length; i < len; i++) {
    var f = timeFormats[i];
    if (seconds < f[0]) {
      return f[2] ? Math.floor(seconds / f[2]) + ' ' + f[1] + ' ago' : f[1];
    }
  }
  return null;
}

var months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
function showTime(timestamp) {
  var date = new Date(timestamp);
  var hour = date.getHours() % 12;
  var ampm = hour > 11 ? 'pm' : 'am';
  hour = hour % 12;
  if (hour === 0) { hour = 12; }
  return months[date.getMonth()] + ' ' + date.getDate() + ', ' +
    hour + ':' + pad(date.getMinutes()) + ampm;
}

// Inspired by mithril :)
function m(element, attr, content) {
  var s = element.split('.');
  var $el = document.createElement(s[0]);
  if (s[1]) { $el.className = s.slice(1).join(' '); }
  if (typeof attr === 'object' && !Array.isArray(attr) &&
     !(attr instanceof HTMLElement)) {
    for (var key in attr) {
      if (/^data-/.test(key)) {
        $el.setAttribute(key, attr[key]);
      } else if (key === 'className') {
        if (attr[key]) { $el.classList.add(attr[key]); }
      } else {
        $el[key] = attr[key];
      }
    }
  } else {
    content = attr;
  }
  if (Array.isArray(content)) {
    content.forEach(function(node) { if (node) { $el.appendChild(node); } });
  } else if (content instanceof HTMLElement) {
    $el.appendChild(content);
  } else if (content != null) {
    $el.textContent = content;
  }
  return $el;
}

function removeChildClasses($node, className) {
  for (var i = 0, len = $node.children.length; i < len; i++) {
    $node.children[i].classList.remove(className);
  }
}

function goToLink(e) {
  chrome.tabs.create({ url: e.target.href || e.target.parentNode.href });
}

var options = JSON.parse(localStorage.getItem('options')) || {};
var videos = JSON.parse(localStorage.getItem('videos')) || [];
var groups = [options.show_ungrouped ?
  { name: 'Other',
    videos: JSON.parse(localStorage.getItem('ungrouped')) || [],
    removable: true } :
  { name: 'All', videos: videos, removable: true }
];
var showTabs = false;

var groupedVideos = JSON.parse(localStorage.getItem('groups')) || [];
if (groupedVideos.length) { showTabs = true; }
groupedVideos.forEach(function(group) { group.removable = true; });
groups = groups.concat(groupedVideos);

var $tabs = document.getElementById('tabs').children[0];
var $content = document.getElementById('content');

// See if there's another video opened in this window.
var tabs = JSON.parse(localStorage.getItem('tabs')) || {};
var tabID, winID, queue, videoPlaying = false;
chrome.windows.getCurrent({}, function(win) {
  winID = win.id;
  var playingVideos;
  if (tabs) {
    for (var id in tabs) {
      if (tabs[id] === winID) {
        tabID = id;
        queue = JSON.parse(localStorage.getItem('queue'));
        if (queue) { queue = queue[tabID]; }
        playingVideos = JSON.parse(localStorage.getItem('playing'));
        if (playingVideos) { playingVideos = playingVideos[tabID]; }
        break;
      }
    }
  }

  // Find out what videos are queued.
  groups.forEach(function(group) {
    group.videos.forEach(function(video) {
      video.queued = queue && queue[video.url] != null;
      video.playing = playingVideos && playingVideos === video.url || false;
      videoPlaying = videoPlaying || video.playing;
    });
  });

  // Make the first tab with unwatched or queued videos selected.
  var tabSelected = false;
  for (var i = 0, len = groups.length; i < len; i++) {
    if (groups[i].videos.filter(function(v) {
        return !v.watched || v.queued; }).length) {
      groups[i].selected = true;
      tabSelected = true;
      break;
    }
  }
  if (!tabSelected) {
    groups[0].selected = true;
  }

  if (options.ignore && options.ignore.length && options.show_ignored_tab) {
    var ignoredVideos = JSON.parse(localStorage.getItem('ignored'));
    groups.push({ name: 'Ignored', videos: ignoredVideos });
    showTabs = true;
  }

  if (!showTabs) {
    $tabs.style.display = 'none';
    $content.style.marginTop = 0;
  }

  renderContent();
});

var videosMap = {};
function renderContent() {
  groups.forEach(function(group) {
    group.unwatched = group.videos.filter(function(video) {
      return !video.watched;
    }).length;
    var $badge;

    if (showTabs) {
      var $tab = m('a.tab', {
        className: group.selected && 'selected',
        onclick: function() {
          // Remember the scroll position of the last selected group.
          var selectedGroup = groups.filter(function(group) {
            return group.selected;
          })[0];
          if (selectedGroup) {
            selectedGroup.scrollTop = document.body.scrollTop;
            selectedGroup.selected = false;
          }

          removeChildClasses($tabs, 'selected');
          removeChildClasses($content, 'selected');
          $tab.classList.add('selected');

          group.selected = true;
          if (!group.rendered) {
            renderVideos(group);
          } else {
            group.$videos.classList.add('selected');
          }
          document.body.scrollTop = group.scrollTop;
        }
      }, m('span.label', group.name));
      $badge = m('span.badge', group.unwatched || '');
      $tab.appendChild($badge);
      $tabs.appendChild($tab);
    }

    // If this videos is also in other tabs, remember in case it's removed.
    group.videos.forEach(function(video) {
      (videosMap[video.url] = videosMap[video.url] || [])
        .push({ group: group, video: video, $badge: $badge });
    });

    if (group.selected) {
      renderVideos(group);
    }
  });
}

function renderVideos(group) {
  group.rendered = true;
  if (!group.videos.length) {
    $content.appendChild(group.$videos = m('div.no-videos', {
      className: group.selected && 'selected',
    }, 'No new videos'));
    return;
  }

  // Put currently playing video at the top, followed by queued videos,
  // then unwatched videos, and finally, watched videos at the bottom.
  group.videos.sort(function(a, b) {
    var play = b.playing - a.playing;
    if (play !== 0) { return play; }
    var watched = a.watched - b.watched;
    if (watched !== 0) { return watched; }
    if (queue) {
      var apos = queue[a.url];
      var bpos = queue[b.url];
      return apos != null && bpos != null ? apos - bpos :
        apos != null && bpos == null ? -1 :
        bpos != null && apos == null ?  1 : b.timestamp - a.timestamp;
    } else {
      return b.timestamp - a.timestamp;
    }
  });

  group.$videos = m('ul', {
    className: group.selected && 'selected',
  }, group.videos.map(function(video) {
    if (!options.show_watched && video.watched) { return; }

    function openNewTab() {
      chrome.tabs.create({ url: video.url }, function(tab) {
        tabs[tab.id] = winID;
        localStorage.setItem('tabs', JSON.stringify(tabs));
        chrome.runtime.sendMessage({
          play: true,
          url: video.url,
          tabID: tab.id,
        });
      });
    }

    var opening = false;
    function open() {
      if (opening) { return; }
      opening = true;

      if (video.queued) {
        // If video is in queue, wait a little to let the user know that
        // it will be removed from the queue as they open it.
        videosMap[video.url].forEach(function(g) {
          g.video.queued = false;
          if (g.video.$video) { g.video.$video.classList.remove('queued'); }
        });
        setTimeout(function() {
          opening = false;
          open();
        }, 500);
        return;
      }

      chrome.runtime.sendMessage({
        watched: true,
        url: video.url,
        tabID: tabID,
      });

      if (options.use_same_tab && tabID) {
        chrome.tabs.update(parseInt(tabID), {
          url: video.url,
          active: true
        }, function(tab) {
          if (!tab) {
            delete tabs[tabID];
            localStorage.setItem('tabs', JSON.stringify(tabs));
            openNewTab();
          } else {
            chrome.runtime.sendMessage({
              play: true,
              url: video.url,
              tabID: tab.id,
            });
            window.close();
          }
        });
      } else {
        openNewTab();
      }
    }

    var vidClass = '.source-' + video.source;
    if (video.watched) { vidClass += '.watched'; }
    if (video.queued) { vidClass += '.queued'; }
    if (video.playing) { vidClass += '.playing'; }
    var $video = m('li.video' + vidClass, [
      m('a.left-side', { href: video.url, disabled: true }, [
        m('img.lazy', { 'data-src': video.thumbnail, onclick: open }),
        video.length && m('span.length', toHumanLength(video.length)),
        video.source === 'twitch' && video.game ?
          m('a.game', {
            href: 'https://www.twitch.tv/directory/game/' +
              encodeURIComponent(video.game) + '/videos/week',
           'data-title': video.game,
           onclick: goToLink,
          }, m('img.lazy', {
            'data-src': 'http://static-cdn.jtvnw.net/ttv-boxart/' +
              encodeURIComponent(video.game) + '-138x190.jpg',
          })) : null,
        videoPlaying && m('span.queue', {
          'data-title': 'Add to Queue',
          onclick: function() {
            var message = { tabID: tabID, url: video.url };
            if (!video.queued) {
              message.queue = true;
            } else {
              message.unqueue = true;
            }
            chrome.runtime.sendMessage(message);
            videosMap[video.url].forEach(function(g) {
              g.video.queued = !video.queued;
              if (g.video.$video) {
                g.video.$video.classList.toggle('queued');
              }
            });
          },
        })
      ]),
      m('div.right-side', [
        group.removable && !video.watched && m('a.close', {
          href: '#',
          'data-title': 'Mark as Watched',
          onclick: function(e) {
            chrome.runtime.sendMessage({
              watched: true,
              url: video.url,
              tabID: tabID,
            });
            videosMap[video.url].forEach(function(g) {
              if (g.$badge) {
                g.$badge.textContent = (--g.group.unwatched) || '';
              }
              g.video.watched = true;
              var $video = g.video.$video;
              if (!$video) { return; }

              if (options.show_watched) {
                $video.classList.add('watched');

              } else {
                if ($video.offsetParent === null) {
                  $video.parentNode.removeChild($video);
                } else {
                  $video.style.height = 0;
                  setTimeout(function() {
                    $video.parentNode.removeChild($video);
                    lazyload.processScroll();
                  }, 250);
                }
              }
            });
            e.preventDefault();
          },
        }, '✖'),
        m('a.title', { href: video.url, onclick: open }, video.title),
        m('div.user', [
          m('span.favicon', { className: 'source-' + video.source }),
          video.otherSource && m('a.favicon', {
            className: 'source-' + video.otherSource.source,
            href: video.otherSource.url,
            onclick: goToLink,
          }),
          video.user.thumbnail ?
            m('img.lazy', { 'data-src': video.user.thumbnail }) : null,
          m('a.name', {
            href: video.user.url,
            onclick: goToLink,
          }, video.user.name),
          video.user.verified &&
            m('span.verified', { 'data-title': 'Verified' })
        ]),
        m('div', [
          video.timestamp && now < video.timestamp &&
            m('span.starts', showTime(video.timestamp)),
          video.timestamp && now >= video.timestamp &&
            m('span.time', timeAgo(video.timestamp)),
          video.views && m('span.views', video.views),
        ]),
        video.desc ? m('div', { innerHTML: video.desc }) : null
      ])
    ]);

    video.$video = $video;
    return $video;
  }));

  $content.appendChild(group.$videos);
  lazyload.addImages(group.$videos);
}
<commit_msg>select tab with playing vid, if no other tab selected<commit_after>/* global chrome, lazyload */

function pad(num) { return num < 10 ? '0' + num : num; }

function toHumanLength(secs) {
  var mins = Math.floor(secs / 60);
  var hours = mins ? Math.floor(mins / 60) : 0;
  secs = secs % 60;
  mins = mins % 60;
  if (hours) { mins = pad(mins); }
  return (hours ? hours + ':' : '') + mins + ':' + pad(secs);
}

var now = Date.now();
var timeFormats = [
  [60, 'seconds', 1],
  [120, '1 minute ago'],
  [3600, 'minutes', 60],
  [7200, '1 hour ago'],
  [86400, 'hours', 3600],
  [172800, '1 day ago'],
  [604800, 'days', 86400],
  [1209600, 'Last week'],
  [2419200, 'weeks', 604800],
  [4838400, 'Last month'],
  [29030400, 'months', 2419200],
  [58060800, 'Last year'],
  [2903040000, 'years', 29030400]
];
function timeAgo(timestamp) {
  var seconds = (now - timestamp) / 1000;
  for (var i = 0, len = timeFormats.length; i < len; i++) {
    var f = timeFormats[i];
    if (seconds < f[0]) {
      return f[2] ? Math.floor(seconds / f[2]) + ' ' + f[1] + ' ago' : f[1];
    }
  }
  return null;
}

var months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
function showTime(timestamp) {
  var date = new Date(timestamp);
  var hour = date.getHours() % 12;
  var ampm = hour > 11 ? 'pm' : 'am';
  hour = hour % 12;
  if (hour === 0) { hour = 12; }
  return months[date.getMonth()] + ' ' + date.getDate() + ', ' +
    hour + ':' + pad(date.getMinutes()) + ampm;
}

// Inspired by mithril :)
function m(element, attr, content) {
  var s = element.split('.');
  var $el = document.createElement(s[0]);
  if (s[1]) { $el.className = s.slice(1).join(' '); }
  if (typeof attr === 'object' && !Array.isArray(attr) &&
     !(attr instanceof HTMLElement)) {
    for (var key in attr) {
      if (/^data-/.test(key)) {
        $el.setAttribute(key, attr[key]);
      } else if (key === 'className') {
        if (attr[key]) { $el.classList.add(attr[key]); }
      } else {
        $el[key] = attr[key];
      }
    }
  } else {
    content = attr;
  }
  if (Array.isArray(content)) {
    content.forEach(function(node) { if (node) { $el.appendChild(node); } });
  } else if (content instanceof HTMLElement) {
    $el.appendChild(content);
  } else if (content != null) {
    $el.textContent = content;
  }
  return $el;
}

function removeChildClasses($node, className) {
  for (var i = 0, len = $node.children.length; i < len; i++) {
    $node.children[i].classList.remove(className);
  }
}

function goToLink(e) {
  chrome.tabs.create({ url: e.target.href || e.target.parentNode.href });
}

var options = JSON.parse(localStorage.getItem('options')) || {};
var videos = JSON.parse(localStorage.getItem('videos')) || [];
var groups = [options.show_ungrouped ?
  { name: 'Other',
    videos: JSON.parse(localStorage.getItem('ungrouped')) || [],
    removable: true } :
  { name: 'All', videos: videos, removable: true }
];
var showTabs = false;

var groupedVideos = JSON.parse(localStorage.getItem('groups')) || [];
if (groupedVideos.length) { showTabs = true; }
groupedVideos.forEach(function(group) { group.removable = true; });
groups = groups.concat(groupedVideos);

var $tabs = document.getElementById('tabs').children[0];
var $content = document.getElementById('content');

// See if there's another video opened in this window.
var tabs = JSON.parse(localStorage.getItem('tabs')) || {};
var tabID, winID, queue, videoPlaying = false;
chrome.windows.getCurrent({}, function(win) {
  winID = win.id;
  var playingVideos;
  if (tabs) {
    for (var id in tabs) {
      if (tabs[id] === winID) {
        tabID = id;
        queue = JSON.parse(localStorage.getItem('queue'));
        if (queue) { queue = queue[tabID]; }
        playingVideos = JSON.parse(localStorage.getItem('playing'));
        if (playingVideos) { playingVideos = playingVideos[tabID]; }
        break;
      }
    }
  }

  // Find out what videos are queued.
  groups.forEach(function(group) {
    group.videos.forEach(function(video) {
      video.queued = queue && queue[video.url] != null;
      video.playing = playingVideos && playingVideos === video.url || false;
      videoPlaying = videoPlaying || video.playing;
    });
  });

  // Make the first tab with unwatched or queued videos selected.
  var tabSelected = false, i, len;
  for (i = 0, len = groups.length; i < len; i++) {
    if (groups[i].videos.filter(function(v) {
        return !v.watched || v.queued; }).length) {
      groups[i].selected = true;
      tabSelected = true;
      break;
    }
  }

  // If there are no new or queued videos, look for a video that's playing.
  if (!tabSelected) {
    for (i = 0, len = groups.length; i < len; i++) {
      if (groups[i].videos.filter(function(v) { return v.playing; }).length) {
          groups[i].selected = true;
          tabSelected = true;
          break;
        }
    }
  }

  // Otherwise, select the first tab.
  if (!tabSelected) {
    groups[0].selected = true;
  }

  if (options.ignore && options.ignore.length && options.show_ignored_tab) {
    var ignoredVideos = JSON.parse(localStorage.getItem('ignored'));
    groups.push({ name: 'Ignored', videos: ignoredVideos });
    showTabs = true;
  }

  if (!showTabs) {
    $tabs.style.display = 'none';
    $content.style.marginTop = 0;
  }

  renderContent();
});

var videosMap = {};
function renderContent() {
  groups.forEach(function(group) {
    group.unwatched = group.videos.filter(function(video) {
      return !video.watched;
    }).length;
    var $badge;

    if (showTabs) {
      var $tab = m('a.tab', {
        className: group.selected && 'selected',
        onclick: function() {
          // Remember the scroll position of the last selected group.
          var selectedGroup = groups.filter(function(group) {
            return group.selected;
          })[0];
          if (selectedGroup) {
            selectedGroup.scrollTop = document.body.scrollTop;
            selectedGroup.selected = false;
          }

          removeChildClasses($tabs, 'selected');
          removeChildClasses($content, 'selected');
          $tab.classList.add('selected');

          group.selected = true;
          if (!group.rendered) {
            renderVideos(group);
          } else {
            group.$videos.classList.add('selected');
          }
          document.body.scrollTop = group.scrollTop;
        }
      }, m('span.label', group.name));
      $badge = m('span.badge', group.unwatched || '');
      $tab.appendChild($badge);
      $tabs.appendChild($tab);
    }

    // If this videos is also in other tabs, remember in case it's removed.
    group.videos.forEach(function(video) {
      (videosMap[video.url] = videosMap[video.url] || [])
        .push({ group: group, video: video, $badge: $badge });
    });

    if (group.selected) {
      renderVideos(group);
    }
  });
}

function renderVideos(group) {
  group.rendered = true;
  if (!group.videos.length) {
    $content.appendChild(group.$videos = m('div.no-videos', {
      className: group.selected && 'selected',
    }, 'No new videos'));
    return;
  }

  // Put currently playing video at the top, followed by queued videos,
  // then unwatched videos, and finally, watched videos at the bottom.
  group.videos.sort(function(a, b) {
    var play = b.playing - a.playing;
    if (play !== 0) { return play; }
    var watched = a.watched - b.watched;
    if (watched !== 0) { return watched; }
    if (queue) {
      var apos = queue[a.url];
      var bpos = queue[b.url];
      return apos != null && bpos != null ? apos - bpos :
        apos != null && bpos == null ? -1 :
        bpos != null && apos == null ?  1 : b.timestamp - a.timestamp;
    } else {
      return b.timestamp - a.timestamp;
    }
  });

  group.$videos = m('ul', {
    className: group.selected && 'selected',
  }, group.videos.map(function(video) {
    if (!options.show_watched && video.watched) { return; }

    function openNewTab() {
      chrome.tabs.create({ url: video.url }, function(tab) {
        tabs[tab.id] = winID;
        localStorage.setItem('tabs', JSON.stringify(tabs));
        chrome.runtime.sendMessage({
          play: true,
          url: video.url,
          tabID: tab.id,
        });
      });
    }

    var opening = false;
    function open() {
      if (opening) { return; }
      opening = true;

      if (video.queued) {
        // If video is in queue, wait a little to let the user know that
        // it will be removed from the queue as they open it.
        videosMap[video.url].forEach(function(g) {
          g.video.queued = false;
          if (g.video.$video) { g.video.$video.classList.remove('queued'); }
        });
        setTimeout(function() {
          opening = false;
          open();
        }, 500);
        return;
      }

      chrome.runtime.sendMessage({
        watched: true,
        url: video.url,
        tabID: tabID,
      });

      if (options.use_same_tab && tabID) {
        chrome.tabs.update(parseInt(tabID), {
          url: video.url,
          active: true
        }, function(tab) {
          if (!tab) {
            delete tabs[tabID];
            localStorage.setItem('tabs', JSON.stringify(tabs));
            openNewTab();
          } else {
            chrome.runtime.sendMessage({
              play: true,
              url: video.url,
              tabID: tab.id,
            });
            window.close();
          }
        });
      } else {
        openNewTab();
      }
    }

    var vidClass = '.source-' + video.source;
    if (video.watched) { vidClass += '.watched'; }
    if (video.queued) { vidClass += '.queued'; }
    if (video.playing) { vidClass += '.playing'; }
    var $video = m('li.video' + vidClass, [
      m('a.left-side', { href: video.url, disabled: true }, [
        m('img.lazy', { 'data-src': video.thumbnail, onclick: open }),
        video.length && m('span.length', toHumanLength(video.length)),
        video.source === 'twitch' && video.game ?
          m('a.game', {
            href: 'https://www.twitch.tv/directory/game/' +
              encodeURIComponent(video.game) + '/videos/week',
           'data-title': video.game,
           onclick: goToLink,
          }, m('img.lazy', {
            'data-src': 'http://static-cdn.jtvnw.net/ttv-boxart/' +
              encodeURIComponent(video.game) + '-138x190.jpg',
          })) : null,
        videoPlaying && m('span.queue', {
          'data-title': 'Add to Queue',
          onclick: function() {
            var message = { tabID: tabID, url: video.url };
            if (!video.queued) {
              message.queue = true;
            } else {
              message.unqueue = true;
            }
            chrome.runtime.sendMessage(message);
            videosMap[video.url].forEach(function(g) {
              g.video.queued = !video.queued;
              if (g.video.$video) {
                g.video.$video.classList.toggle('queued');
              }
            });
          },
        })
      ]),
      m('div.right-side', [
        group.removable && !video.watched && m('a.close', {
          href: '#',
          'data-title': 'Mark as Watched',
          onclick: function(e) {
            chrome.runtime.sendMessage({
              watched: true,
              url: video.url,
              tabID: tabID,
            });
            videosMap[video.url].forEach(function(g) {
              if (g.$badge) {
                g.$badge.textContent = (--g.group.unwatched) || '';
              }
              g.video.watched = true;
              var $video = g.video.$video;
              if (!$video) { return; }

              if (options.show_watched) {
                $video.classList.add('watched');

              } else {
                if ($video.offsetParent === null) {
                  $video.parentNode.removeChild($video);
                } else {
                  $video.style.height = 0;
                  setTimeout(function() {
                    $video.parentNode.removeChild($video);
                    lazyload.processScroll();
                  }, 250);
                }
              }
            });
            e.preventDefault();
          },
        }, '✖'),
        m('a.title', { href: video.url, onclick: open }, video.title),
        m('div.user', [
          m('span.favicon', { className: 'source-' + video.source }),
          video.otherSource && m('a.favicon', {
            className: 'source-' + video.otherSource.source,
            href: video.otherSource.url,
            onclick: goToLink,
          }),
          video.user.thumbnail ?
            m('img.lazy', { 'data-src': video.user.thumbnail }) : null,
          m('a.name', {
            href: video.user.url,
            onclick: goToLink,
          }, video.user.name),
          video.user.verified &&
            m('span.verified', { 'data-title': 'Verified' })
        ]),
        m('div', [
          video.timestamp && now < video.timestamp &&
            m('span.starts', showTime(video.timestamp)),
          video.timestamp && now >= video.timestamp &&
            m('span.time', timeAgo(video.timestamp)),
          video.views && m('span.views', video.views),
        ]),
        video.desc ? m('div', { innerHTML: video.desc }) : null
      ])
    ]);

    video.$video = $video;
    return $video;
  }));

  $content.appendChild(group.$videos);
  lazyload.addImages(group.$videos);
}
<|endoftext|>