<commit_before>/*!
 * glide.js
 * Author: Ryan Mitchell
 * Git: https://github.com/rmitchellnet
 * License: MIT
 */

(function (root, factory) {
    var pluginName = 'Glide';

    if (typeof define === 'function' && define.amd) {
        define([], factory(pluginName));
    } else if (typeof exports === 'object') {
        module.exports = factory(pluginName);
    } else {
        root[pluginName] = factory(pluginName);
    }
} (this, function (pluginName) {
    'use strict';

    var defaults = {
        easing: 'M0,0 C0.11,0.494 0.128,0.756 0.254,0.882 0.386,1.014 0.504,1 1,1'
    };

    /**
     * Merge defaults with user options
     * @param {Object} defaults Default settings
     * @param {Object} options User options
     */
    var extend = function (target, options) {
        var prop, extended = {};
        for (prop in defaults) {
            if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
                extended[prop] = defaults[prop];
            }
        }
        for (prop in options) {
            if (Object.prototype.hasOwnProperty.call(options, prop)) {
                extended[prop] = options[prop];
            }
        }
        return extended;
    };

    /**
     * Some private helper function
     */
    var privateFunction = function () {
        // private helper code goes here
    };

    /**
     * Plugin Object
     * @param element The html element to initialize
     * @param {Object} options User options
     * @constructor
     */
    function Plugin(element, options) {
        this.element = document.querySelector(element);
        this.options = extend(defaults, options);

        this.element.style['position'] = 'fixed';

        //init code goes here
        console.log('Plugin initialized');
    }

    // Plugin prototype
    Plugin.prototype = {

        start: function () {
            //this.element.innerHTML = this.options.someDefaultOption;
            this.scrollLoop = requestAnimationFrame(this.start.bind(this));
            this.element.parentNode.style['height'] = this.element.clientHeight + 'px';
            var scrollY = -window.scrollY;
            TweenMax.to(this.element, 0.8, {
                y: scrollY,
                ease: this.options.easing
            });
        },
        cancel: function () {
            cancelAnimationFrame(this.scrollLoop);
        }

    };

    // add lightweight jQuery wrapper, if available
    if (window.jQuery) {
        var $ = window.jQuery;

        $.fn[pluginName] = function (options) {
            options = options || {};

            return this.each(function () {
                // add plugin to element data
                if (!$.data(this, 'plugin_' + pluginName)) {
                    options.element = this;
                    $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
                }
            });
        };
    }

    return Plugin;
}));<commit_msg>updating comment<commit_after>/*!
 * glide.js
 * Author: Ryan Mitchell
 * Git: https://github.com/rmitchellnet/glide.js
 * License: MIT
 */

(function (root, factory) {
    var pluginName = 'Glide';

    if (typeof define === 'function' && define.amd) {
        define([], factory(pluginName));
    } else if (typeof exports === 'object') {
        module.exports = factory(pluginName);
    } else {
        root[pluginName] = factory(pluginName);
    }
} (this, function (pluginName) {
    'use strict';

    var defaults = {
        easing: 'M0,0 C0.11,0.494 0.128,0.756 0.254,0.882 0.386,1.014 0.504,1 1,1'
    };

    /**
     * Merge defaults with user options
     * @param {Object} defaults Default settings
     * @param {Object} options User options
     */
    var extend = function (target, options) {
        var prop, extended = {};
        for (prop in defaults) {
            if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
                extended[prop] = defaults[prop];
            }
        }
        for (prop in options) {
            if (Object.prototype.hasOwnProperty.call(options, prop)) {
                extended[prop] = options[prop];
            }
        }
        return extended;
    };

    /**
     * Some private helper function
     */
    var privateFunction = function () {
        // private helper code goes here
    };

    /**
     * Plugin Object
     * @param element The html element to initialize
     * @param {Object} options User options
     * @constructor
     */
    function Plugin(element, options) {
        this.element = document.querySelector(element);
        this.options = extend(defaults, options);

        this.element.style['position'] = 'fixed';

        //init code goes here
        console.log('Plugin initialized');
    }

    // Plugin prototype
    Plugin.prototype = {

        start: function () {
            //this.element.innerHTML = this.options.someDefaultOption;
            this.scrollLoop = requestAnimationFrame(this.start.bind(this));
            this.element.parentNode.style['height'] = this.element.clientHeight + 'px';
            var scrollY = -window.scrollY;
            TweenMax.to(this.element, 0.8, {
                y: scrollY,
                ease: this.options.easing
            });
        },
        cancel: function () {
            cancelAnimationFrame(this.scrollLoop);
        }

    };

    // add lightweight jQuery wrapper, if available
    if (window.jQuery) {
        var $ = window.jQuery;

        $.fn[pluginName] = function (options) {
            options = options || {};

            return this.each(function () {
                // add plugin to element data
                if (!$.data(this, 'plugin_' + pluginName)) {
                    options.element = this;
                    $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
                }
            });
        };
    }

    return Plugin;
}));<|endoftext|>


<commit_before>var Component = new Brick.Component();
Component.requires = {
    yui: ['base', 'widget'],
    mod: [
        {name: 'sys', files: ['component.js']}
    ]
};
Component.entryPoint = function(NS){

    var Y = Brick.YUI,
        L = Y.Lang,
        BINDUI = 'bindUI',
        SYNCUI = 'syncUI',

        UI = Y.Widget.UI_SRC,

        BOUNDING_BOX = 'boundingBox';

    var WidgetClick = function(options){
        var CLICKS = options && options.CLICKS ? options.CLICKS : null;
        this._initClicks(CLICKS);
    };
    WidgetClick.prototype = {
        initializer: function(){
            Y.after(this._bindUIWidgetClick, this, BINDUI);
        },
        _bindUIWidgetClick: function(){
            var boundingBox = this.get(BOUNDING_BOX);
            boundingBox.on({
                click: Y.bind(this._onWidgetClick, this)
            });

            this.publish({
                click: this._defWidgetClick
            });
        },
        _defWidgetClick: function(){
        },
        _onWidgetClick: function(e){
            if (!e || !e.target){
                return;
            }

            e.dataClick = e.target.getData('click');

            if (!e.dataClick){
                var elPrev = e.target.ancestor('[data-click]');
                if (elPrev){
                    e.dataClick = elPrev.getData('click');
                    e.defineTarget = elPrev;
                }
            } else {
                e.defineTarget = e.target;
            }

            var state = this._clickState;
            if (e.dataClick && !!state.get(e.dataClick, 'added')){
                var click = state.data[e.dataClick],
                    event = click.event,
                    context = click.context;

                if (Y.Lang.isString(event)){
                    event = this[event];
                }
                if (L.isFunction(event)){
                    event.apply(context || this, arguments);
                    e.halt();
                    return;
                }
            }

            if (L.isFunction(this.onClick)
                && this.onClick.apply(this, arguments)){

                e.halt();
                return;
            }

            if (!this.fire('click', {dataClick: e.dataClick})){
                e.halt();
            }
        },
        _initClicks: function(options){
            this._clickState = new Y.State();

            var ctor = this.constructor,
                c = ctor,
                clicks = {};

            while (c){
                clicks = Y.merge(clicks, c.CLICKS);
                c = c.superclass ? c.superclass.constructor : null;
            }

            if (options){
                clicks = Y.merge(clicks, options);
            }

            if (!clicks){
                return;
            }
            var nClicks = {}, a, name, i;
            for (name in clicks){
                a = name.split(',');
                for (i = 0; i < a.length; i++){
                    a[i] = Y.Lang.trim(a[i]);
                    if (a[i].length === 0){
                        continue;
                    }
                    nClicks[a[i]] = Y.Lang.isString(clicks[name]) ? clicks[name] : Y.merge(clicks[name]);
                }
            }
            clicks = nClicks;
            for (name in clicks){
                if (Y.Lang.isString(clicks[name])){
                    clicks[name] = {
                        event: clicks[name]
                    };
                }
            }

            clicks = Y.AttributeCore.protectAttrs(clicks);

            var state = this._clickState,
                added, config;

            for (name in clicks){
                if (!clicks.hasOwnProperty(name)){
                    continue;
                }
                added = state.get(name, 'added');
                if (added){
                    continue;
                }
                config = clicks[name];
                config.added = true;
                if (!config.event){
                    config.event = name;
                }
                if (!config.context){
                    config.context = this;
                }
                state.data[name] = config;
            }
        }
    };
    NS.WidgetClick = WidgetClick;

    var WAITING = 'waiting';

    var Waiting = function(){
    };
    Waiting.ATTRS = {
        waiting: {
            value: false
        }
    };
    Waiting.WAITING_CLASS_NAME = Y.Widget.getClassName(WAITING);
    Waiting.prototype = {
        initializer: function(){
            Y.after(this._syncUIWaiting, this, SYNCUI);
            Y.after(this._bindUIWaiting, this, BINDUI);
        },
        _syncUIWaiting: function(){
            this._uiSetWaiting(this.get('waiting'));
        },
        _bindUIWaiting: function(){
            this.after('waitingChange', this._afterWaitingChange);
        },
        _afterWaitingChange: function(e){
            if (e.src != UI){
                this._uiSetWaiting(e.newVal);
            }
        },
        _uiSetWaiting: function(val){
            var boundingBox = this.get(BOUNDING_BOX);
            boundingBox.all('[data-wait]').each(function(node){
                var flag = node.getData('wait');
                switch (flag) {
                    case 'show':
                        node.setStyle('display', val ? '' : 'none');
                        break;
                    case 'hide':
                        node.setStyle('display', val ? 'none' : '');
                        break;
                    case 'disable':
                        node.set('disabled', val);
                        break;
                    case 'enable':
                        node.set('disabled', !val);
                        break;
                }
            }, this);
        }
    };
    NS.WidgetWaiting = Waiting;

    var EditorStatus = function(){
    };
    EditorStatus.ATTRS = {
        isEdit: {
            value: true
        }
    };
    EditorStatus.prototype = {
        initializer: function(){
            Y.after(this._syncUIEditorStatus, this, SYNCUI);
            Y.after(this._bindUIEditorStatus, this, BINDUI);
        },
        _syncUIEditorStatus: function(){
            this._uiSetEditorStatus(this.get('isEdit'));
        },
        _bindUIEditorStatus: function(){
            this.after('isEditChange', this._afterEditorStatusChange);
        },
        _afterEditorStatusChange: function(e){
            if (e.src != UI){
                this._uiSetEditorStatus(e.newVal);
            }
        },
        _uiSetEditorStatus: function(val){
            var boundingBox = this.get(BOUNDING_BOX);
            boundingBox.all('[data-isedit]').each(function(node){
                var flag = node.getData('isedit');
                switch (flag) {
                    case 'show':
                        node.setStyle('display', val ? '' : 'none');
                        break;
                    case 'hide':
                        node.setStyle('display', val ? 'none' : '');
                        break;
                    case 'disable':
                        node.set('disabled', val);
                        break;
                    case 'enable':
                        node.set('disabled', !val);
                        break;
                }
            }, this);
        }
    };
    NS.WidgetEditorStatus = EditorStatus;

    var ContainerWidgetExt = function(){
    };
    ContainerWidgetExt.ATTRS = {};
    ContainerWidgetExt.prototype = {
        initializer: function(){
            this._containerWidget = [];
        },
        destructor: function(){
            this.cleanWidgets();
        },
        eachWidget: function(fn, context){
            var list = this._containerWidget;
            for (var i = 0; i < list.length; i++){
                var wi = list[i];
                fn.call(context || this, wi.widget, wi.name, i);
            }
        },
        addWidget: function(name, widget){
            var curWidget = this.getWidget(name);
            if (curWidget){
                throw new Exception('Widget is exist');
            }
            var list = this._containerWidget;
            list[list.length] = {
                name: name,
                widget: widget
            };
            return widget;
        },
        getWidget: function(name){
            var find = null;
            this.eachWidget(function(w, n){
                if (name === n){
                    find = w;
                }
            });
            return find;
        },
        cleanWidgets: function(){
            this.eachWidget(function(widget){
                widget.destroy();
            }, this);
            this._containerWidget = [];
        }
    };
    NS.ContainerWidgetExt = ContainerWidgetExt;

    var TriggerWidgetExt = function(){
    };
    TriggerWidgetExt._split = function(s){
        s = s || [];

        if (Y.Lang.isString(s)){
            s = s.replace(/\s+/g, '').split(',');
        }
        return s;
    };
    TriggerWidgetExt._indexOf = function(arr, str){
        var find = false;
        for (var i = 0; i < arr.length; i++){
            if (arr[i] === str){
                find = true;
                break;
            }
        }
        return find;
    };
    TriggerWidgetExt._action = function(bbox, action, names, codes, exactMatch){
        names = TriggerWidgetExt._split(names);
        codes = TriggerWidgetExt._split(codes);

        var name, code, i, find, and = true;

        bbox.all('[data-trigger]').each(function(node){
            name = node.getData('trigger');
            if (!TriggerWidgetExt._indexOf(names, name)){
                return;
            }
            if (codes.length > 0 || exactMatch){
                code = (node.getData('code') || '').replace(/\s+/g, '');

                and = true;

                if (code.indexOf('&') > -1){
                    code = code.split('&');
                } else if (code.indexOf('|') > -1){
                    code = code.split('|');
                    and = false;
                } else {
                    code = [code];
                }

                if (and){
                    for (i = 0; i < code.length; i++){
                        if (!TriggerWidgetExt._indexOf(codes, code[i])){
                            return;
                        }
                    }
                } else {
                    find = false;
                    for (i = 0; i < code.length; i++){
                        if (TriggerWidgetExt._indexOf(codes, code[i])){
                            find = true;
                            break;
                        }
                    }
                    if (!find){
                        return;
                    }
                }
            }
            if (action === 'hide'){
                node.addClass('hide');
            } else if (action === 'show'){
                node.removeClass('hide');
            }
        }, this);
    };
    TriggerWidgetExt.prototype = {
        triggerHide: function(names, codes, toggleCodes){
            var bbox = this.get(BOUNDING_BOX);
            TriggerWidgetExt._action(bbox, 'hide', names, codes);
            TriggerWidgetExt._action(bbox, 'show', names, toggleCodes, true);
        },
        triggerShow: function(names, codes, toggleCodes){
            var bbox = this.get(BOUNDING_BOX);
            TriggerWidgetExt._action(bbox, 'show', names, codes);
            TriggerWidgetExt._action(bbox, 'hide', names, toggleCodes, true);
        },
    };
    NS.TriggerWidgetExt = TriggerWidgetExt;
};<commit_msg>release: set on click function in widget<commit_after>var Component = new Brick.Component();
Component.requires = {
    yui: ['base', 'widget'],
    mod: [
        {name: 'sys', files: ['component.js']}
    ]
};
Component.entryPoint = function(NS){

    var Y = Brick.YUI,
        L = Y.Lang,
        BINDUI = 'bindUI',
        SYNCUI = 'syncUI',

        UI = Y.Widget.UI_SRC,

        BOUNDING_BOX = 'boundingBox';

    var WidgetClick = function(options){
        var CLICKS = options && options.CLICKS ? options.CLICKS : null;
        this._initClicks(CLICKS);
    };
    WidgetClick.prototype = {
        initializer: function(){
            Y.after(this._bindUIWidgetClick, this, BINDUI);
        },
        _bindUIWidgetClick: function(){
            var boundingBox = this.get(BOUNDING_BOX);
            boundingBox.on({
                click: Y.bind(this._onWidgetClick, this)
            });

            this.publish({
                click: this._defWidgetClick
            });
        },
        _defWidgetClick: function(){
        },
        _onWidgetClick: function(e){
            if (!e || !e.target){
                return;
            }

            e.dataClick = e.target.getData('click');

            if (!e.dataClick){
                var elPrev = e.target.ancestor('[data-click]');
                if (elPrev){
                    e.dataClick = elPrev.getData('click');
                    e.defineTarget = elPrev;
                }
            } else {
                e.defineTarget = e.target;
            }

            var state = this._clickState,
                a;

            if (e.dataClick && (a = e.dataClick.split(':')).length === 2){
                if (a[0] === 'this' && Y.Lang.isFunction(this[a[1]])){
                    this[a[1]].apply(this, arguments);
                    e.halt();
                    return;
                }
            }

            if (e.dataClick && !!state.get(e.dataClick, 'added')){
                var click = state.data[e.dataClick],
                    event = click.event,
                    context = click.context;

                if (Y.Lang.isString(event)){
                    event = this[event];
                }
                if (L.isFunction(event)){
                    event.apply(context || this, arguments);
                    e.halt();
                    return;
                }
            }

            if (L.isFunction(this.onClick)
                && this.onClick.apply(this, arguments)){

                e.halt();
                return;
            }

            if (!this.fire('click', {dataClick: e.dataClick})){
                e.halt();
            }
        },
        _initClicks: function(options){
            this._clickState = new Y.State();

            var ctor = this.constructor,
                c = ctor,
                clicks = {};

            while (c){
                clicks = Y.merge(clicks, c.CLICKS);
                c = c.superclass ? c.superclass.constructor : null;
            }

            if (options){
                clicks = Y.merge(clicks, options);
            }

            if (!clicks){
                return;
            }
            var nClicks = {}, a, name, i;
            for (name in clicks){
                a = name.split(',');
                for (i = 0; i < a.length; i++){
                    a[i] = Y.Lang.trim(a[i]);
                    if (a[i].length === 0){
                        continue;
                    }
                    nClicks[a[i]] = Y.Lang.isString(clicks[name]) ? clicks[name] : Y.merge(clicks[name]);
                }
            }
            clicks = nClicks;
            for (name in clicks){
                if (Y.Lang.isString(clicks[name])){
                    clicks[name] = {
                        event: clicks[name]
                    };
                }
            }

            clicks = Y.AttributeCore.protectAttrs(clicks);

            var state = this._clickState,
                added, config;

            for (name in clicks){
                if (!clicks.hasOwnProperty(name)){
                    continue;
                }
                added = state.get(name, 'added');
                if (added){
                    continue;
                }
                config = clicks[name];
                config.added = true;
                if (!config.event){
                    config.event = name;
                }
                if (!config.context){
                    config.context = this;
                }
                state.data[name] = config;
            }
        }
    };
    NS.WidgetClick = WidgetClick;

    var WAITING = 'waiting';

    var Waiting = function(){
    };
    Waiting.ATTRS = {
        waiting: {
            value: false
        }
    };
    Waiting.WAITING_CLASS_NAME = Y.Widget.getClassName(WAITING);
    Waiting.prototype = {
        initializer: function(){
            Y.after(this._syncUIWaiting, this, SYNCUI);
            Y.after(this._bindUIWaiting, this, BINDUI);
        },
        _syncUIWaiting: function(){
            this._uiSetWaiting(this.get('waiting'));
        },
        _bindUIWaiting: function(){
            this.after('waitingChange', this._afterWaitingChange);
        },
        _afterWaitingChange: function(e){
            if (e.src != UI){
                this._uiSetWaiting(e.newVal);
            }
        },
        _uiSetWaiting: function(val){
            var boundingBox = this.get(BOUNDING_BOX);
            boundingBox.all('[data-wait]').each(function(node){
                var flag = node.getData('wait');
                switch (flag) {
                    case 'show':
                        node.setStyle('display', val ? '' : 'none');
                        break;
                    case 'hide':
                        node.setStyle('display', val ? 'none' : '');
                        break;
                    case 'disable':
                        node.set('disabled', val);
                        break;
                    case 'enable':
                        node.set('disabled', !val);
                        break;
                }
            }, this);
        }
    };
    NS.WidgetWaiting = Waiting;

    var EditorStatus = function(){
    };
    EditorStatus.ATTRS = {
        isEdit: {
            value: true
        }
    };
    EditorStatus.prototype = {
        initializer: function(){
            Y.after(this._syncUIEditorStatus, this, SYNCUI);
            Y.after(this._bindUIEditorStatus, this, BINDUI);
        },
        _syncUIEditorStatus: function(){
            this._uiSetEditorStatus(this.get('isEdit'));
        },
        _bindUIEditorStatus: function(){
            this.after('isEditChange', this._afterEditorStatusChange);
        },
        _afterEditorStatusChange: function(e){
            if (e.src != UI){
                this._uiSetEditorStatus(e.newVal);
            }
        },
        _uiSetEditorStatus: function(val){
            var boundingBox = this.get(BOUNDING_BOX);
            boundingBox.all('[data-isedit]').each(function(node){
                var flag = node.getData('isedit');
                switch (flag) {
                    case 'show':
                        node.setStyle('display', val ? '' : 'none');
                        break;
                    case 'hide':
                        node.setStyle('display', val ? 'none' : '');
                        break;
                    case 'disable':
                        node.set('disabled', val);
                        break;
                    case 'enable':
                        node.set('disabled', !val);
                        break;
                }
            }, this);
        }
    };
    NS.WidgetEditorStatus = EditorStatus;

    var ContainerWidgetExt = function(){
    };
    ContainerWidgetExt.ATTRS = {};
    ContainerWidgetExt.prototype = {
        initializer: function(){
            this._containerWidget = [];
        },
        destructor: function(){
            this.cleanWidgets();
        },
        eachWidget: function(fn, context){
            var list = this._containerWidget;
            for (var i = 0; i < list.length; i++){
                var wi = list[i];
                fn.call(context || this, wi.widget, wi.name, i);
            }
        },
        addWidget: function(name, widget){
            var curWidget = this.getWidget(name);
            if (curWidget){
                throw new Exception('Widget is exist');
            }
            var list = this._containerWidget;
            list[list.length] = {
                name: name,
                widget: widget
            };
            return widget;
        },
        getWidget: function(name){
            var find = null;
            this.eachWidget(function(w, n){
                if (name === n){
                    find = w;
                }
            });
            return find;
        },
        cleanWidgets: function(){
            this.eachWidget(function(widget){
                widget.destroy();
            }, this);
            this._containerWidget = [];
        }
    };
    NS.ContainerWidgetExt = ContainerWidgetExt;

    var TriggerWidgetExt = function(){
    };
    TriggerWidgetExt._split = function(s){
        s = s || [];

        if (Y.Lang.isString(s)){
            s = s.replace(/\s+/g, '').split(',');
        }
        return s;
    };
    TriggerWidgetExt._indexOf = function(arr, str){
        var find = false;
        for (var i = 0; i < arr.length; i++){
            if (arr[i] === str){
                find = true;
                break;
            }
        }
        return find;
    };
    TriggerWidgetExt._action = function(bbox, action, names, codes, exactMatch){
        names = TriggerWidgetExt._split(names);
        codes = TriggerWidgetExt._split(codes);

        var name, code, i, find, and = true;

        bbox.all('[data-trigger]').each(function(node){
            name = node.getData('trigger');
            if (!TriggerWidgetExt._indexOf(names, name)){
                return;
            }
            if (codes.length > 0 || exactMatch){
                code = (node.getData('code') || '').replace(/\s+/g, '');

                and = true;

                if (code.indexOf('&') > -1){
                    code = code.split('&');
                } else if (code.indexOf('|') > -1){
                    code = code.split('|');
                    and = false;
                } else {
                    code = [code];
                }

                if (and){
                    for (i = 0; i < code.length; i++){
                        if (!TriggerWidgetExt._indexOf(codes, code[i])){
                            return;
                        }
                    }
                } else {
                    find = false;
                    for (i = 0; i < code.length; i++){
                        if (TriggerWidgetExt._indexOf(codes, code[i])){
                            find = true;
                            break;
                        }
                    }
                    if (!find){
                        return;
                    }
                }
            }
            if (action === 'hide'){
                node.addClass('hide');
            } else if (action === 'show'){
                node.removeClass('hide');
            }
        }, this);
    };
    TriggerWidgetExt.prototype = {
        triggerHide: function(names, codes, toggleCodes){
            var bbox = this.get(BOUNDING_BOX);
            TriggerWidgetExt._action(bbox, 'hide', names, codes);
            TriggerWidgetExt._action(bbox, 'show', names, toggleCodes, true);
        },
        triggerShow: function(names, codes, toggleCodes){
            var bbox = this.get(BOUNDING_BOX);
            TriggerWidgetExt._action(bbox, 'show', names, codes);
            TriggerWidgetExt._action(bbox, 'hide', names, toggleCodes, true);
        },
    };
    NS.TriggerWidgetExt = TriggerWidgetExt;
};<|endoftext|>


<commit_before>'use strict';

const gulp = require('gulp');
const spawn = require('child_process').spawn;
const rm = require('rimraf').sync;
const path = require('path');

const OUT_DIR = 'serve';

const LESS_IN = 'less/main.less';
const LESS_WATCH = 'less/**/*.less';
const LESS_OUT = `${OUT_DIR}/style.min.css`;

const HTML_IN = 'html/index.html';
const HTML_WATCH = 'html/**/*.html';
const HTML_OUT = `${OUT_DIR}/index.html`;

const JS_IN = 'js/main.js';
const JS_WATCH = 'js/**/*.js';
const JS_OUT = `${OUT_DIR}/abra.min.js`;

function run(command) {
	command = command.split(' ');
	const proc = spawn(command[0], command.slice(1), {
		stdio: 'inherit',
		shell: true
	});
	proc.on('close', (code) => {
		if (code) process.exit(code);
	});
}

gulp.task('default', ['less', 'html', 'js']);

gulp.task('clean', () => {
	rm(LESS_OUT);
	rm(LESS_OUT + '.map');
	rm(JS_OUT);
	rm(JS_OUT + '.map');
	rm(HTML_OUT);
});

gulp.task('watch', () => {
	gulp.watch(LESS_WATCH, ['less']);
	gulp.watch(HTML_WATCH, ['html']);
	gulp.watch(JS_WATCH, ['js']);
});

gulp.task('less', function (done) {
	const opt = [
		'--source-map',
		'--source-map-less-inline',
		'--clean-css',
		'--autoprefix="last 2 versions"'
	].join(' ');

	run(`lessc ${opt} ${LESS_IN} ${LESS_OUT}`);
	done();
});

gulp.task('html', function (done) {
	const opt = [
		'--remove-comments',
		'--sort-attributes',
		'--sort-class-name',
		'--remove-redundant-attributes',
		'--collapse-whitespace',
		'--conservative-collapse'
	].join(' ');

	run(`html-minifier ${opt} ${HTML_IN} -o ${HTML_OUT}`);
	done();
});

gulp.task('js', function (done) {
	const opt = [
		`--source-map content=${JS_OUT}.map,url=${path.basename(JS_OUT)}.map,filename=${JS_OUT}.map`,
		'-m reserved="[$,require,exports]",safari10=true',
		'-c'
	].join(' ');

	run(`browserify --debug --entry ${JS_IN} | exorcist ${JS_OUT}.map > ${JS_OUT} && uglifyjs ${opt} --output ${JS_OUT} ${JS_OUT}`);
	done();
});
<commit_msg>[C] fix firefox not finding less sourcemaps<commit_after>'use strict';

const gulp = require('gulp');
const spawn = require('child_process').spawn;
const rm = require('rimraf').sync;
const path = require('path');

const OUT_DIR = 'serve';

const LESS_DIR = 'less';
const LESS_IN = `${LESS_DIR}/main.less`;
const LESS_WATCH = `${LESS_DIR}/**/*.less`;
const LESS_OUT = `${OUT_DIR}/style.min.css`;

const HTML_DIR = 'html';
const HTML_IN = `${HTML_DIR}/index.html`;
const HTML_WATCH = `${HTML_DIR}/**/*.html`;
const HTML_OUT = `${OUT_DIR}/index.html`;

const JS_DIR = 'js';
const JS_IN = `${JS_DIR}/main.js`;
const JS_WATCH = `${JS_DIR}/**/*.js`;
const JS_OUT = `${OUT_DIR}/abra.min.js`;

function run(command) {
	command = command.split(' ');
	const proc = spawn(command[0], command.slice(1), {
		stdio: 'inherit',
		shell: true
	});
	proc.on('close', (code) => {
		if (code) process.exit(code);
	});
}

gulp.task('default', ['less', 'html', 'js']);

gulp.task('clean', () => {
	rm(LESS_OUT);
	rm(LESS_OUT + '.map');
	rm(JS_OUT);
	rm(JS_OUT + '.map');
	rm(HTML_OUT);
});

gulp.task('watch', () => {
	gulp.watch(LESS_WATCH, ['less']);
	gulp.watch(HTML_WATCH, ['html']);
	gulp.watch(JS_WATCH, ['js']);
});

gulp.task('less', function (done) {
	const opt = [
		'--source-map',
		'--source-map-less-inline',
		`--source-map-rootpath=${LESS_DIR}`,
		'--clean-css',
		'--autoprefix="last 2 versions"'
	].join(' ');

	run(`lessc ${opt} ${LESS_IN} ${LESS_OUT}`);
	done();
});

gulp.task('html', function (done) {
	const opt = [
		'--remove-comments',
		'--sort-attributes',
		'--sort-class-name',
		'--remove-redundant-attributes',
		'--collapse-whitespace',
		'--conservative-collapse'
	].join(' ');

	run(`html-minifier ${opt} ${HTML_IN} -o ${HTML_OUT}`);
	done();
});

gulp.task('js', function (done) {
	const opt = [
		`--source-map content=${JS_OUT}.map,url=${path.basename(JS_OUT)}.map,filename=${JS_OUT}.map`,
		'-m reserved="[$,require,exports]",safari10=true',
		'-c'
	].join(' ');

	run(`browserify --debug --entry ${JS_IN} | exorcist ${JS_OUT}.map > ${JS_OUT} && uglifyjs ${opt} --output ${JS_OUT} ${JS_OUT}`);
	done();
});
<|endoftext|>


<commit_before>/* -*- Mode: java; tab-width: 2; c-tab-always-indent: t; indent-tabs-mode: t; c-basic-offset: 2 -*- */

/*
	Copyright (C) 2005 SKYRIX Software AG

	This file is part of OpenGroupware.org.

	OGo is free software; you can redistribute it and/or modify it under
	the terms of the GNU Lesser General Public License as published by the
	Free Software Foundation; either version 2, or (at your option) any
	later version.

	OGo is distributed in the hope that it will be useful, but WITHOUT ANY
	WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
	License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with OGo; see the file COPYING.  If not, write to the
	Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
	02111-1307, USA.
*/

/* Dependencies:
 * It's required that "currentIndex" is defined in a top level context.
 *
 * Exports:
 * defines hasRecipients() returning a bool for the
 * surrounding context to check.
 */

var lastIndex = currentIndex;

function sanitizedCn(cn) {
  var parts;
  parts = cn.split(', ');
  if(parts.length == 1)
    return cn;
  return parts[0];
}

function hasAddress(email) {
  var e = $(email);
  if(e)
    return true;
  return false;
}

function checkAddresses() {
  alert("addressCount: " + this.getAddressCount() + " currentIndex: " + currentIndex + " lastIndex: " + lastIndex);
}

function fancyAddRow(shouldEdit, text, type) {
  var addr = $('addr_' + lastIndex);
  if (addr && addr.value == '') {
    var input = $('subjectField');
    if (input && input.value != '') {
      input.focus();
      input.select();
      return;
    }
  }
  var addressList = $("addressList").tBodies[0];
  var lastChild = $("lastRow");

  currentIndex++;
  var proto = lastChild.previous("tr");
  var row = proto.cloneNode(true);
  row.writeAttribute("id", 'row_' + currentIndex);
  var rowNodes = row.childNodesWithTag("td");
  var select = $(rowNodes[0]).childNodesWithTag("select")[0];
  select.name = 'popup_' + currentIndex;
  select.value = (type? type : proto.down("select").value);
  var input = $(rowNodes[1]).childNodesWithTag("input")[0];
  input.name  = 'addr_' + currentIndex;
  input.id = 'addr_' + currentIndex;
  input.value = text;
	input.stopObserving("keydown", onContactKeydown);

  addressList.insertBefore(row, lastChild);

  if (shouldEdit) {
    input.writeAttribute("autocomplete", "off");
    input.observe("keydown", onContactKeydown); // bind listener for address completion
    input.focus();
    input.select();
  }
}

function addressFieldGotFocus(sender) {
  var idx;

  idx = this.getIndexFromIdentifier(sender.id);
  if (lastIndex == idx) return;
  this.removeLastEditedRowIfEmpty();
  onWindowResize(null);

  return false;
}

function addressFieldLostFocus(sender) {
  lastIndex = this.getIndexFromIdentifier(sender.id);

	if (sender.confirmedValue) {
		sender.value = sender.confirmedValue;
		sender.confirmedValue = false;
	}

  var addresses = sender.value.split(',');
  if (addresses.length > 0) {
    sender.value = addresses[0].strip();
    for (var i = 1; i < addresses.length; i++) {
      var addr = addresses[i].strip();
      if (addr.length > 0)
				fancyAddRow(false, addr, $(sender).up("tr").down("select").value);
    }
  }
  onWindowResize(null);

  return false;
}

function removeLastEditedRowIfEmpty() {
  var addr, addressList, senderRow;

  addressList = $("addressList").tBodies[0];

  if (lastIndex == 0 && addressList.childNodes.length <= 2) return;
  addr = $('addr_' + lastIndex);
  if (!addr) return;
  if (addr.value.strip() != '') return;
  senderRow = $("row_" + lastIndex);
	addressList.removeChild(senderRow);
}

function getIndexFromIdentifier(id) {
  return id.split('_')[1];
}

function getAddressIDs() {
  var addressList, rows, i, count, addressIDs;

  addressIDs = new Array();

  addressList = $("addressList").tBodies[0];
  rows  = addressList.childNodes;
  count = rows.length;

  for (i = 0; i < count; i++) {
    var row, rowId;

    row = rows[i];
    rowId = row.id;
    if (rowId && rowId != 'lastRow') {
      var idx;

      idx = this.getIndexFromIdentifier(rowId);
      addressIDs.push(idx);
    }
  }
  return addressIDs;
}

function getAddressCount() {
  var addressCount, addressIDs, i, count;

  addressCount = 0;
  addressIDs   = this.getAddressIDs();
  count        = addressIDs.length;
  for (i = 0; i < count; i++) {
    var idx, input;

    idx   = addressIDs[i];
    input = $('addr_' + idx);
    if (input && input.value != '')
      addressCount++;
  }
  return addressCount;
}

function hasRecipients() {
  var count;

  count = this.getAddressCount();

  return (count > 0);
}

function initMailToSelection() {
  currentIndex = lastIndex = $$("table#addressList tr").length - 2;
}

FastInit.addOnLoad(initMailToSelection);
<commit_msg><commit_after>/* -*- Mode: java; tab-width: 2; c-tab-always-indent: t; indent-tabs-mode: t; c-basic-offset: 2 -*- */

/*
	Copyright (C) 2005 SKYRIX Software AG

	This file is part of OpenGroupware.org.

	OGo is free software; you can redistribute it and/or modify it under
	the terms of the GNU Lesser General Public License as published by the
	Free Software Foundation; either version 2, or (at your option) any
	later version.

	OGo is distributed in the hope that it will be useful, but WITHOUT ANY
	WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
	License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with OGo; see the file COPYING.  If not, write to the
	Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
	02111-1307, USA.
*/

/* Dependencies:
 * It's required that "currentIndex" is defined in a top level context.
 *
 * Exports:
 * defines hasRecipients() returning a bool for the
 * surrounding context to check.
 */

var lastIndex = currentIndex;

function sanitizedCn(cn) {
  var parts;
  parts = cn.split(', ');
  if(parts.length == 1)
    return cn;
  return parts[0];
}

function hasAddress(email) {
  var e = $(email);
  if(e)
    return true;
  return false;
}

function checkAddresses() {
  alert("addressCount: " + this.getAddressCount() + " currentIndex: " + currentIndex + " lastIndex: " + lastIndex);
}

function fancyAddRow(shouldEdit, text, type) {
  var addr = $('addr_' + lastIndex);
  if (addr && addr.value == '') {
    var input = $('subjectField');
    if (input && input.value != '') {
      input.focus();
      input.select();
      return;
    }
  }
  var addressList = $("addressList").tBodies[0];
  var lastChild = $("lastRow");

  currentIndex++;
  var proto = lastChild.previous("tr");
  var row = proto.cloneNode(true);
  row.writeAttribute("id", 'row_' + currentIndex);
  var rowNodes = row.childNodesWithTag("td");
  var select = $(rowNodes[0]).childNodesWithTag("select")[0];
  select.name = 'popup_' + currentIndex;
  select.value = (type? type : proto.down("select").value);
  var input = $(rowNodes[1]).childNodesWithTag("input")[0];
  input.name  = 'addr_' + currentIndex;
  input.id = 'addr_' + currentIndex;
  input.value = text;
	input.stopObserving("keydown", onContactKeydown);

  addressList.insertBefore(row, lastChild);

  if (shouldEdit) {
    input.writeAttribute("autocomplete", "off");
    input.observe("keydown", onContactKeydown); // bind listener for address completion
		input.observe("blur", onContactBlur);
    input.focus();
    input.select();
  }
}

function addressFieldGotFocus(sender) {
  var idx;

  idx = this.getIndexFromIdentifier(sender.id);
  if (lastIndex == idx) return;
  this.removeLastEditedRowIfEmpty();
  onWindowResize(null);

  return false;
}

function addressFieldLostFocus(sender) {
  lastIndex = this.getIndexFromIdentifier(sender.id);

	if (sender.confirmedValue) {
		sender.value = sender.confirmedValue;
		sender.confirmedValue = false;
	}

  var addresses = sender.value.split(',');
  if (addresses.length > 0) {
    sender.value = addresses[0].strip();
    for (var i = 1; i < addresses.length; i++) {
      var addr = addresses[i].strip();
      if (addr.length > 0)
				fancyAddRow(false, addr, $(sender).up("tr").down("select").value);
    }
  }
  onWindowResize(null);

  return false;
}

function removeLastEditedRowIfEmpty() {
  var addr, addressList, senderRow;

  addressList = $("addressList").tBodies[0];

  if (lastIndex == 0 && addressList.childNodes.length <= 2) return;
  addr = $('addr_' + lastIndex);
  if (!addr) return;
  if (addr.value.strip() != '') return;
  senderRow = $("row_" + lastIndex);
	addressList.removeChild(senderRow);
}

function getIndexFromIdentifier(id) {
  return id.split('_')[1];
}

function getAddressIDs() {
  var addressList, rows, i, count, addressIDs;

  addressIDs = new Array();

  addressList = $("addressList").tBodies[0];
  rows  = addressList.childNodes;
  count = rows.length;

  for (i = 0; i < count; i++) {
    var row, rowId;

    row = rows[i];
    rowId = row.id;
    if (rowId && rowId != 'lastRow') {
      var idx;

      idx = this.getIndexFromIdentifier(rowId);
      addressIDs.push(idx);
    }
  }
  return addressIDs;
}

function getAddressCount() {
  var addressCount, addressIDs, i, count;

  addressCount = 0;
  addressIDs   = this.getAddressIDs();
  count        = addressIDs.length;
  for (i = 0; i < count; i++) {
    var idx, input;

    idx   = addressIDs[i];
    input = $('addr_' + idx);
    if (input && input.value != '')
      addressCount++;
  }
  return addressCount;
}

function hasRecipients() {
  var count;

  count = this.getAddressCount();

  return (count > 0);
}

function initMailToSelection() {
  currentIndex = lastIndex = $$("table#addressList tr").length - 2;
}

FastInit.addOnLoad(initMailToSelection);
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2012-2014 Jochen S. Klar <jklar@aip.de>,
 *                           Adrian M. Partl <apartl@aip.de>,
 *                           AIP E-Science (www.aip.de)
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var app = angular.module('admin',['table','modal','ngSanitize']);

app.config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.headers.common['Accept'] = 'application/json';
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
}]);

app.factory('AdminService', ['$http','$window','TableService','ModalService',function($http,$window,TableService,ModalService) {

    var values = {};
    var errors = {};
    var activeUrl = null;

    // check if we have a daiquiri table or not
    var table = Boolean(angular.element('[daiquiri-table]').length);

    // enable links in daiquiri table
    if (table) {
        TableService.callback.rows = function(scope) {
            angular.element('.daiquiri-admin-option').on('click', scope.fetchHtml);
        }
    }

    return {
        values: values,
        errors: errors,
        fetchHtml: function (url) {
            $http.get(url,{'headers': {'Accept': 'application/html'}}).success(function(html) {
                for (var value in values) delete values[value];
                for (var error in errors) delete errors[error];

                if (ModalService.modal.html != html) {
                    ModalService.modal.html = html;
                }

                activeUrl = url;
                ModalService.modal.enabled = true;
            });
        },
        submitForm: function(submit) {
            if (submit) {
                var data = {
                    'csrf': angular.element('#csrf').attr('value')
                };

                // merge with form values and take arrays into account
                angular.forEach(values, function(value, key) {
                    if (angular.isObject(value)) {
                        data[key] = [];

                        if (angular.isArray(value)) {
                            // this is an array coming from a multiselect
                            angular.forEach(value, function(v,k) {
                                data[key].push(v);
                            });
                        } else {
                            // this is an object coming from a set of checkboxes
                            angular.forEach(value, function(v,k) {
                                if (v === true) {
                                    // for value from a set of checkboxes use the key
                                    data[key].push(k);
                                }
                            });
                        }
                    } else {
                        data[key] = value;
                    }
                });

                // fire up post request
                $http.post(activeUrl,$.param(data)).success(function(response) {
                    for (var error in errors) delete errors[error];

                    if (response.status === 'ok') {
                        ModalService.modal.enabled = false;

                        if (table) {
                            TableService.fetchRows();
                        } else {
                            $window.location.reload();
                        }
                    } else if (response.status === 'error') {
                        angular.forEach(response.errors, function(error, key) {
                            errors[key] = error;
                        });
                    } else {
                        errors['form'] = {'form': 'Error: Unknown response from server.'};
                    }
                });
            } else {
                ModalService.modal.enabled = false;
            }
        }
    };
}]);

app.controller('AdminController', ['$scope','AdminService',function($scope,AdminService) {

    $scope.values = AdminService.values;
    $scope.errors = AdminService.errors;

    $scope.fetchHtml = function(event) {
        AdminService.fetchHtml(event.target.href);
        event.preventDefault();
    };

    $scope.submitForm = function() {
        AdminService.submitForm($scope.submit);
    };
}]);<commit_msg>modal for forbidden ajax request<commit_after>/*
 *  Copyright (c) 2012-2014 Jochen S. Klar <jklar@aip.de>,
 *                           Adrian M. Partl <apartl@aip.de>,
 *                           AIP E-Science (www.aip.de)
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var app = angular.module('admin',['table','modal','ngSanitize']);

app.config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.headers.common['Accept'] = 'application/json';
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
}]);

app.factory('AdminService', ['$http','$window','TableService','ModalService',function($http,$window,TableService,ModalService) {

    var values = {};
    var errors = {};
    var activeUrl = null;

    // check if we have a daiquiri table or not
    var table = Boolean(angular.element('[daiquiri-table]').length);

    // enable links in daiquiri table
    if (table) {
        TableService.callback.rows = function(scope) {
            angular.element('.daiquiri-admin-option').on('click', scope.fetchHtml);
        }
    }

    return {
        values: values,
        errors: errors,
        fetchHtml: function (url) {
            $http.get(url,{'headers': {'Accept': 'application/html'}})
                .success(function(html) {
                    for (var value in values) delete values[value];
                    for (var error in errors) delete errors[error];

                    if (ModalService.modal.html != html) {
                        ModalService.modal.html = html;
                    }

                    activeUrl = url;
                    ModalService.modal.enabled = true;
                })
                .error(function() {
                    ModalService.modal.html = '

Error

Please reload the page.

';
                    ModalService.modal.enabled = true;
                });
        },
        submitForm: function(submit) {
            if (submit) {
                var data = {
                    'csrf': angular.element('#csrf').attr('value')
                };

                // merge with form values and take arrays into account
                angular.forEach(values, function(value, key) {
                    if (angular.isObject(value)) {
                        data[key] = [];

                        if (angular.isArray(value)) {
                            // this is an array coming from a multiselect
                            angular.forEach(value, function(v,k) {
                                data[key].push(v);
                            });
                        } else {
                            // this is an object coming from a set of checkboxes
                            angular.forEach(value, function(v,k) {
                                if (v === true) {
                                    // for value from a set of checkboxes use the key
                                    data[key].push(k);
                                }
                            });
                        }
                    } else {
                        data[key] = value;
                    }
                });

                // fire up post request
                $http.post(activeUrl,$.param(data)).success(function(response) {
                    for (var error in errors) delete errors[error];

                    if (response.status === 'ok') {
                        ModalService.modal.enabled = false;

                        if (table) {
                            TableService.fetchRows();
                        } else {
                            $window.location.reload();
                        }
                    } else if (response.status === 'error') {
                        angular.forEach(response.errors, function(error, key) {
                            errors[key] = error;
                        });
                    } else {
                        errors['form'] = {'form': 'Error: Unknown response from server.'};
                    }
                });
            } else {
                ModalService.modal.enabled = false;
            }
        }
    };
}]);

app.controller('AdminController', ['$scope','AdminService',function($scope,AdminService) {

    $scope.values = AdminService.values;
    $scope.errors = AdminService.errors;

    $scope.fetchHtml = function(event) {
        AdminService.fetchHtml(event.target.href);
        event.preventDefault();
    };

    $scope.submitForm = function() {
        AdminService.submitForm($scope.submit);
    };
}]);<|endoftext|>


<commit_before>(function() {

  /**
   * New `reddit`.
   */

  var reddit = new Snoocore({
    userAgent: 'snoobear',
    oauth: {
      type: 'implicit',
      key: 'ZMCcBVy-jqgrrA',
      redirectUri: 'http://www.snoobear.co/',
      scope: [ 'submit' ]
    }
  });

  /**
   * Get `authUrl`.
   */

  var authUrl = reddit.getImplicitAuthUrl();

  /**
   * Attach auth click listener.
   */

  document.getElementById('auth').addEventListener('click', function(e) {
    window.location.href = authUrl;
  });

  /**
   * If auth hash is in URL, then authenticate user.
   */

  var match = ('' + window.location.hash).match(/access_token=(.*?)&/);
  var accessToken = match ? match[1] : '';
  if (accessToken) {
    reddit.auth(accessToken)
      .then()
      .done(function() {
        document.getElementsByClassName('Oauth')[0].classList.toggle('hide');
        document.getElementsByClassName('Form')[0].classList.toggle('hide');
      });
  };

  /**
   * Grab form fields.
   */

  function fields() {
    var title = document.querySelector('.title').value;
    var subreddits = _.uniq(document.querySelector('.subreddits').value.replace(/\s+/g, '').split(','));
    var url = document.querySelector('.url').value;
    var kind = 'link';

    return {
      sr: subreddits,
      title: title,
      url: url,
      kind: kind
    };
  }

  /**
   * On submit.
   */

  document.getElementById('submit').addEventListener('click', function(e) {
    start(fields());
  });

  /**
   * Submit to reddit.
   */

  function redditSubmit(obj) {
    reddit('api/submit').post(obj);
  }

  /**
   * Background workers.
   */

  function start(fields) {
    if (window.Worker) {
      var timer = new Worker('/js/worker.js');
      timer.postMessage('start');
      timer.addEventListener('message', function(e) {
        if (fields.sr.length === 0) timer.terminate();
        redditSubmit({
          sr: fields.sr.pop(),
          title: fields.title,
          url: fields.url,
          kind: fields.kind
        });
      });
    } else {
      console.log('Web workers disabled for this browser.');
      redditSubmit({
        sr: fields.sr.pop(),
        title: fields.title,
        url: fields.url,
        kind: fields.kind
      });
      setInterval(function() {
        redditSubmit({
          sr: fields.sr.pop(),
          title: fields.title,
          url: fields.url,
          kind: fields.kind
        });
      }, 3960000)
    }
  }

})();<commit_msg>updated client/js/index.js<commit_after>(function() {

  /**
   * New `reddit`.
   */

  var reddit = new Snoocore({
    userAgent: 'snoobear',
    oauth: {
      type: 'implicit',
      key: 'ZMCcBVy-jqgrrA',
      redirectUri: 'http://www.snoobear.co/',
      // redirectUri: 'http://localhost:3000',
      scope: [ 'submit' ]
    }
  });

  /**
   * Get `authUrl`.
   */

  var authUrl = reddit.getImplicitAuthUrl();

  /**
   * Attach auth click listener.
   */

  document.getElementById('auth').addEventListener('click', function(e) {
    window.location.href = authUrl;
  });

  /**
   * If auth hash is in URL, then authenticate user.
   */

  var match = ('' + window.location.hash).match(/access_token=(.*?)&/);
  var accessToken = match ? match[1] : '';
  if (accessToken) {
    reddit.auth(accessToken)
      .then()
      .done(function() {
        document.getElementsByClassName('Oauth')[0].classList.toggle('hide');
        document.getElementsByClassName('Form')[0].classList.toggle('hide');
      });
  };

  /**
   * Grab form fields.
   */

  function fields() {
    var title = document.querySelector('.title').value;
    var subreddits = _.uniq(document.querySelector('.subreddits').value.replace(/\s+/g, '').split(','));
    var url = document.querySelector('.url').value;
    var kind = 'link';

    return {
      sr: subreddits,
      title: title,
      url: url,
      kind: kind
    };
  }

  /**
   * On submit.
   */

  document.getElementById('submit').addEventListener('click', function(e) {
    start(fields());
    document.getElementsByClassName('Form')[0].classList.toggle('hide');
    document.getElementsByClassName('Queued')[0].classList.toggle('hide');
  });

  /**
   * Submit to reddit.
   */

  function redditSubmit(obj) {
    // add <li> to class ul.Subreddits
    //
    reddit('api/submit')
      .post(obj)
      .then(function() {
        // Success!
        var li = document.querySelector('li[data-subreddit="' + obj.sr + '"]');
        li.innerText = '';
        var sr = document.createElement('span');
        sr.innerText = obj.sr;
        var icon = document.createElement('span');
        icon.className = 'glyphicon glyphicon-ok-sign pull-right';
        li.appendChild(sr);
        li.appendChild(icon);

        var message = document.createElement('div');
        message.className = 'text-success';
        message.innerText = 'Success!';
        li.appendChild(message);

      }, function(error) {
        var li = document.querySelector('li[data-subreddit="' + obj.sr + '"]');
        li.innerText = '';
        var sr = document.createElement('span');
        sr.innerText = obj.sr;
        var icon = document.createElement('span');
        icon.className = 'glyphicon glyphicon-remove-sign pull-right';
        li.appendChild(sr);
        li.appendChild(icon);

        var err = JSON.parse(error.body);
        var message = document.createElement('div');
        message.className = 'text-danger';
        message.innerText = err.json.errors.join(' ')
        li.appendChild(message);
      });
  }

  /**
   * Add subreddits in Queued.
   */

  function addSubreddits(arr) {
    var ul = document.getElementsByClassName('Queued-subreddits')[0];
    for (var i = 0; i < arr.length; i++) {
      var li = document.createElement('li');
      li.dataset.subreddit = arr[i];
      var sr = document.createElement('span');
      sr.innerText = arr[i];
      var icon = document.createElement('span');
      icon.className = 'glyphicon glyphicon-option-horizontal pull-right';
      li.appendChild(sr);
      li.appendChild(icon);
      ul.appendChild(li);
    }
  }

  /**
   * Background workers.
   */

  function start(fields) {
    addSubreddits(fields.sr);
    if (window.Worker) {
      var timer = new Worker('/js/worker.js');
      timer.postMessage('start');
      timer.addEventListener('message', function(e) {
        if (fields.sr.length === 0) timer.terminate();
        redditSubmit({
          sr: fields.sr.pop(),
          title: fields.title,
          url: fields.url,
          kind: fields.kind
        });
      });
    } else {
      console.log('Web workers disabled for this browser.');
      redditSubmit({
        sr: fields.sr.pop(),
        title: fields.title,
        url: fields.url,
        kind: fields.kind
      });
      setInterval(function() {
        redditSubmit({
          sr: fields.sr.pop(),
          title: fields.title,
          url: fields.url,
          kind: fields.kind
        });
      }, 3960000)
    }
  }

})();<|endoftext|>


<commit_before>"use strict";

const $ = require("jquery");
const escape = require("css.escape");
const viewport = $("#viewport");

var serverHash = -1; // eslint-disable-line no-var
var lastMessageId = -1; // eslint-disable-line no-var

module.exports = {
	// Same value as media query in CSS that forces sidebars to become overlays
	mobileViewportPixels: 768,
	inputCommands: {collapse, expand, join},
	findCurrentNetworkChan,
	serverHash,
	lastMessageId,
	confirmExit,
	scrollIntoViewNicely,
	hasRoleInChannel,
	move,
	closeChan,
	resetHeight,
	toggleNotificationMarkers,
	updateTitle,
	togglePasswordField,
	requestIdleCallback,
	togglePreviewMoreButtonsIfNeeded,
};

function findCurrentNetworkChan(name) {
	name = name.toLowerCase();

	return $(".network .chan.active")
		.parent(".network")
		.find(".chan")
		.filter(function() {
			return $(this).attr("aria-label").toLowerCase() === name;
		})
		.first();
}

function resetHeight(element) {
	element.style.height = element.style.minHeight;
}

// Given a channel element will determine if the lounge user or a given nick is one of the supplied roles.
function hasRoleInChannel(channel, roles, nick) {
	if (!channel || !roles) {
		return false;
	}

	const channelID = channel.data("id");
	const network = $("#sidebar .network").has(`.chan[data-id="${channelID}"]`);
	const target = nick || network.attr("data-nick");
	const user = channel.find(`.names-original .user[data-name="${escape(target)}"]`).first();
	return user.parent().is("." + roles.join(", ."));
}

// Reusable scrollIntoView parameters for channel list / user list
function scrollIntoViewNicely(el) {
	// Ideally this would use behavior: "smooth", but that does not consistently work in e.g. Chrome
	// https://github.com/iamdustan/smoothscroll/issues/28#issuecomment-364061459
	el.scrollIntoView({block: "center", inline: "nearest"});
}

function collapse() {
	$(".chan.active .toggle-button.toggle-preview.opened").click();
	return true;
}

function expand() {
	$(".chan.active .toggle-button.toggle-preview:not(.opened)").click();
	return true;
}

function join(args) {
	const channel = args[0];

	if (channel) {
		const chan = findCurrentNetworkChan(channel);

		if (chan.length) {
			chan.trigger("click");
		}
	}
}

const favicon = $("#favicon");

function toggleNotificationMarkers(newState) {
	// Toggles the favicon to red when there are unread notifications
	if (favicon.data("toggled") !== newState) {
		const old = favicon.prop("href");
		favicon.prop("href", favicon.data("other"));
		favicon.data("other", old);
		favicon.data("toggled", newState);
	}

	// Toggles a dot on the menu icon when there are unread notifications
	viewport.toggleClass("notified", newState);
}

function updateTitle() {
	let title = $(document.body).data("app-name");
	const chanTitle = $("#sidebar").find(".chan.active").attr("aria-label");

	if (chanTitle && chanTitle.length > 0) {
		title = `${chanTitle} — ${title}`;
	}

	// add highlight count to title
	let alertEventCount = 0;
	$(".badge.highlight").each(function() {
		alertEventCount += parseInt($(this).attr("data-highlight"));
	});

	if (alertEventCount > 0) {
		title = `(${alertEventCount}) ${title}`;
	}

	document.title = title;
}

function togglePasswordField(elem) {
	$(elem).on("click", function() {
		const $this = $(this);
		const input = $this.closest("div").find("input");

		input.attr("type", input.attr("type") === "password" ? "text" : "password");

		swapLabel($this);
		swapLabel($this.find("span"));
		$this.toggleClass("visible");
	});
}

// Given a element, swap its aria-label with the content of `data-alt-label`
function swapLabel(element) {
	const altText = element.data("alt-label");
	element.data("alt-label", element.attr("aria-label")).attr("aria-label", altText);
}

function confirmExit() {
	if ($(document.body).hasClass("public")) {
		window.onbeforeunload = function() {
			return "Are you sure you want to navigate away from this page?";
		};
	}
}

function move(array, old_index, new_index) {
	if (new_index >= array.length) {
		let k = new_index - array.length;

		while ((k--) + 1) {
			this.push(undefined);
		}
	}

	array.splice(new_index, 0, array.splice(old_index, 1)[0]);
	return array;
}

function closeChan(chan) {
	const socket = require("./socket");
	let cmd = "/close";

	if (chan.hasClass("lobby")) {
		cmd = "/quit";
		const server = chan.find(".name").html();

		if (!confirm("Disconnect from " + server + "?")) { // eslint-disable-line no-alert
			return false;
		}
	}

	socket.emit("input", {
		target: chan.data("id"),
		text: cmd,
	});
	chan.css({
		transition: "none",
		opacity: 0.4,
	});
	return false;
}

function requestIdleCallback(callback, timeout) {
	if (window.requestIdleCallback) {
		// During an idle period the user agent will run idle callbacks in FIFO order
		// until either the idle period ends or there are no more idle callbacks eligible to be run.
		window.requestIdleCallback(callback, {timeout});
	} else {
		callback();
	}
}

// Force handling preview display
function togglePreviewMoreButtonsIfNeeded() {
	$("#chat .chan.active .toggle-content.toggle-type-link.show")
		.trigger("showMoreIfNeeded");
}
<commit_msg>Fix confirmation box text for network removal in client<commit_after>"use strict";

const $ = require("jquery");
const escape = require("css.escape");
const viewport = $("#viewport");

var serverHash = -1; // eslint-disable-line no-var
var lastMessageId = -1; // eslint-disable-line no-var

module.exports = {
	// Same value as media query in CSS that forces sidebars to become overlays
	mobileViewportPixels: 768,
	inputCommands: {collapse, expand, join},
	findCurrentNetworkChan,
	serverHash,
	lastMessageId,
	confirmExit,
	scrollIntoViewNicely,
	hasRoleInChannel,
	move,
	closeChan,
	resetHeight,
	toggleNotificationMarkers,
	updateTitle,
	togglePasswordField,
	requestIdleCallback,
	togglePreviewMoreButtonsIfNeeded,
};

function findCurrentNetworkChan(name) {
	name = name.toLowerCase();

	return $(".network .chan.active")
		.parent(".network")
		.find(".chan")
		.filter(function() {
			return $(this).attr("aria-label").toLowerCase() === name;
		})
		.first();
}

function resetHeight(element) {
	element.style.height = element.style.minHeight;
}

// Given a channel element will determine if the lounge user or a given nick is one of the supplied roles.
function hasRoleInChannel(channel, roles, nick) {
	if (!channel || !roles) {
		return false;
	}

	const channelID = channel.data("id");
	const network = $("#sidebar .network").has(`.chan[data-id="${channelID}"]`);
	const target = nick || network.attr("data-nick");
	const user = channel.find(`.names-original .user[data-name="${escape(target)}"]`).first();
	return user.parent().is("." + roles.join(", ."));
}

// Reusable scrollIntoView parameters for channel list / user list
function scrollIntoViewNicely(el) {
	// Ideally this would use behavior: "smooth", but that does not consistently work in e.g. Chrome
	// https://github.com/iamdustan/smoothscroll/issues/28#issuecomment-364061459
	el.scrollIntoView({block: "center", inline: "nearest"});
}

function collapse() {
	$(".chan.active .toggle-button.toggle-preview.opened").click();
	return true;
}

function expand() {
	$(".chan.active .toggle-button.toggle-preview:not(.opened)").click();
	return true;
}

function join(args) {
	const channel = args[0];

	if (channel) {
		const chan = findCurrentNetworkChan(channel);

		if (chan.length) {
			chan.trigger("click");
		}
	}
}

const favicon = $("#favicon");

function toggleNotificationMarkers(newState) {
	// Toggles the favicon to red when there are unread notifications
	if (favicon.data("toggled") !== newState) {
		const old = favicon.prop("href");
		favicon.prop("href", favicon.data("other"));
		favicon.data("other", old);
		favicon.data("toggled", newState);
	}

	// Toggles a dot on the menu icon when there are unread notifications
	viewport.toggleClass("notified", newState);
}

function updateTitle() {
	let title = $(document.body).data("app-name");
	const chanTitle = $("#sidebar").find(".chan.active").attr("aria-label");

	if (chanTitle && chanTitle.length > 0) {
		title = `${chanTitle} — ${title}`;
	}

	// add highlight count to title
	let alertEventCount = 0;
	$(".badge.highlight").each(function() {
		alertEventCount += parseInt($(this).attr("data-highlight"));
	});

	if (alertEventCount > 0) {
		title = `(${alertEventCount}) ${title}`;
	}

	document.title = title;
}

function togglePasswordField(elem) {
	$(elem).on("click", function() {
		const $this = $(this);
		const input = $this.closest("div").find("input");

		input.attr("type", input.attr("type") === "password" ? "text" : "password");

		swapLabel($this);
		swapLabel($this.find("span"));
		$this.toggleClass("visible");
	});
}

// Given a element, swap its aria-label with the content of `data-alt-label`
function swapLabel(element) {
	const altText = element.data("alt-label");
	element.data("alt-label", element.attr("aria-label")).attr("aria-label", altText);
}

function confirmExit() {
	if ($(document.body).hasClass("public")) {
		window.onbeforeunload = function() {
			return "Are you sure you want to navigate away from this page?";
		};
	}
}

function move(array, old_index, new_index) {
	if (new_index >= array.length) {
		let k = new_index - array.length;

		while ((k--) + 1) {
			this.push(undefined);
		}
	}

	array.splice(new_index, 0, array.splice(old_index, 1)[0]);
	return array;
}

function closeChan(chan) {
	const socket = require("./socket");
	let cmd = "/close";

	if (chan.hasClass("lobby")) {
		cmd = "/quit";
		const server = chan.find(".name").html();

		if (!confirm(`Are you sure you want to remove ${server}?`)) { // eslint-disable-line no-alert
			return false;
		}
	}

	socket.emit("input", {
		target: chan.data("id"),
		text: cmd,
	});
	chan.css({
		transition: "none",
		opacity: 0.4,
	});
	return false;
}

function requestIdleCallback(callback, timeout) {
	if (window.requestIdleCallback) {
		// During an idle period the user agent will run idle callbacks in FIFO order
		// until either the idle period ends or there are no more idle callbacks eligible to be run.
		window.requestIdleCallback(callback, {timeout});
	} else {
		callback();
	}
}

// Force handling preview display
function togglePreviewMoreButtonsIfNeeded() {
	$("#chat .chan.active .toggle-content.toggle-type-link.show")
		.trigger("showMoreIfNeeded");
}
<|endoftext|>


<commit_before>var queue = [];
var queueTruncated = false;
var searchResults = {};
var progress = {progress: 0, interval: null};
var paused = true;

var currentSong = {};
var currentProgress;
var streaming = false;

var search = function() {
    var searchTerms = $('#search-terms').val();
    $('#search-button').prop('disabled', true);

    $.ajax({
        type: 'POST',
        url: '/search',
        data: JSON.stringify({
            terms: searchTerms
            //pageToken: 0 // don't use unless you really want a specific page
        }),
        contentType: 'application/json'
    })
    .done(function(data) {
        searchResults = JSON.parse(data);
        $('#search-results').empty();
        $('#search-results-text').removeClass('hidden');
        $('#search-remove').removeClass('hidden');
        $('#search-button').prop('disabled', false);

        // TODO: separate backends somehow
        // right now we just sort songs by score
        var songs = [];
        _.each(_.pluck(searchResults, 'songs'), function(backendSongs) {
            _.each(backendSongs, function(song) {
                songs.push(song);
            });
        });
        songs = _.sortBy(songs, 'score').reverse();
        _.each(songs, function(song) {
            $.tmpl('searchTemplate', {
                title: song.title,
                artist: song.artist,
                album: song.album,
                albumArt: song.albumArt,
                duration: durationToString(song.duration / 1000),
                songID: song.songID,
                backendName: song.backendName
            }).appendTo('#search-results');
        });
        /*
            var songsInOrder = _.sortBy(searchResults[backendName].songs, 'score');
            _.each(songsInOrder, function(songID) {
                var song = searchResults[backendName].songs[songID];
            });
        });
        // TODO: pagination using backendResults.next/prevPageToken
        if (searchResults.length > resultsCount) {
            $.tmpl( 'ellipsisTemplate', {
        */
    }).fail(function() {
        $('#search-button').prop('disabled', false);
    });
};

var appendQueue = function(backendName, songID) {
    if (songID !== 0 && !songID) { return; }
    if (!backendName) { return; }
    searchResults[backendName].songs[songID].userID = $.cookie('userID');
    $.ajax({
        type: 'POST',
        url: '/queue',
        data: JSON.stringify({
            songs: [searchResults[backendName].songs[songID]]
        }),
        contentType: 'application/json'
    });

    $('#search-results').empty();
    $('#search-results-text').addClass('hidden');
    $('#search-remove').addClass('hidden');
};

var searchRemove = function() {
    $('#search-results').empty();
    $('#search-results-text').addClass('hidden');
    $('#search-remove').addClass('hidden');
};

var socket = io();
socket.on('queue', function(data) {
    queue = data.items;
    _.each(queue, function(song) {
        if (!song.albumArt) {
            song.albumArt = {};
        }
        if (!song.albumArt.lq) {
            song.albumArt.lq = 'media/NoAlbumArt.png';
        }
    });
    queueTruncated = (data.length > data.items.length);
    updateQueue();
});

socket.on('volume', function(data) {
    if (data.userID === $.cookie('userID')) { return; }
    $('#volume').val(data.volume);
    $('#audio')[0].volume = data.volume;
});
var setVolume = _.throttle(function(volume) {
    socket.emit('setVolume', {
        userID: $.cookie('userID'),
        volume: volume
    });
}, 100);

var startPlayback = function() {
    $('#audio').attr('src', '/song/' + currentSong.backendName +
            '/' + currentSong.songID + '.' + currentSong.format);

    var audio = document.getElementById('audio');
    var setPos = function() {
        var pos = 0;
        if (currentSong.position) {
            pos = currentSong.position / 1000 + (new Date().getTime() - currentSong.playbackStart) / 1000;
        }

        audio.currentTime = pos;
        audio.removeEventListener('loadedmetadata', setPos, false);
    };
    audio.addEventListener('loadedmetadata', setPos, false);
};

socket.on('playback', function(data) {
    currentSong = data;
    if (!data || !data.playbackStart) {
        currentSong.playbackStart = new Date().getTime();
        $('#audio').trigger('pause');
        paused = true;
        $('#playpauseicon').removeClass('glyphicon-pause glyphicon-play');
        $('#playpauseicon').addClass('glyphicon-play');

        clearInterval(progress.interval);
        if (data) {
            currentProgress = (data.position || 0);
            progress.started = new Date().getTime() - currentProgress;
            progress.duration = data.duration;
        }
    } else {
        currentSong.playbackStart = new Date().getTime();
        paused = false;
        $('#playpauseicon').removeClass('glyphicon-pause glyphicon-play');
        $('#playpauseicon').addClass('glyphicon-pause');

        // volume update
        $('#volume').val(data.volume);
        $('#audio')[0].volume = data.volume;

        currentProgress = (data.position || 0);
        progress.started = new Date() - currentProgress;
        progress.duration = data.duration;

        clearInterval(progress.interval);
        if (data.playbackStart) {
            progress.interval = setInterval(function() {
                updateProgress(100);
            }, 100);
        }

        if (streaming) {
            startPlayback();
        }
    }
});

var pad = function(number, length) {
    var str = '' + number;

    while (str.length < length) {
        str = '0' + str;
    }

    return str;
};

// UI
var updateProgress = function(dt) { // dt = ms passed since last call
    if (!queue[0]) {
        clearInterval(progress.interval);
        return;
    }

    var currentProgress = new Date() - progress.started;
    $('#progress').css('width', 100 * (currentProgress / progress.duration) + '%');
    if (currentProgress > progress.duration) {
        $('#progress').css('width', '100%');
    }
};

var updateQueue = function() {
    $('#queue').empty();

    if (queue) {
        // now playing
        if (queue[0]) {
            queue[0].durationString = durationToString(queue[0].duration / 1000);
            $.tmpl('nowPlayingTemplate', queue[0]).appendTo('#queue');
            updateProgress(0);
            $('#nowplaying').click(function(e) {
                var posX = e.pageX - $(this).offset().left;
                socket.emit('startPlayback', (posX / $(this).outerWidth()) * queue[0].duration);
            });
            $('#nowplaying').mousemove(function(e) {
                var posX = e.pageX - $(this).offset().left;
                $('#progressmouseover').css('width', 100 * (posX / $(this).outerWidth()) + '%');
            });
            $('#nowplaying').hover(function(e) {
                $('#progressmouseover').css('visibility', 'visible');
            }, function(e) {
                $('#progressmouseover').css('visibility', 'hidden');
            });
            $('#remove0').mousemove(function(e) {
                $('#progressmouseover').css('visibility', 'hidden');
                e.stopPropagation();
            });
            $('#remove0').hover(function(e) {
                // TODO: this is a bit stupid?
                $('#progressmouseover').css('visibility', 'visible');
            });
        }

        var onRemoveClick = function(e) {
            removeFromQueue(i, queue.backendName + queue.songID);
            e.stopPropagation();
        };

        // rest of queue
        for (var i = 1; i < queue.length; i++) {
            queue[i].durationString = durationToString(queue[i].duration / 1000);
            queue[i].pos = i;
            $.tmpl('queueTemplate', queue[i]).appendTo('#queue');
            $('#remove' + i).click(onRemoveClick);
        }
        if (queueTruncated) {
            $.tmpl('queueTruncated').appendTo('#queue');
        }
    }
};

var durationToString = function(seconds) {
    var durationString = Math.floor(seconds / 60);
    durationString += ':' + pad(Math.floor(seconds % 60), 2);
    return durationString;
};

var removeFromQueue = function(pos, id) {
    socket.emit('removeFromQueue', {
        pos: pos
    });
    $(document.getElementById(id)).css('background-color', '#fee');
};

var skipSongs = function(cnt) {
    socket.emit('skipSongs', cnt);
};

$(document).ready(function() {
    // generate a user ID if there is not one yet
    if (!$.cookie('userID')) {
        var s4 = function() {
            return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
        };
        var guid = s4() + s4() + '-' + s4() + '-' + s4() + '-' +
                s4() + '-' + s4() + s4() + s4();
        $.cookie('userID', guid);
    }

    var nowPlayingMarkup =
        '
' +
        '

' +
        '

' +
        '

' +
        '
' +
        '
${title}
' +
        '
${artist} (${album}) - ${durationString}
' +
        '
' +
        '
';

    $.template('nowPlayingTemplate', nowPlayingMarkup);

    var searchResultMarkup =
        '
' +

        '

' +
        '
${title}
' +
        '
${artist} (${album}) - ${duration}
' +
        '
';

    $.template('searchTemplate', searchResultMarkup);

    var ellipsisResultMarkup =
        '
' +
        '
${title}
' +
        '
';

    $.template('ellipsisTemplate', ellipsisResultMarkup);

    $('#search-terms').keyup(function(e) {
        if (e.keyCode === 13) {
            search();
        }
    });

    var queueMarkup =
        '
' +

        '

' +
        '

' +

        '
' +
        '
${title}
' +
        '
${artist} (${album}) - ${durationString}
' +
        '
' +
        '
';

    $.template('queueTemplate', queueMarkup);

    var queueTruncatedMarkup = '
' +
    '
' +
    '
...
' +
    '
' +
    '
';

    $.template('queueTruncated', queueTruncatedMarkup);

    var preMuteVolume;
    var setVolumeIcon = function() {
        var volume = $('#audio')[0].volume;
        $('#muteicon').removeClass(
                'glyphicon-volume-off ' +
                'glyphicon-volume-down ' +
                'glyphicon-volume-up');

        if (volume >= 0.5) {
            $('#muteicon').addClass('glyphicon-volume-up');
        } else if (volume > 0) {
            $('#muteicon').addClass('glyphicon-volume-down');
        } else {
            $('#muteicon').addClass('glyphicon-volume-off');
        }
    };
    $('#volume').on('input', function(event) {
        var volume = $('#volume').val();
        $('#audio')[0].volume = volume;
        setVolume(volume);
        setVolumeIcon();
    });
    $('#mute').click(function(event) {
        if ($('#volume').val() === 0) {
            $('#audio')[0].volume = preMuteVolume;
            $('#volume').val(preMuteVolume);
        } else {
            preMuteVolume = $('#audio')[0].volume;
            $('#audio')[0].volume = 0;
            $('#volume').val(0);
        }
        setVolumeIcon();
    });
    $('#previous').click(function(event) {
        socket.emit('skipSongs', -1);
    });
    $('#next').click(function(event) {
        socket.emit('skipSongs', 1);
    });
    $('#playpause').click(function(event) {
        if (paused) {
            socket.emit('startPlayback');
        } else {
            socket.emit('pausePlayback');
        }
    });
    $('#shuffle').click(function(event) {
        socket.emit('shuffleQueue');
    });
    $('#stream').click(function(event) {
        if (streaming) {
            streaming = false;
            var audio = document.getElementById('audio');

            var streamingButton = $('#stream');
            streamingButton.removeClass('btn-primary');

            audio.pause();
        } else {
            streaming = true;

            var streamingButton = $('#stream');
            streamingButton.addClass('btn-primary');

            startPlayback();
        }
    });
    $(function () {
      $('[data-toggle="tooltip"]').tooltip()
    })
});
<commit_msg>make tests pass also<commit_after>var queue = [];
var queueTruncated = false;
var searchResults = {};
var progress = {progress: 0, interval: null};
var paused = true;

var currentSong = {};
var currentProgress;
var streaming = false;

var search = function() {
    var searchTerms = $('#search-terms').val();
    $('#search-button').prop('disabled', true);

    $.ajax({
        type: 'POST',
        url: '/search',
        data: JSON.stringify({
            terms: searchTerms
            //pageToken: 0 // don't use unless you really want a specific page
        }),
        contentType: 'application/json'
    })
    .done(function(data) {
        searchResults = JSON.parse(data);
        $('#search-results').empty();
        $('#search-results-text').removeClass('hidden');
        $('#search-remove').removeClass('hidden');
        $('#search-button').prop('disabled', false);

        // TODO: separate backends somehow
        // right now we just sort songs by score
        var songs = [];
        _.each(_.pluck(searchResults, 'songs'), function(backendSongs) {
            _.each(backendSongs, function(song) {
                songs.push(song);
            });
        });
        songs = _.sortBy(songs, 'score').reverse();
        _.each(songs, function(song) {
            $.tmpl('searchTemplate', {
                title: song.title,
                artist: song.artist,
                album: song.album,
                albumArt: song.albumArt,
                duration: durationToString(song.duration / 1000),
                songID: song.songID,
                backendName: song.backendName
            }).appendTo('#search-results');
        });
        /*
            var songsInOrder = _.sortBy(searchResults[backendName].songs, 'score');
            _.each(songsInOrder, function(songID) {
                var song = searchResults[backendName].songs[songID];
            });
        });
        // TODO: pagination using backendResults.next/prevPageToken
        if (searchResults.length > resultsCount) {
            $.tmpl( 'ellipsisTemplate', {
        */
    }).fail(function() {
        $('#search-button').prop('disabled', false);
    });
};

var appendQueue = function(backendName, songID) {
    if (songID !== 0 && !songID) { return; }
    if (!backendName) { return; }
    searchResults[backendName].songs[songID].userID = $.cookie('userID');
    $.ajax({
        type: 'POST',
        url: '/queue',
        data: JSON.stringify({
            songs: [searchResults[backendName].songs[songID]]
        }),
        contentType: 'application/json'
    });

    $('#search-results').empty();
    $('#search-results-text').addClass('hidden');
    $('#search-remove').addClass('hidden');
};

var searchRemove = function() {
    $('#search-results').empty();
    $('#search-results-text').addClass('hidden');
    $('#search-remove').addClass('hidden');
};

var socket = io();
socket.on('queue', function(data) {
    queue = data.items;
    _.each(queue, function(song) {
        if (!song.albumArt) {
            song.albumArt = {};
        }
        if (!song.albumArt.lq) {
            song.albumArt.lq = 'media/NoAlbumArt.png';
        }
    });
    queueTruncated = (data.length > data.items.length);
    updateQueue();
});

socket.on('volume', function(data) {
    if (data.userID === $.cookie('userID')) { return; }
    $('#volume').val(data.volume);
    $('#audio')[0].volume = data.volume;
});
var setVolume = _.throttle(function(volume) {
    socket.emit('setVolume', {
        userID: $.cookie('userID'),
        volume: volume
    });
}, 100);

var startPlayback = function() {
    $('#audio').attr('src', '/song/' + currentSong.backendName +
            '/' + currentSong.songID + '.' + currentSong.format);

    var audio = document.getElementById('audio');
    var setPos = function() {
        var pos = 0;
        if (currentSong.position) {
            pos = currentSong.position / 1000 + (new Date().getTime() - currentSong.playbackStart) / 1000;
        }

        audio.currentTime = pos;
        audio.removeEventListener('loadedmetadata', setPos, false);
    };
    audio.addEventListener('loadedmetadata', setPos, false);
};

socket.on('playback', function(data) {
    currentSong = data;
    if (!data || !data.playbackStart) {
        currentSong.playbackStart = new Date().getTime();
        $('#audio').trigger('pause');
        paused = true;
        $('#playpauseicon').removeClass('glyphicon-pause glyphicon-play');
        $('#playpauseicon').addClass('glyphicon-play');

        clearInterval(progress.interval);
        if (data) {
            currentProgress = (data.position || 0);
            progress.started = new Date().getTime() - currentProgress;
            progress.duration = data.duration;
        }
    } else {
        currentSong.playbackStart = new Date().getTime();
        paused = false;
        $('#playpauseicon').removeClass('glyphicon-pause glyphicon-play');
        $('#playpauseicon').addClass('glyphicon-pause');

        // volume update
        $('#volume').val(data.volume);
        $('#audio')[0].volume = data.volume;

        currentProgress = (data.position || 0);
        progress.started = new Date() - currentProgress;
        progress.duration = data.duration;

        clearInterval(progress.interval);
        if (data.playbackStart) {
            progress.interval = setInterval(function() {
                updateProgress(100);
            }, 100);
        }

        if (streaming) {
            startPlayback();
        }
    }
});

var pad = function(number, length) {
    var str = '' + number;

    while (str.length < length) {
        str = '0' + str;
    }

    return str;
};

// UI
var updateProgress = function(dt) { // dt = ms passed since last call
    if (!queue[0]) {
        clearInterval(progress.interval);
        return;
    }

    var currentProgress = new Date() - progress.started;
    $('#progress').css('width', 100 * (currentProgress / progress.duration) + '%');
    if (currentProgress > progress.duration) {
        $('#progress').css('width', '100%');
    }
};

var updateQueue = function() {
    $('#queue').empty();

    if (queue) {
        // now playing
        if (queue[0]) {
            queue[0].durationString = durationToString(queue[0].duration / 1000);
            $.tmpl('nowPlayingTemplate', queue[0]).appendTo('#queue');
            updateProgress(0);
            $('#nowplaying').click(function(e) {
                var posX = e.pageX - $(this).offset().left;
                socket.emit('startPlayback', (posX / $(this).outerWidth()) * queue[0].duration);
            });
            $('#nowplaying').mousemove(function(e) {
                var posX = e.pageX - $(this).offset().left;
                $('#progressmouseover').css('width', 100 * (posX / $(this).outerWidth()) + '%');
            });
            $('#nowplaying').hover(function(e) {
                $('#progressmouseover').css('visibility', 'visible');
            }, function(e) {
                $('#progressmouseover').css('visibility', 'hidden');
            });
            $('#remove0').mousemove(function(e) {
                $('#progressmouseover').css('visibility', 'hidden');
                e.stopPropagation();
            });
            $('#remove0').hover(function(e) {
                // TODO: this is a bit stupid?
                $('#progressmouseover').css('visibility', 'visible');
            });
        }

        var onRemoveClick = function(e) {
            removeFromQueue(i, queue.backendName + queue.songID);
            e.stopPropagation();
        };

        // rest of queue
        for (var i = 1; i < queue.length; i++) {
            queue[i].durationString = durationToString(queue[i].duration / 1000);
            queue[i].pos = i;
            $.tmpl('queueTemplate', queue[i]).appendTo('#queue');
            $('#remove' + i).click(onRemoveClick);
        }
        if (queueTruncated) {
            $.tmpl('queueTruncated').appendTo('#queue');
        }
    }
};

var durationToString = function(seconds) {
    var durationString = Math.floor(seconds / 60);
    durationString += ':' + pad(Math.floor(seconds % 60), 2);
    return durationString;
};

var removeFromQueue = function(pos, id) {
    socket.emit('removeFromQueue', {
        pos: pos
    });
    $(document.getElementById(id)).css('background-color', '#fee');
};

var skipSongs = function(cnt) {
    socket.emit('skipSongs', cnt);
};

$(document).ready(function() {
    // generate a user ID if there is not one yet
    if (!$.cookie('userID')) {
        var s4 = function() {
            return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
        };
        var guid = s4() + s4() + '-' + s4() + '-' + s4() + '-' +
                s4() + '-' + s4() + s4() + s4();
        $.cookie('userID', guid);
    }

    var nowPlayingMarkup =
        '
' +
        '

' +
        '

' +
        '

' +
        '
' +
        '
${title}
' +
        '
${artist} (${album}) - ${durationString}
' +
        '
' +
        '
';

    $.template('nowPlayingTemplate', nowPlayingMarkup);

    var searchResultMarkup =
        '
' +

        '

' +
        '
${title}
' +
        '
${artist} (${album}) - ${duration}
' +
        '
';

    $.template('searchTemplate', searchResultMarkup);

    var ellipsisResultMarkup =
        '
' +
        '
${title}
' +
        '
';

    $.template('ellipsisTemplate', ellipsisResultMarkup);

    $('#search-terms').keyup(function(e) {
        if (e.keyCode === 13) {
            search();
        }
    });

    var queueMarkup =
        '
' +

        '

' +
        '

' +

        '
' +
        '
${title}
' +
        '
${artist} (${album}) - ${durationString}
' +
        '
' +
        '
';

    $.template('queueTemplate', queueMarkup);

    var queueTruncatedMarkup = '
' +
    '
' +
    '
...
' +
    '
' +
    '
';

    $.template('queueTruncated', queueTruncatedMarkup);

    var preMuteVolume;
    var setVolumeIcon = function() {
        var volume = $('#audio')[0].volume;
        $('#muteicon').removeClass(
                'glyphicon-volume-off ' +
                'glyphicon-volume-down ' +
                'glyphicon-volume-up');

        if (volume >= 0.5) {
            $('#muteicon').addClass('glyphicon-volume-up');
        } else if (volume > 0) {
            $('#muteicon').addClass('glyphicon-volume-down');
        } else {
            $('#muteicon').addClass('glyphicon-volume-off');
        }
    };
    $('#volume').on('input', function(event) {
        var volume = $('#volume').val();
        $('#audio')[0].volume = volume;
        setVolume(volume);
        setVolumeIcon();
    });
    $('#mute').click(function(event) {
        if ($('#volume').val() === 0) {
            $('#audio')[0].volume = preMuteVolume;
            $('#volume').val(preMuteVolume);
        } else {
            preMuteVolume = $('#audio')[0].volume;
            $('#audio')[0].volume = 0;
            $('#volume').val(0);
        }
        setVolumeIcon();
    });
    $('#previous').click(function(event) {
        socket.emit('skipSongs', -1);
    });
    $('#next').click(function(event) {
        socket.emit('skipSongs', 1);
    });
    $('#playpause').click(function(event) {
        if (paused) {
            socket.emit('startPlayback');
        } else {
            socket.emit('pausePlayback');
        }
    });
    $('#shuffle').click(function(event) {
        socket.emit('shuffleQueue');
    });
    $('#stream').click(function(event) {
        var streamingButton = $('#stream');
        if (streaming) {
            streaming = false;
            var audio = document.getElementById('audio');

            streamingButton.removeClass('btn-primary');

            audio.pause();
        } else {
            streaming = true;

            streamingButton.addClass('btn-primary');

            startPlayback();
        }
    });
    $(function () {
      $('[data-toggle="tooltip"]').tooltip();
    });
});
<|endoftext|>