<commit_before>/**
 * Provides a skinnable UI for playing Youtube videos by ID.
 * Requires Ojay and SWFObject version 2.0.
 * @constructor
 * @class YoutubePlayer
 */
var YoutubePlayer = new JS.Class({
    include: [Ojay.Observable, JS.State],

    /**
     * @param {String} videoId
     * @param {String} elementId
     */
    initialize: function(videoId, elementId, options) {
        this._videoId   = videoId;
        this._elementId = elementId;
        this._options   = options || {};

        this._options.skipTime    = options.skipTime    || this.klass.SKIP_TIME;
        this._options.volumeSteps = options.volumeSteps || this.klass.VOLUME_STEPS;

        this.klass._register(this);

        var elements = this._elements = {};
        Ojay.byId(this._elementId).insert(this.getHTML(), 'after');

        this._embed();
        this._setupProgressSlider();

        this.on('playing', function(player) {
            player.klass._playing(player);
            player.setState('PLAYING');
            player._elements._playButton.removeClass('play').addClass('pause').setContent('Pause');
        });

        this.on('paused', function(player) {
            player.setState('PAUSED');
            player._elements._playButton.removeClass('pause').addClass('play').setContent('Play');
        });
    },

    /**
     * Instantiates a flash video player in the correct element.
     * @returns {YoutubePlayer}
     */
    _embed: function() {
        var url = this.klass.getPlayerURL(this._elementId);
        if (url === null) return null;

        var element = Ojay.byId(this._elementId), width = element.getWidth();
        if (!this.klass.hasAcceptableFlash()) element.insert(this.klass._getFlashWarning(), 'before');

        swfobject.embedSWF( url, escape(this._elementId),
                            width, this._options.height || width / this.klass.ASPECT_RATIO,
                            this.klass.FLASH_VERSION,
                            null, null,
                            {allowScriptAccess: 'always', wmode: 'transparent'});
        return this;
    },

    /**
     * Returns an Ojay collection containing the HTML elements used for the UI.
     * @returns {DomCollection}
     */
    getHTML: function() {
        var elements = this._elements, self = this;
        if (elements._container) return elements._container;
        elements._container = Ojay(Ojay.HTML.div({className: 'youtube-controls'}, function(C) {
            C.h3({className: 'controls-header'}, 'Video player controls');

            elements._playButton = Ojay(C.button({className: 'play-pause'}, 'Play'));

            C.div({className: 'progress-controls'}, function(P) {
                elements._time = Ojay(P.div({className: 'time'}));
                elements._skipBack = Ojay(P.button({className: 'skip-back'}, 'Skip back'));
                P.concat(self.getProgressSliderElement().node);
                elements._skipForward = Ojay(P.button({className: 'skip-forwards'}, 'Skip forwards'));
            });

            C.div({className: 'volume-controls'}, function(V) {
                elements._volumeDownButton = Ojay(V.button({className: 'volume-down'}, 'Lower volume'));
                V.concat(self.getVolumeSteps().node);
                elements._volumeUpButton = Ojay(V.button({className: 'volume-up'}, 'Raise volume'));
            });
        }));

        elements._playButton.on('click')._(this).toggle();

        elements._skipBack.on('click', Ojay.stopDefault)._(this).skip(-this.klass.SKIP_TIME);
        elements._skipForward.on('click', Ojay.stopDefault)._(this).skip(this.klass.SKIP_TIME);

        elements._volumeDownButton.on('click', Ojay.stopDefault)._(this).decreaseVolume();
        elements._volumeUpButton.on('click', Ojay.stopDefault)._(this).increaseVolume();

        return elements._container;
    },

    /**
     * Returns HTML elements required to implement a slider.
     * @returns {DomCollection}
     */
    getProgressSliderElement: function() {
        var elements = this._elements;
        return elements._progressSlider = Ojay( Ojay.HTML.div({className: 'progress-slider'}, function(HTML) {
            elements._progressSliderThumb = Ojay( HTML.div({className: 'thumb'}) );
        }) );
    },

    /**
     */
    _setupProgressSlider: function() {
        var elements    = this._elements,
            thumbWidth  = elements._progressSliderThumb.getWidth(),
            sliderWidth = elements._progressSlider.getWidth();

        var limit = this._progressSliderLimit = sliderWidth - thumbWidth;
        this._progressSlider = YAHOO.widget.Slider.getHorizSlider(elements._progressSlider.node,
                elements._progressSliderThumb.node, 0, this._progressSliderLimit);

        this._progressBar = Ojay(Ojay.HTML.div({className: 'progress-bar', style: {width: 0}}));
        Ojay(this._progressSlider.getEl()).insert(this._progressBar, 'before');

        this._thumbWidth = thumbWidth;

        var player = this._getPlayer(), self = this;
        this._progressSlider.subscribe('change', function(value) {
            self.setCompletion(value / limit);
        });
    },

    /**
     * Sets the percentage completion of the video.
     * @param {Number} offset
     * @returns {YoutubePlayer}
     */
    setCompletion: function(offset) {
        var player = this._getPlayer();
        player.seekTo(offset * player.getDuration(), true);
        return this;
    },

    /**
     * Skips forward or back in time by the specified nubmer of seconds.
     * Providing a negative number skips back.
     */
    skip: function(offset) {
        var player   = this._getPlayer(),
            time     = player.getCurrentTime(),
            skipTime = time + offset;

        if (skipTime < 0) {
            skipTime = 0;
        } else if (skipTime > (duration = player.getDuration())) {
            skipTime = duration - 5;
        }

        player.seekTo(skipTime);
        return this;
    },

    getVolumeSteps: function() {
        if (this._elements._volumeSteps) return this._elements._volumeSteps;

        var elements = this._elements,
            numSteps = this._options.volumeSteps,
            step     = 100 / numSteps,
            self     = this;

        if (numSteps < 1) numSteps = 1;

        return elements._volumeSteps = Ojay(Ojay.HTML.div({className: 'volume-steps steps-' + numSteps}, function(V) {
            var stepAmount, ctrl, i;

            for (i = 0; i < numSteps; i++) {
                stepAmount = 100 / numSteps * (i + 1);
                elements['_stepControl' + i] = ctrl = Ojay(V.span({className: 'step step-' + i}));
                ctrl.on('click')._(self).setVolume(stepAmount);
            }
        }));
    },

    /**
     * Sets the volume as a percentage.
     * @param {Number} volume
     * @returns {YoutubePlayer}
     */
    setVolume: function(volume) {
        var player        = this._getPlayer(),
            currentVolume = player.getVolume(),
            classPrefix   = 'volume-level-';

        if (volume === undefined) volume = currentVolume;

        this._elements._volumeSteps.replaceClass(classPrefix + currentVolume,
                                                 classPrefix + (volume));

        player.setVolume(volume);
        return this;
    },

    /**
     * Increases the volume by one step, where the total number of steps is
     * determined by the volumeSteps option.
     * @returns {YoutubePlayer}
     */
    increaseVolume: function() {
        var step   = 100 / this._options.volumeSteps,
            volume = this._getPlayer().getVolume() + step;
        if (volume > 100) volume = 100;
        this.setVolume(volume);
    },

    /**
    * Decreases the volume by one step, where the total number of steps is
    * determined by the volumeSteps option.
     * @returns {YoutubePlayer}
     */
    decreaseVolume: function() {
        var step   = 100 / this._options.volumeSteps,
            volume = this._getPlayer().getVolume() - step;
        if (volume < 0) volume = 0;
        this.setVolume(volume);
    },

    /**
     * Cues the requested video when the video player is ready.
     */
    _onready: function() {
        var callback = 'YoutubePlayer._dispatchStateEvent("' + this._elementId + '")';
        this._getPlayer().addEventListener('onStateChange', callback);
        this._getPlayer().cueVideoById(this._videoId);
        this.setState('PAUSED');
        this.notifyObservers('ready');
        setInterval(this.method('updateTime'), 250);
        this.setVolume();
    },

    /**
     * Returns a string representation of the current play time.
     * @returns {String}
     */
    getTimeString: function() { try {
        var player = this._getPlayer();
        var time = player.getCurrentTime();
        var mins = (time / 60).floor(), secs = (time % 60).floor();
            mins = mins > 0 ? mins : 0;
            secs = secs > 0 ? secs : 0;
        return mins + ':' + (secs < 10 ? '0' : '') + secs;
    } catch (e) {
        return '';
    }},

    /**
     * Dispatches event calls from the YouTube API to Ojay.Observable's named
     * event listener system. See YoutubePlayer.STATES for event names.
     * @param {Number} state
     */
    _dispatchStateEvent: function(state) {
        for (var key in this.klass.STATES) {
            if (this.klass.STATES[key] == state)
                this.notifyObservers(key);
        }
    },

    /**
     * Returns a reference to the flash object that provides the Youtube API.
     * @returns {HTMLElement}
     */
    _getPlayer: function() {
        return Ojay.byId(this._elementId).node;
    },

    states: {
        PAUSED: {
            /**
             * @returns {YoutubePlayer}
             */
            play: function() {
                this._getPlayer().playVideo();
                return this;
            },

            /**
             * @returns {YoutubePlayer}
             */
            toggle: function() {
                return this.play();
            },

            /**
             * Updates the time display
             * @returns {YoutubePlayer}
             */
            updateTime: function() {
                this._elements._time.setContent(this.getTimeString());
                return this;
            }
        },

        PLAYING: {
            /**
             * @returns {YoutubePlayer}
             */
            pause: function() {
                this._getPlayer().pauseVideo();
                return this;
            },

            /**
             * @returns {YoutubePlayer}
             */
            toggle: function() {
                return this.pause();
            },

            /**
             * Updates the time display
             * @returns {YoutubePlayer}
             */
            updateTime: function() { try {
                var player = this._getPlayer();
                this._elements._time.setContent(this.getTimeString());
                var offset   = player.getCurrentTime() / player.getDuration(),
                    distance = offset * this._progressSliderLimit;

                this._progressSlider.setValue(distance, true, true, true);

                this._progressBar.setStyle({width: this._thumbWidth * offset +
                                                   distance + 'px'});
                return this;
            } catch (e) {
                return this;
            }}
        }
    },

    extend: {
        _instances: {},

        /**
         * @param {YoutubePlayer}
         */
        _register: function(player) {
            this._instances[player._elementId] = player;
        },

        /**
         * @param {YoutubePlayer}
         */
        _playing: function(player) {
            if (this._current && this._current !== player) this._current.pause();
            this._current = player;
        },

        /**
         * Returns the correct URL for the given video ID.
         * @param {String} videoId
         * @returns {String}
         */
        getVideoURL: function(videoId) {
            return 'http://www.youtube.com/v/' + videoId + '&enablejsapi=1';
        },

        /**
         * Returns the correct player URL for the set developer key.
         * @param {String} playerId
         * @returns {String}
         */
        getPlayerURL: function(playerId) {
            if (this.API_KEY === null) return null;
            var uri = Ojay.URI.parse('http://www.youtube.com/apiplayer?enablejsapi=1');
            uri.setParam('key', this.API_KEY);
            uri.setParam('playerapiid', playerId);
            return uri.toString();
        },

        /**
         * Returns the instance with the given ID.
         * @param {id}
         * @returns {YoutubePlayer}
         */
        findById: function(id) {
            return this._instances[id];
        },

        /**
         * Dispatches calls from the YouTube event API to individual
         * YoutubePlayer instances. Is curried to allow usage with the
         * YouTube event API while adding instance identification.
         * @param {String} playerId
         * @param {String} state
         */
        _dispatchStateEvent: function(playerId, state) {
            YoutubePlayer.findById(playerId)._dispatchStateEvent(state);
        }.curry(),

        /**
         * The amount of time, in seconds, to seek forwards or back when a skip
         * control is used.
         */
        SKIP_TIME: 15,

        /**
         * The number of steps from silent to full volume. This controls how
         * many step controls are displayed, as well as the fraction by which
         * the volume is increased or decreased by the volume up and down
         * controls.
         */
        VOLUME_STEPS: 4,

        FLASH_VERSION: '8',
        ASPECT_RATIO: 4/3,

        /* Must be assigned before use */
        CLIENT_ID:  null,
        API_KEY:    null,

        /**
         * Returns true iff we have a recent enough Flash plugin installed.
         * @returns {Boolean}
         */
        hasAcceptableFlash: function() {
            var expected = this.MINIMUM_FLASH_VERSION, actual = swfobject.getFlashPlayerVersion();
            return !(actual.major < expected.major ||
                     actual.minor < expected.minor ||
                     actual.release < expected.release);
        },

        /**
         * @returns {HTMLElement}
         */
        _getFlashWarning: function() {
            var self = this;
            return Ojay.HTML.p({className: 'flash-warning'},
                'If this media player does not work for you, try ',
                Ojay.HTML.a({href: self.FLASH_DOWNLOAD_URL},
                        'installing the latest Flash plugin'), '.');
        },

        /* Where to go to download Flash */
        FLASH_DOWNLOAD_URL:     'http://www.adobe.com/products/flashplayer/',
        MINIMUM_FLASH_VERSION:  {major: 9, minor: 0, release: 0},

        STATES: {
            unstarted:  -1,
            ended:      0,
            playing:    1,
            paused:     2,
            buffering:  3,
            cued:       5
        }
    }
});

onYouTubePlayerReady = function(playerId) {
    YoutubePlayer.findById(playerId)._onready();
};
<commit_msg>Better class names for volume steps.<commit_after>/**
 * Provides a skinnable UI for playing Youtube videos by ID.
 * Requires Ojay and SWFObject version 2.0.
 * @constructor
 * @class YoutubePlayer
 */
var YoutubePlayer = new JS.Class({
    include: [Ojay.Observable, JS.State],

    /**
     * @param {String} videoId
     * @param {String} elementId
     */
    initialize: function(videoId, elementId, options) {
        this._videoId   = videoId;
        this._elementId = elementId;
        this._options   = options || {};

        this._options.skipTime    = options.skipTime    || this.klass.SKIP_TIME;
        this._options.volumeSteps = options.volumeSteps || this.klass.VOLUME_STEPS;

        this.klass._register(this);

        var elements = this._elements = {};
        Ojay.byId(this._elementId).insert(this.getHTML(), 'after');

        this._embed();
        this._setupProgressSlider();

        this.on('playing', function(player) {
            player.klass._playing(player);
            player.setState('PLAYING');
            player._elements._playButton.removeClass('play').addClass('pause').setContent('Pause');
        });

        this.on('paused', function(player) {
            player.setState('PAUSED');
            player._elements._playButton.removeClass('pause').addClass('play').setContent('Play');
        });
    },

    /**
     * Instantiates a flash video player in the correct element.
     * @returns {YoutubePlayer}
     */
    _embed: function() {
        var url = this.klass.getPlayerURL(this._elementId);
        if (url === null) return null;

        var element = Ojay.byId(this._elementId), width = element.getWidth();
        if (!this.klass.hasAcceptableFlash()) element.insert(this.klass._getFlashWarning(), 'before');

        swfobject.embedSWF( url, escape(this._elementId),
                            width, this._options.height || width / this.klass.ASPECT_RATIO,
                            this.klass.FLASH_VERSION,
                            null, null,
                            {allowScriptAccess: 'always', wmode: 'transparent'});
        return this;
    },

    /**
     * Returns an Ojay collection containing the HTML elements used for the UI.
     * @returns {DomCollection}
     */
    getHTML: function() {
        var elements = this._elements, self = this;
        if (elements._container) return elements._container;
        elements._container = Ojay(Ojay.HTML.div({className: 'youtube-controls'}, function(C) {
            C.h3({className: 'controls-header'}, 'Video player controls');

            elements._playButton = Ojay(C.button({className: 'play-pause'}, 'Play'));

            C.div({className: 'progress-controls'}, function(P) {
                elements._time = Ojay(P.div({className: 'time'}));
                elements._skipBack = Ojay(P.button({className: 'skip-back'}, 'Skip back'));
                P.concat(self.getProgressSliderElement().node);
                elements._skipForward = Ojay(P.button({className: 'skip-forwards'}, 'Skip forwards'));
            });

            C.div({className: 'volume-controls'}, function(V) {
                elements._volumeDownButton = Ojay(V.button({className: 'volume-down'}, 'Lower volume'));
                V.concat(self.getVolumeSteps().node);
                elements._volumeUpButton = Ojay(V.button({className: 'volume-up'}, 'Raise volume'));
            });
        }));

        elements._playButton.on('click')._(this).toggle();

        elements._skipBack.on('click', Ojay.stopDefault)._(this).skip(-this.klass.SKIP_TIME);
        elements._skipForward.on('click', Ojay.stopDefault)._(this).skip(this.klass.SKIP_TIME);

        elements._volumeDownButton.on('click', Ojay.stopDefault)._(this).decreaseVolume();
        elements._volumeUpButton.on('click', Ojay.stopDefault)._(this).increaseVolume();

        return elements._container;
    },

    /**
     * Returns HTML elements required to implement a slider.
     * @returns {DomCollection}
     */
    getProgressSliderElement: function() {
        var elements = this._elements;
        return elements._progressSlider = Ojay( Ojay.HTML.div({className: 'progress-slider'}, function(HTML) {
            elements._progressSliderThumb = Ojay( HTML.div({className: 'thumb'}) );
        }) );
    },

    /**
     */
    _setupProgressSlider: function() {
        var elements    = this._elements,
            thumbWidth  = elements._progressSliderThumb.getWidth(),
            sliderWidth = elements._progressSlider.getWidth();

        var limit = this._progressSliderLimit = sliderWidth - thumbWidth;
        this._progressSlider = YAHOO.widget.Slider.getHorizSlider(elements._progressSlider.node,
                elements._progressSliderThumb.node, 0, this._progressSliderLimit);

        this._progressBar = Ojay(Ojay.HTML.div({className: 'progress-bar', style: {width: 0}}));
        Ojay(this._progressSlider.getEl()).insert(this._progressBar, 'before');

        this._thumbWidth = thumbWidth;

        var player = this._getPlayer(), self = this;
        this._progressSlider.subscribe('change', function(value) {
            self.setCompletion(value / limit);
        });
    },

    /**
     * Sets the percentage completion of the video.
     * @param {Number} offset
     * @returns {YoutubePlayer}
     */
    setCompletion: function(offset) {
        var player = this._getPlayer();
        player.seekTo(offset * player.getDuration(), true);
        return this;
    },

    /**
     * Skips forward or back in time by the specified nubmer of seconds.
     * Providing a negative number skips back.
     */
    skip: function(offset) {
        var player   = this._getPlayer(),
            time     = player.getCurrentTime(),
            skipTime = time + offset;

        if (skipTime < 0) {
            skipTime = 0;
        } else if (skipTime > (duration = player.getDuration())) {
            skipTime = duration - 5;
        }

        player.seekTo(skipTime);
        return this;
    },

    getVolumeSteps: function() {
        if (this._elements._volumeSteps) return this._elements._volumeSteps;

        var elements = this._elements,
            numSteps = this._options.volumeSteps,
            step     = 100 / numSteps,
            self     = this;

        if (numSteps < 1) numSteps = 1;

        return elements._volumeSteps = Ojay(Ojay.HTML.div({className: 'volume-steps steps-' + numSteps}, function(V) {
            var stepAmount, ctrl, i;

            for (i = 1; i <= numSteps; i++) {
                stepAmount = 100 / numSteps * i;
                elements['_stepControl' + i] = ctrl = Ojay(V.span({className: 'step step-' + i}));
                ctrl.on('click')._(self).setVolume(stepAmount);
            }
        }));
    },

    /**
     * Sets the volume as a percentage.
     * @param {Number} volume
     * @returns {YoutubePlayer}
     */
    setVolume: function(volume) {
        var player        = this._getPlayer(),
            currentVolume = player.getVolume(),
            classPrefix   = 'volume-level-';

        if (volume === undefined) volume = currentVolume;

        this._elements._volumeSteps.replaceClass(classPrefix + currentVolume,
                                                 classPrefix + (volume));

        player.setVolume(volume);
        return this;
    },

    /**
     * Increases the volume by one step, where the total number of steps is
     * determined by the volumeSteps option.
     * @returns {YoutubePlayer}
     */
    increaseVolume: function() {
        var step   = 100 / this._options.volumeSteps,
            volume = this._getPlayer().getVolume() + step;
        if (volume > 100) volume = 100;
        this.setVolume(volume);
    },

    /**
    * Decreases the volume by one step, where the total number of steps is
    * determined by the volumeSteps option.
     * @returns {YoutubePlayer}
     */
    decreaseVolume: function() {
        var step   = 100 / this._options.volumeSteps,
            volume = this._getPlayer().getVolume() - step;
        if (volume < 0) volume = 0;
        this.setVolume(volume);
    },

    /**
     * Cues the requested video when the video player is ready.
     */
    _onready: function() {
        var callback = 'YoutubePlayer._dispatchStateEvent("' + this._elementId + '")';
        this._getPlayer().addEventListener('onStateChange', callback);
        this._getPlayer().cueVideoById(this._videoId);
        this.setState('PAUSED');
        this.notifyObservers('ready');
        setInterval(this.method('updateTime'), 250);
        this.setVolume();
    },

    /**
     * Returns a string representation of the current play time.
     * @returns {String}
     */
    getTimeString: function() { try {
        var player = this._getPlayer();
        var time = player.getCurrentTime();
        var mins = (time / 60).floor(), secs = (time % 60).floor();
            mins = mins > 0 ? mins : 0;
            secs = secs > 0 ? secs : 0;
        return mins + ':' + (secs < 10 ? '0' : '') + secs;
    } catch (e) {
        return '';
    }},

    /**
     * Dispatches event calls from the YouTube API to Ojay.Observable's named
     * event listener system. See YoutubePlayer.STATES for event names.
     * @param {Number} state
     */
    _dispatchStateEvent: function(state) {
        for (var key in this.klass.STATES) {
            if (this.klass.STATES[key] == state)
                this.notifyObservers(key);
        }
    },

    /**
     * Returns a reference to the flash object that provides the Youtube API.
     * @returns {HTMLElement}
     */
    _getPlayer: function() {
        return Ojay.byId(this._elementId).node;
    },

    states: {
        PAUSED: {
            /**
             * @returns {YoutubePlayer}
             */
            play: function() {
                this._getPlayer().playVideo();
                return this;
            },

            /**
             * @returns {YoutubePlayer}
             */
            toggle: function() {
                return this.play();
            },

            /**
             * Updates the time display
             * @returns {YoutubePlayer}
             */
            updateTime: function() {
                this._elements._time.setContent(this.getTimeString());
                return this;
            }
        },

        PLAYING: {
            /**
             * @returns {YoutubePlayer}
             */
            pause: function() {
                this._getPlayer().pauseVideo();
                return this;
            },

            /**
             * @returns {YoutubePlayer}
             */
            toggle: function() {
                return this.pause();
            },

            /**
             * Updates the time display
             * @returns {YoutubePlayer}
             */
            updateTime: function() { try {
                var player = this._getPlayer();
                this._elements._time.setContent(this.getTimeString());
                var offset   = player.getCurrentTime() / player.getDuration(),
                    distance = offset * this._progressSliderLimit;

                this._progressSlider.setValue(distance, true, true, true);

                this._progressBar.setStyle({width: this._thumbWidth * offset +
                                                   distance + 'px'});
                return this;
            } catch (e) {
                return this;
            }}
        }
    },

    extend: {
        _instances: {},

        /**
         * @param {YoutubePlayer}
         */
        _register: function(player) {
            this._instances[player._elementId] = player;
        },

        /**
         * @param {YoutubePlayer}
         */
        _playing: function(player) {
            if (this._current && this._current !== player) this._current.pause();
            this._current = player;
        },

        /**
         * Returns the correct URL for the given video ID.
         * @param {String} videoId
         * @returns {String}
         */
        getVideoURL: function(videoId) {
            return 'http://www.youtube.com/v/' + videoId + '&enablejsapi=1';
        },

        /**
         * Returns the correct player URL for the set developer key.
         * @param {String} playerId
         * @returns {String}
         */
        getPlayerURL: function(playerId) {
            if (this.API_KEY === null) return null;
            var uri = Ojay.URI.parse('http://www.youtube.com/apiplayer?enablejsapi=1');
            uri.setParam('key', this.API_KEY);
            uri.setParam('playerapiid', playerId);
            return uri.toString();
        },

        /**
         * Returns the instance with the given ID.
         * @param {id}
         * @returns {YoutubePlayer}
         */
        findById: function(id) {
            return this._instances[id];
        },

        /**
         * Dispatches calls from the YouTube event API to individual
         * YoutubePlayer instances. Is curried to allow usage with the
         * YouTube event API while adding instance identification.
         * @param {String} playerId
         * @param {String} state
         */
        _dispatchStateEvent: function(playerId, state) {
            YoutubePlayer.findById(playerId)._dispatchStateEvent(state);
        }.curry(),

        /**
         * The amount of time, in seconds, to seek forwards or back when a skip
         * control is used.
         */
        SKIP_TIME: 15,

        /**
         * The number of steps from silent to full volume. This controls how
         * many step controls are displayed, as well as the fraction by which
         * the volume is increased or decreased by the volume up and down
         * controls.
         */
        VOLUME_STEPS: 4,

        FLASH_VERSION: '8',
        ASPECT_RATIO: 4/3,

        /* Must be assigned before use */
        CLIENT_ID:  null,
        API_KEY:    null,

        /**
         * Returns true iff we have a recent enough Flash plugin installed.
         * @returns {Boolean}
         */
        hasAcceptableFlash: function() {
            var expected = this.MINIMUM_FLASH_VERSION, actual = swfobject.getFlashPlayerVersion();
            return !(actual.major < expected.major ||
                     actual.minor < expected.minor ||
                     actual.release < expected.release);
        },

        /**
         * @returns {HTMLElement}
         */
        _getFlashWarning: function() {
            var self = this;
            return Ojay.HTML.p({className: 'flash-warning'},
                'If this media player does not work for you, try ',
                Ojay.HTML.a({href: self.FLASH_DOWNLOAD_URL},
                        'installing the latest Flash plugin'), '.');
        },

        /* Where to go to download Flash */
        FLASH_DOWNLOAD_URL:     'http://www.adobe.com/products/flashplayer/',
        MINIMUM_FLASH_VERSION:  {major: 9, minor: 0, release: 0},

        STATES: {
            unstarted:  -1,
            ended:      0,
            playing:    1,
            paused:     2,
            buffering:  3,
            cued:       5
        }
    }
});

onYouTubePlayerReady = function(playerId) {
    YoutubePlayer.findById(playerId)._onready();
};
<|endoftext|>


<commit_before>/**
 *
 *
 * @param host
 * @param port
 * @param options
 * @constructor
 */

var WSManager = function (host, port, options){
    var defaultOpt = {
        reconnectInterval: 8000,
        timeout: 8000
    };

    this.ws = null;
    this.url = "ws://" + host + ":" + port;
    this.host = host;
    this.port = port;

    this.reconnecting = false;
    this.connectTimer = null;
    this.reconnectTimer = null;
    this.reconnAttempt = 0;

    //Reconnection interval time (milliseconds)
    this.reconnectInterval = options.reconnectInterval;

    //Connection attempt timeout (milliseconds)
    this.timeout = options.timeout;

    //Storage for bound callback events
    this.callbacks = {};

    // Hack for firefox
    window.addEventListener("beforeunload", function() {
        this.ws.onclose = function () {}; // disable onclose handler
        this.ws.close();
        this.ws = null;
    }.bind(this), false);

};

WSManager.prototype.connect = function () {
    this.ws = null;

    if (window.WebSocket === 'undefined') {
        this.ws = new window.WebSocket(this.url);
    }else if (window.MozWebSocket === 'undefined') {
        this.ws = new window.MozWebSocket(this.url);
    } else {
        alert("This Browser does not support WebSockets.  Use newest version of Google Chrome, FireFox or Opera. ");
        return;
    }

    //Timeout for connection attempt NOTE: Disabled due abnormal behaviour with Firefox on Android
    /*
     this.connectTimer = setTimeout(function() {
     if(this.ws != null)
     if(this.ws.readyState != this.ws.OPEN)
     this.ws.close()
     }.bind(this), this.timeout)
     */

    this.ws.onopen = function() {
        //clearTimeout(this.connectTimer)
        //this.connectTimer = null

        clearTimeout(this.reconnectTimer);
        this.reconnectTimer = null;

        this.reconnAttempt = 0;
        this.reconnecting = false;

        this.triggerEvent("connected", this.host+":" + this.port);

    }.bind(this);

    this.ws.onmessage = function (evt) {
        //console.log("Got msg: " + evt.data)
        this.parseMessage(evt.data);
    }.bind(this);

    this.ws.onclose = function(evt) {
        //clearTimeout(this.connectTimer)
        //this.connectTimer = null

        clearTimeout(this.reconnectTimer);
        this.reconnectTimer = null;

        if(!this.reconnecting){
            var reason = "failed";
            if(evt.wasClean)
            {
                reason = "closed";
            }

            this.triggerEvent("disconnected", {url: this.host + ":" + this.port, reason: reason, code: evt.code});
        }

        // Reconnect if the  connection was not closed cleanly (network error/abnormal server close etc.)
        if(!evt.wasClean){
            this.reconnecting = true;

            console.log("Reconnecting in " + this.reconnectInterval/1000 + " seconds...");

            this.reconnectTimer = setTimeout(function() {
                if(this.ws !== null){
                    if(this.ws.readyState !== this.ws.OPEN || this.ws.readyState !== this.ws.CONNECTING){
                        this.reconnAttempt = this.reconnAttempt + 1;
                        this.triggerEvent("reconnecting", {host: this.url, attempt: this.reconnAttempt});

                        this.connect(this.url);
                    }
                }
            }.bind(this), this.reconnectInterval);
        }

    }.bind(this);

    this.ws.onerror = function(e) {
        this.triggerEvent("error", e);
    };
};

WSManager.prototype.stopReconnect = function() {
    if(this.reconnectTimer !== null)
    {
        clearTimeout(this.reconnectTimer);
    }

    if(this.connectTimer !== null)
    {
        clearTimeout(this.connectTimer);
    }
    this.reconnecting = false;
};

WSManager.prototype.parseMessage = function (message) {
    var parsed = JSON.parse(message);
    this.processEvent(parsed);
};

WSManager.prototype.processEvent = function (json) {
    if(json.event){
        //console.log("Got event: "+json['event'])
        if(json.data){

            // Triggering the event
            this.triggerEvent(json.event, json.data);
        }
    }
};

//A function for binding custom event callbacks for Connection
WSManager.prototype.bindEvent = function(eventName, callback){
    this.callbacks[eventName] = this.callbacks[eventName] || [];
    this.callbacks[eventName].push(callback);
    return this;
};

//Triggers the bound event and gives it some data as argument if it has a callback function
WSManager.prototype.triggerEvent = function(eventName, message){
    var eventChain = this.callbacks[eventName], i;
    if(eventChain === 'undefined'){
        console.log("Error: Received an unknown event: " + eventName);
        return;
    }
    for(i = 0; i < eventChain.length; i+1){
        eventChain[i](message);
    }
};

<commit_msg>Removed unused wsmanager.js<commit_after><|endoftext|>


<commit_before>const Sequelize = require('sequelize');

const db = process.env.CLEARDB_DATABASE_URL ? new Sequelize(process.env.CLEARDB_DATABASE_URL)
                                    : new Sequelize('computervision', 'root', 'password');
exports.db = db;<commit_msg>(dev) commented out db pw<commit_after>const Sequelize = require('sequelize');

const db = process.env.CLEARDB_DATABASE_URL ? new Sequelize(process.env.CLEARDB_DATABASE_URL)
                                    : new Sequelize('computervision', 'root', '');
exports.db = db;<|endoftext|>


<commit_before>module.exports = function (grunt) {

	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),

		jsdoc: {
			configFile: 'jsdoc-conf.json',
			dest: 'output'
		},

		less: {
			options: {
				paths: ['<%= copyAssets.src %>/css']
			},
			files: {
				expand: true,
				cwd: '<%= copyAssets.src %>/css',
				src: 'main.less',
				ext: '.css',
				dest: '<%= copyAssets.dest %>/css'
			}
		},
		subst: {
			options: {
				pattern: /\$dev-guide/ig,
				replacement: 'developer-guide'
			},
			files: {
				src: 'output/**/*.html'
			}
		},

		copyAssets: {
			src: 'assets',
			dest: '<%= jsdoc.dest %>'
		}

	});

	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-string-replace');

	grunt.loadTasks('tasks');

	grunt.registerTask('default', ['jsdoc', 'less', 'copy-assets', 'subst']);

	grunt.registerMultiTask('subst', 'Substitute string in files, in place', function() {
		var options = this.options({});

		this.files.forEach(function(file) {
			file.src.forEach(function(path) {
				grunt.file.copy(path, path, {
					process: function(text) {
						return text.replace(options.pattern, options.replacement);
					}
				});
			});
		});
	});

};
<commit_msg>Updated subst tasks and moved subst to tasks dir<commit_after>module.exports = function (grunt) {

	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),

		jsdoc: {
			configFile: 'jsdoc-conf.json',
			dest: 'output'
		},

		less: {
			options: {
				paths: ['<%= copyAssets.src %>/css']
			},
			files: {
				expand: true,
				cwd: '<%= copyAssets.src %>/css',
				src: 'main.less',
				ext: '.css',
				dest: '<%= copyAssets.dest %>/css'
			}
		},
		subst: {
			'dev-guide': {
				options: {
					pattern: /\$dev-guide/ig,
					replacement: 'developer-guide'
				},
				files: {
					src: 'output/**/*.html'
				}
			},
			'api': {
				options: {
					pattern: /\$api/ig,
					relative: true,
					base: 'output'
				},
				files: {
					src: 'output/**/*.html'
				}
			}
		},

		copyAssets: {
			src: 'assets',
			dest: '<%= jsdoc.dest %>'
		}

	});

	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-string-replace');

	grunt.loadTasks('tasks');

	grunt.registerTask('default', ['jsdoc', 'less', 'copy-assets', 'subst']);
};
<|endoftext|>


<commit_before>'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

var modRewrite = require('connect-modrewrite');
//var lrSnippet = require('grunt-contrib-watch/lib/utils').livereloadSnippet;
var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;

module.exports = function (grunt) {

    require('time-grunt')(grunt); // show elapsed time at the end
    require('load-grunt-tasks')(grunt); // load all grunt tasks

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'release'
    };

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
          options: {
            livereload: '<%= connect.options.livereload %>'
          },
            styles: {
                files: ['<%= yeoman.app %>/styles/**/*.{less,css}'],
                tasks: ['less:dist']
            },
            compass: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['compass:server']
            },
            js: {
                files: ['<%= yeoman.app %>/**/*.js'],
                tasks: []
            },
            jade: {
                files: [
                    '<%= yeoman.app %>/**/*.{html,jade}'
                ],
                tasks: ['jade', 'preprocess:dist']
            },
          assets: {
                files: [
                    '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ],
                tasks: []
            },
        },
        connect: {
            options: {
              port: 80,
              livereload: 35729,
              // change this to '0.0.0.0' to access the server from outside
              hostname: '0.0.0.0',
              middleware: function (connect, options) {
                //console.log(lrSnippet);
                var middlewares = [proxySnippet,

                  modRewrite(['!\\.html|\\.js|\\.css|\\.png|\\.svg$ /index.html [L]'])
                ];

                if (!Array.isArray(options.base)) {
                  options.base = [options.base];
                }
                options.base.forEach(function(base) {
                  middlewares.push(connect.static(base)); // Serve static files.
                });
                middlewares.push(require('connect-livereload')());
                //middlewares.push(lrSnippet());
                return middlewares;
              },
            },
            proxies: [
          {
            context: '/api',
            host: 'localhost', // you can use localhost...
            port: 3000, // enter your port number here...
            https: false,
            changeOrigin: false
          }
        ],
            livereload: {
                options: {
                    open: true,
                    base: [
                        yeomanConfig.dist,
                        '.tmp',
                        //yeomanConfig.app,
                        ''
                    ]
                }
            }//,
            /*test: {
                options: {
                    base: [
                        '.tmp',
                        'test',
                        yeomanConfig.app,
                    ]
                }
            },
            dist: {
                options: {
                    open: true,
                    base: yeomanConfig.dist
                }
            }*/
        },
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= yeoman.dist %>/*',
                        '!<%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc'
            },
            all: [
                'Gruntfile.js',
                '<%= yeoman.app %>/scripts/{,*/}*.js',
                '!<%= yeoman.app %>/scripts/vendor/*',
                'test/spec/{,*/}*.js'
            ]
        },
        autoprefixer: {
            options: {
                browsers: ['last 1 version']
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },
        // not used since Uglify task does concat,
        // but still available if needed
        /*concat: {
            dist: {}
        },*/
        'bower-install': {
            app: {
                html: '<%= yeoman.app %>/index.html',
                ignorePath: '<%= yeoman.app %>/'
            }
        },
        // not enabled since usemin task does concat and uglify
        // check index.html to edit your build targets
        // enable this task if you prefer defining your build targets here
        uglify: {
        //    dist: {}
          options: {
            report: 'min',
            mangle: false//{topLevel: false}
          }
        },
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
                        '<%= yeoman.dist %>/styles/fonts/{,*/}*.*'
                    ]
                }
            }
        },
        less: {
          dist: {
            files: {
              '<%= yeoman.dist %>/styles/style.css': '<%= yeoman.app %>/styles/style.less'
            }
          }
        },
        compass: {
          options: {
            sassDir: '<%= yeoman.app %>/styles',
            cssDir: '<%= yeoman.dist %>/styles',
            generatedImagesDir: '.tmp/images/generated',
            imagesDir: '<%= yeoman.app %>/images',
            javascriptsDir: '<%= yeoman.app %>/scripts',
            fontsDir: '<%= yeoman.app %>/styles/fonts',
            //importPath: '/bower_components',
            httpImagesPath: '/images',
            httpGeneratedImagesPath: '/images/generated',
            httpFontsPath: '/styles/fonts',
            relativeAssets: false,
            noLineComments: true
          },
          dist: {
            options: {
              generatedImagesDir: '<%= yeoman.dist %>/images/generated'
            }
          },
          server: {
            options: {
              debugInfo: true
            }
          }
        },
        preprocess : {
          dev : {
            options : {
              context:{env:'development'}
            },
            files : {
              '<%= yeoman.dist %>/index.html':'<%= yeoman.app %>/index.html',
                '<%= yeoman.dist %>/templates/sessionlist.html':'<%= yeoman.app %>/templates/sessionlist.html',
                '<%= yeoman.dist %>/templates/*.html':'<%= yeoman.app %>/templates/*.html',
            }
          },
          dist : {
            options : {
              context:{env:'production'}
            },
            files : {
                '<%= yeoman.dist %>/index.html':'<%= yeoman.app %>/index.html',
                '<%= yeoman.dist %>/templates/sessionlist.html':'<%= yeoman.app %>/templates/sessionlist.html',
                '<%= yeoman.dist %>/templates/results.html':'<%= yeoman.app %>/templates/results.html',
                '<%= yeoman.dist %>/templates/signin.html':'<%= yeoman.app %>/templates/signin.html'
            }
          }
        },
        useminPrepare: {
            options: {
                dest: '<%= yeoman.dist %>'
            },
            html: '<%= yeoman.app %>/index.html'
        },
        usemin: {
            options: {
              dirs: ['<%= yeoman.dist %>']
            },
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
            //js: ['<%= yeoman.app %>/{,*/}*.js']
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        cssmin: {
            // This task is pre-configured if you do not wish to use Usemin
            // blocks for your CSS. By default, the Usemin block from your
            // `index.html` will take care of minification, e.g.
            //
            //     <!-- build:css({.tmp,app}) styles/main.css -->
            //
            // dist: {
            //     files: {
            //         '<%= yeoman.dist %>/styles/main.css': [
            //             '.tmp/styles/{,*/}*.css',
            //             '<%= yeoman.app %>/styles/{,*/}*.css'
            //         ]
            //     }
            // }
        },
        htmlmin: {
            dist: {
                options: {
                    removeComments: true,
                    // https://github.com/yeoman/grunt-usemin/issues/44
                    collapseWhitespace: true,
                    collapseBooleanAttributes: true,
                    //removeAttributeQuotes: true,
                    removeRedundantAttributes: true,
                    //useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true
                },
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.dist %>',
                    src: '*.html',
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'images/{,*/}*.{webp,gif,png,jpeg,jpg}',
                        //'styles/fonts/{,*/}*.*',
                        //'bower_components/sass-bootstrap/fonts/*.*',
                        //'styles/{,*/}*.css',
                        'templates/{,*/}*.html'
                    ]
                }]
            },
            //styles: {
            //    expand: true,
            //    dot: true,
            //    cwd: '<%= yeoman.app %>/styles',
            //    dest: '.tmp/styles/',
            //    src: '{,*/}*.css'
            //}
        },
        modernizr: {
            devFile: '<%= yeoman.app %>/bower_components/modernizr/modernizr.js',
            outputFile: '<%= yeoman.dist %>/bower_components/modernizr/modernizr.js',
            files: [
                '<%= yeoman.dist %>/scripts/{,*/}*.js',
                '<%= yeoman.dist %>/styles/{,*/}*.css',
                '!<%= yeoman.dist %>/scripts/vendor/*'
            ],
            uglify: true
        },
        concurrent: {
            min: [
                //'copy:styles',
                //'imagemin',
                'svgmin',
            ]
        },
        jade: {
          compile: {
            options: {
                pretty: true
            },
            files: [
              {
                    expand: true,
                    cwd: '<%= yeoman.app %>',
                    src: '**/*.jade', //['{,*/}*.jade','templates/{,*/}*.jade'],
                    dest: 'release',
                    ext: '.html'
                }
            ]
        }
    }
    });

    grunt.registerTask('server', function (target) {
        //if (target === 'dist') {
        //    return grunt.task.run(['build', 'connect:dist:keepalive']);
        //}

        grunt.task.run([
            'build',
            'configureProxies',
            //'connect:livereload',
            'watch'
        ]);
    });

    grunt.registerTask('build', [
        'clean:dist',
        'preprocess:dist', // generate html
        'compass:dist',
        //'less:dist',
        'concurrent:min', // copy and minify assets
        //'autoprefixer', // autoprefix css to .tmp
        'jade',
        //'useminPrepare', // prepare usemin tasks
        //'concat',
        //'cssmin', // minify css from .tmp to release
        //'uglify',
        /*'modernizr',*/
        'copy:dist',
        /*'rev',*/
        //'usemin', // update usemin html references
        //'htmlmin' // minify html in place
    ]);

    grunt.registerTask('default', [
        'jshint',
        'build'
    ]);

    grunt.registerTask('heroku', 'clean less mincss');
};
<commit_msg>grunt dammit<commit_after>'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

var modRewrite = require('connect-modrewrite');
//var lrSnippet = require('grunt-contrib-watch/lib/utils').livereloadSnippet;
var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;

module.exports = function (grunt) {

    require('time-grunt')(grunt); // show elapsed time at the end
    require('load-grunt-tasks')(grunt); // load all grunt tasks

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'release'
    };

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
          options: {
            livereload: '<%= connect.options.livereload %>'
          },
            styles: {
                files: ['<%= yeoman.app %>/styles/**/*.{less,css}'],
                tasks: ['less:dist']
            },
            compass: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['compass:server']
            },
            js: {
                files: ['<%= yeoman.app %>/**/*.js'],
                tasks: []
            },
            jade: {
                files: [
                    '<%= yeoman.app %>/**/*.{html,jade}'
                ],
                tasks: ['jade', 'preprocess:dist']
            },
          assets: {
                files: [
                    '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ],
                tasks: []
            },
        },
        connect: {
            options: {
              port: 80,
              livereload: 35729,
              // change this to '0.0.0.0' to access the server from outside
              hostname: '0.0.0.0',
              middleware: function (connect, options) {
                //console.log(lrSnippet);
                var middlewares = [proxySnippet,

                  modRewrite(['!\\.html|\\.js|\\.css|\\.png|\\.svg$ /index.html [L]'])
                ];

                if (!Array.isArray(options.base)) {
                  options.base = [options.base];
                }
                options.base.forEach(function(base) {
                  middlewares.push(connect.static(base)); // Serve static files.
                });
                middlewares.push(require('connect-livereload')());
                //middlewares.push(lrSnippet());
                return middlewares;
              },
            },
            proxies: [
          {
            context: '/api',
            host: 'localhost', // you can use localhost...
            port: 3000, // enter your port number here...
            https: false,
            changeOrigin: false
          }
        ],
            livereload: {
                options: {
                    open: true,
                    base: [
                        yeomanConfig.dist,
                        '.tmp',
                        //yeomanConfig.app,
                        ''
                    ]
                }
            }//,
            /*test: {
                options: {
                    base: [
                        '.tmp',
                        'test',
                        yeomanConfig.app,
                    ]
                }
            },
            dist: {
                options: {
                    open: true,
                    base: yeomanConfig.dist
                }
            }*/
        },
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= yeoman.dist %>/*',
                        '!<%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc'
            },
            all: [
                'Gruntfile.js',
                '<%= yeoman.app %>/scripts/{,*/}*.js',
                '!<%= yeoman.app %>/scripts/vendor/*',
                'test/spec/{,*/}*.js'
            ]
        },
        autoprefixer: {
            options: {
                browsers: ['last 1 version']
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },
        // not used since Uglify task does concat,
        // but still available if needed
        /*concat: {
            dist: {}
        },*/
        'bower-install': {
            app: {
                html: '<%= yeoman.app %>/index.html',
                ignorePath: '<%= yeoman.app %>/'
            }
        },
        // not enabled since usemin task does concat and uglify
        // check index.html to edit your build targets
        // enable this task if you prefer defining your build targets here
        uglify: {
        //    dist: {}
          options: {
            report: 'min',
            mangle: false//{topLevel: false}
          }
        },
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
                        '<%= yeoman.dist %>/styles/fonts/{,*/}*.*'
                    ]
                }
            }
        },
        less: {
          dist: {
            files: {
              '<%= yeoman.dist %>/styles/style.css': '<%= yeoman.app %>/styles/style.less'
            }
          }
        },
        compass: {
          options: {
            sassDir: '<%= yeoman.app %>/styles',
            cssDir: '<%= yeoman.dist %>/styles',
            generatedImagesDir: '.tmp/images/generated',
            imagesDir: '<%= yeoman.app %>/images',
            javascriptsDir: '<%= yeoman.app %>/scripts',
            fontsDir: '<%= yeoman.app %>/styles/fonts',
            //importPath: '/bower_components',
            httpImagesPath: '/images',
            httpGeneratedImagesPath: '/images/generated',
            httpFontsPath: '/styles/fonts',
            relativeAssets: false,
            noLineComments: true
          },
          dist: {
            options: {
              generatedImagesDir: '<%= yeoman.dist %>/images/generated'
            }
          },
          server: {
            options: {
              debugInfo: true
            }
          }
        },
        preprocess : {
          dev : {
            options : {
              context:{env:'development'}
            },
            files : {
              '<%= yeoman.dist %>/index.html':'<%= yeoman.app %>/index.html',
                '<%= yeoman.dist %>/templates/sessionlist.html':'<%= yeoman.app %>/templates/sessionlist.html',
                '<%= yeoman.dist %>/templates/*.html':'<%= yeoman.app %>/templates/*.html',
            }
          },
          dist : {
            options : {
              context:{env:'production'}
            },
            files : {
                '<%= yeoman.dist %>/index.html':'<%= yeoman.app %>/index.html',
                '<%= yeoman.dist %>/templates/sessionlist.html':'<%= yeoman.app %>/templates/sessionlist.html',
                '<%= yeoman.dist %>/templates/results.html':'<%= yeoman.app %>/templates/results.html',
                '<%= yeoman.dist %>/templates/signin.html':'<%= yeoman.app %>/templates/signin.html'
            }
          }
        },
        useminPrepare: {
            options: {
                dest: '<%= yeoman.dist %>'
            },
            html: '<%= yeoman.app %>/index.html'
        },
        usemin: {
            options: {
              dirs: ['<%= yeoman.dist %>']
            },
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
            //js: ['<%= yeoman.app %>/{,*/}*.js']
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        cssmin: {
            // This task is pre-configured if you do not wish to use Usemin
            // blocks for your CSS. By default, the Usemin block from your
            // `index.html` will take care of minification, e.g.
            //
            //     <!-- build:css({.tmp,app}) styles/main.css -->
            //
            // dist: {
            //     files: {
            //         '<%= yeoman.dist %>/styles/main.css': [
            //             '.tmp/styles/{,*/}*.css',
            //             '<%= yeoman.app %>/styles/{,*/}*.css'
            //         ]
            //     }
            // }
        },
        htmlmin: {
            dist: {
                options: {
                    removeComments: true,
                    // https://github.com/yeoman/grunt-usemin/issues/44
                    collapseWhitespace: true,
                    collapseBooleanAttributes: true,
                    //removeAttributeQuotes: true,
                    removeRedundantAttributes: true,
                    //useShortDoctype: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true
                },
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.dist %>',
                    src: '*.html',
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'images/{,*/}*.{webp,gif,png,jpeg,jpg}',
                        //'styles/fonts/{,*/}*.*',
                        //'bower_components/sass-bootstrap/fonts/*.*',
                        //'styles/{,*/}*.css',
                        'templates/{,*/}*.html'
                    ]
                }]
            },
            //styles: {
            //    expand: true,
            //    dot: true,
            //    cwd: '<%= yeoman.app %>/styles',
            //    dest: '.tmp/styles/',
            //    src: '{,*/}*.css'
            //}
        },
        modernizr: {
            devFile: '<%= yeoman.app %>/bower_components/modernizr/modernizr.js',
            outputFile: '<%= yeoman.dist %>/bower_components/modernizr/modernizr.js',
            files: [
                '<%= yeoman.dist %>/scripts/{,*/}*.js',
                '<%= yeoman.dist %>/styles/{,*/}*.css',
                '!<%= yeoman.dist %>/scripts/vendor/*'
            ],
            uglify: true
        },
        concurrent: {
            min: [
                //'copy:styles',
                //'imagemin',
                'svgmin',
            ]
        },
        jade: {
          compile: {
            options: {
                pretty: true
            },
            files: [
              {
                    expand: true,
                    cwd: '<%= yeoman.app %>',
                    src: '**/*.jade', //['{,*/}*.jade','templates/{,*/}*.jade'],
                    dest: 'release',
                    ext: '.html'
                }
            ]
        }
    }
    });

    grunt.registerTask('server', function (target) {
        //if (target === 'dist') {
        //    return grunt.task.run(['build', 'connect:dist:keepalive']);
        //}

        grunt.task.run([
            'build',
            'configureProxies',
            //'connect:livereload',
            'watch'
        ]);
    });

    grunt.registerTask('build', [
        'clean:dist',
        'preprocess:dist', // generate html
        'compass:dist',
        //'less:dist',
        'concurrent:min', // copy and minify assets
        //'autoprefixer', // autoprefix css to .tmp
        'jade',
        //'useminPrepare', // prepare usemin tasks
        //'concat',
        //'cssmin', // minify css from .tmp to release
        //'uglify',
        /*'modernizr',*/
        'copy:dist',
        /*'rev',*/
        //'usemin', // update usemin html references
        //'htmlmin' // minify html in place
    ]);

    grunt.registerTask('default', [
        'jshint',
        'build'
    ]);

    grunt.registerTask('heroku', 'grunt server');
};
<|endoftext|>


<commit_before>// Default modules to build
var defaults = [
    'utils',
    'globals',
    'aes-cbc',
    'aes-gcm',
    'sha1',
    'sha256',
    'hmac-sha1',
    'hmac-sha256',
    'pbkdf2-hmac-sha1',
    'pbkdf2-hmac-sha256',
    'rng',
    'bn',
    'rsa-pkcs1',
    'rng-globals',
    'origin',
];

// Supported browsers
var browsers = [
    // Latest browsers
    {
        browserName: 'Firefox',
    },
    {
        browserName: 'Chrome',
    },
    {
        browserName: 'Internet Explorer',
    },
    {
        browserName: 'Safari',
        platform: 'OS X 10.11',
    },
    // Legacy browsers
    {
        browserName: 'Firefox',
        version: '22',
    },
    {
        browserName: 'Internet Explorer',
        version: '10',
    },
    {
        browserName: 'Opera',
        version: '12',
    },
    {
        browserName: 'Safari',
        version: '5.1',
    },
];

// Grunt setup
module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-qunit');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.loadNpmTasks('grunt-rollup');

    // Finally, configure
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        rollup: {
            options: {
                format: 'umd',
                sourceMap: true,
                banner: "/*! asmCrypto<%= pkg.version && ' v'+pkg.version %>, (c) 2013 <%= pkg.author.name %>, opensource.org/licenses/<%= pkg.license %> */",
            },
            default: {
                options: {
                    moduleName: 'asmCrypto',
                },
                files: {
                    'asmcrypto.js': './src/entry-default.js'
                }
            },
            all: {
                options: {
                    moduleName: 'asmCrypto',
                },
                files: {
                    'asmcrypto.js': './src/entry-export_all.js'
                }
            },
            test_AES_ASM: {
                options: {
                    moduleName: 'AES_asm',
                },
                files: {
                    'test/aes.asm.js': './src/aes/aes.asm.js'
                }
            },
        },

        uglify: {
            options: {
                mangle: {},
                compress: {},
                sourceMap: true,
                sourceMapIn: 'asmcrypto.js.map',
                sourceMapIncludeSources: true,
                screwIE8: true,
                banner: "/*! asmCrypto<%= pkg.version && ' v'+pkg.version %>, (c) 2013 <%= pkg.author.name %>, opensource.org/licenses/<%= pkg.license %> */"
            },
            all: {
                files: {
                    'asmcrypto.min.js': 'asmcrypto.js'
                }
            }
        },

        jsdoc: {
            all: {
                src: ['src/**/*.js', 'README.md'],
                options: {
                    destination: 'doc'
                }
            }
        },

        qunit: {
            all: {
                options: {
                    timeout: 120000,
                    urls: ['http://localhost:9999/index.html']
                }
            }
        },

        connect: {
            all: {
                options: {
                    hostname: 'localhost',
                    port: 9999,
                    base: ['test', '.'],
                    directory: 'test'
                }
            }
        },

        watch: {
            all: {
                files: 'src/**/*.js',
                tasks: ['rollup:all', 'rollup:test_AES_ASM']
            }
        },

        clean: [
            'asmcrypto.js',
            'asmcrypto.js.map',
            'test/aes.asm.js',
            'test/aes.asm.js.map',
            'doc/'
        ]
    });

    grunt.registerTask('default', ['rollup:default', 'rollup:test_AES_ASM', 'uglify']);
    grunt.registerTask('devel', ['rollup:all', 'rollup:test_AES_ASM', 'connect', 'watch']);
    grunt.registerTask('test', ['connect', 'qunit']);
};
<commit_msg>Remove unused config<commit_after>// Grunt setup
module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-qunit');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-jsdoc');
    grunt.loadNpmTasks('grunt-rollup');

    // Finally, configure
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        rollup: {
            options: {
                format: 'umd',
                sourceMap: true,
                banner: "/*! asmCrypto<%= pkg.version && ' v'+pkg.version %>, (c) 2013 <%= pkg.author.name %>, opensource.org/licenses/<%= pkg.license %> */",
            },
            default: {
                options: {
                    moduleName: 'asmCrypto',
                },
                files: {
                    'asmcrypto.js': './src/entry-default.js'
                }
            },
            all: {
                options: {
                    moduleName: 'asmCrypto',
                },
                files: {
                    'asmcrypto.js': './src/entry-export_all.js'
                }
            },
            test_AES_ASM: {
                options: {
                    moduleName: 'AES_asm',
                },
                files: {
                    'test/aes.asm.js': './src/aes/aes.asm.js'
                }
            },
        },

        uglify: {
            options: {
                mangle: {},
                compress: {},
                sourceMap: true,
                sourceMapIn: 'asmcrypto.js.map',
                sourceMapIncludeSources: true,
                screwIE8: true,
                banner: "/*! asmCrypto<%= pkg.version && ' v'+pkg.version %>, (c) 2013 <%= pkg.author.name %>, opensource.org/licenses/<%= pkg.license %> */"
            },
            all: {
                files: {
                    'asmcrypto.min.js': 'asmcrypto.js'
                }
            }
        },

        jsdoc: {
            all: {
                src: ['src/**/*.js', 'README.md'],
                options: {
                    destination: 'doc'
                }
            }
        },

        qunit: {
            all: {
                options: {
                    timeout: 120000,
                    urls: ['http://localhost:9999/index.html']
                }
            }
        },

        connect: {
            all: {
                options: {
                    hostname: 'localhost',
                    port: 9999,
                    base: ['test', '.'],
                    directory: 'test'
                }
            }
        },

        watch: {
            all: {
                files: 'src/**/*.js',
                tasks: ['rollup:all', 'rollup:test_AES_ASM']
            }
        },

        clean: [
            'asmcrypto.js',
            'asmcrypto.js.map',
            'test/aes.asm.js',
            'test/aes.asm.js.map',
            'doc/'
        ]
    });

    grunt.registerTask('default', ['rollup:default', 'rollup:test_AES_ASM', 'uglify']);
    grunt.registerTask('devel', ['rollup:all', 'rollup:test_AES_ASM', 'connect', 'watch']);
    grunt.registerTask('test', ['connect', 'qunit']);
};
<|endoftext|>


<commit_before>

module.exports = function(grunt) {

  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-conventional-changelog');
  grunt.loadNpmTasks('grunt-karma');
  grunt.loadNpmTasks('grunt-ngdocs');

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    dist: 'dist',
    sources: 'src/**/*.js',
    tests: 'test/**/*.js',
    ngdocFiles: 'src/**/*.ngdoc',
    clean: {
      dist: ['<%= dist %>']
    },
    jshint: {
      files: ['Gruntfile.js','<%= sources %>','<%= tests %>'],
      options: {
        jshintrc: '.jshintrc'
      }
    },
    karma: {
      options: {
        configFile: 'karma.conf.js',
        singleRun: true
      },
      unit: {},
      travis: {
        browsers: ['Firefox', 'PhantomJS']
      },
      watch: {
        singleRun: false
      }
    },
    uglify: {
      options: {
        banner: '/** Componitor v. <%= pkg.version %> */\n'
      },
      dist: {
        options: {
          mangle: false,
          compress: false,
          beautify: true,
          wrap: true
        },
        src: ['<%= sources %>'],
        dest: '<%= dist %>/componitor.js'
      },
      minify: {
        options: {
          sourceMap: true
        },
        src: ['<%= uglify.dist.dest %>'],
        dest: '<%= dist %>/componitor.min.js'
      }
    },
    ngdocs: {
      options: {
        dest: '<%= dist %>/docs',
        scripts: [
          'angular.js',
          '<%= uglify.dist.dest %>'
        ],
        title: 'componitor',
        startPage: 'api/componitor',
        titleLink: 'https://github.com/NitorCreations/componitor',
        html5Mode: false
      },
      api: {
        src: ['<%= sources %>', '<%= ngdocFiles %>'],
        title: 'API Documentation'
      }
    },
    watch: {
      ngdocs: {
        files: ['Gruntfile.js', '<%= sources %>', '<%= ngdocFiles %>'],
        tasks: ['after-test']
      }
    }
  });

  grunt.registerTask('before-test', ['enforce', 'jshint']);
  grunt.registerTask('test', ['karma:unit']);
  grunt.registerTask('after-test', ['build']);
  grunt.registerTask('build', ['clean:dist', 'uglify', 'ngdocs:api']);
  grunt.registerTask('default', ['before-test','test','after-test']);

  grunt.registerTask('travis', ['before-test', 'karma:travis']);

  // Loaned from Angular-ui bootstrap
  grunt.registerTask('enforce', 'Install commit message enforce script if it doesn\'t exist', function() {
    if (!grunt.file.exists('.git/hooks/commit-msg')) {
      grunt.file.copy('misc/validate-commit-msg.js', '.git/hooks/commit-msg');
      require('fs').chmodSync('.git/hooks/commit-msg', '0755');
    }
  });

};
<commit_msg>chore(docs): change title link<commit_after>
module.exports = function(grunt) {

  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-conventional-changelog');
  grunt.loadNpmTasks('grunt-karma');
  grunt.loadNpmTasks('grunt-ngdocs');

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    dist: 'dist',
    sources: 'src/**/*.js',
    tests: 'test/**/*.js',
    ngdocFiles: 'src/**/*.ngdoc',
    clean: {
      dist: ['<%= dist %>']
    },
    jshint: {
      files: ['Gruntfile.js','<%= sources %>','<%= tests %>'],
      options: {
        jshintrc: '.jshintrc'
      }
    },
    karma: {
      options: {
        configFile: 'karma.conf.js',
        singleRun: true
      },
      unit: {},
      travis: {
        browsers: ['Firefox', 'PhantomJS']
      },
      watch: {
        singleRun: false
      }
    },
    uglify: {
      options: {
        banner: '/** Componitor v. <%= pkg.version %> */\n'
      },
      dist: {
        options: {
          mangle: false,
          compress: false,
          beautify: true,
          wrap: true
        },
        src: ['<%= sources %>'],
        dest: '<%= dist %>/componitor.js'
      },
      minify: {
        options: {
          sourceMap: true
        },
        src: ['<%= uglify.dist.dest %>'],
        dest: '<%= dist %>/componitor.min.js'
      }
    },
    ngdocs: {
      options: {
        dest: '<%= dist %>/docs',
        scripts: [
          'angular.js',
          '<%= uglify.dist.dest %>'
        ],
        title: 'componitor',
        startPage: 'api/componitor',
        titleLink: 'https://nitorcreations.github.io/componitor',
        html5Mode: false
      },
      api: {
        src: ['<%= sources %>', '<%= ngdocFiles %>'],
        title: 'API Documentation'
      }
    },
    watch: {
      ngdocs: {
        files: ['Gruntfile.js', '<%= sources %>', '<%= ngdocFiles %>'],
        tasks: ['after-test']
      }
    }
  });

  grunt.registerTask('before-test', ['enforce', 'jshint']);
  grunt.registerTask('test', ['karma:unit']);
  grunt.registerTask('after-test', ['build']);
  grunt.registerTask('build', ['clean:dist', 'uglify', 'ngdocs:api']);
  grunt.registerTask('default', ['before-test','test','after-test']);

  grunt.registerTask('travis', ['before-test', 'karma:travis']);

  // Loaned from Angular-ui bootstrap
  grunt.registerTask('enforce', 'Install commit message enforce script if it doesn\'t exist', function() {
    if (!grunt.file.exists('.git/hooks/commit-msg')) {
      grunt.file.copy('misc/validate-commit-msg.js', '.git/hooks/commit-msg');
      require('fs').chmodSync('.git/hooks/commit-msg', '0755');
    }
  });

};
<|endoftext|>


<commit_before>"use strict";

module.exports = function(arr) {
  return arr.length > 0 ? arr[arr.length - 1] : null;
};
<commit_msg>Simplify `getLast` (#7267)<commit_after>"use strict";

module.exports = arr => arr[arr.length - 1];
<|endoftext|>


<commit_before>/*
 * holonomy-website
 * https://github.com/holonomy/website
 * licensed under the AGPLv3 license.
 */

'use strict';

var _ = require('lodash');

var banner = '/* <%= pkg.name %> <%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd") %> */\n';

module.exports = function(grunt) {

  // project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    // lint JavaScript
    jshint: {
      all: ['Gruntfile.js', 'helpers/*.js'],
      options: {
        jshintrc: '.jshintrc',
      },
    },

    // build HTML from templates and data
    assemble: {
      options: {
        pkg: '<%= pkg %>',
        flatten: true,
        assets: 'static/assets',
        partials: ['templates/includes/*.hbs'],
        helpers: ['templates/helpers/*.js'],
        layout: 'templates/layouts/index.hbs',
        data: ['data/*.{json,yml}'],
      },
      build: {
        files: {'build/': ['templates/*.hbs']},
      },
    },

    // compile LESS to CSS
    less: {
      options: {
        banner: banner,
        paths: [
          'node_modules/bootstrap/less',
          'styles',
        ],
      },
      develop: {
        files: {
          'build/index.css': 'styles/index.less',
        },
      },
      deploy: {
        options: {
          cleancss: true,
          report: 'gzip',
          sourceMap: true,
        },
        files: {
          'build/index.css': 'styles/index.less',
        },
      },
    },

    // before generating any new files,
    // remove any previously-created files.
    clean: {
      build: ['build/**/*']
    },

    connect: {
      options: {
        port: 5000,
        base: 'build',
      },
      develop: {
        options: {
          livereload: true,
        },
      },
      default: {
        options: {
          keepalive: true,
        },
      },
    },

    watch: {
      livereload: {
        options: {
          livereload: true,
        },
        files: ['build/**/*'],
      },
      all: {
        files: ['<%= jshint.all %>'],
        tasks: ['jshint'],
      },
      grunt: {
        files: ['Gruntfile.js'],
        tasks: ['clean', 'readme', 'assemble', 'less:develop'],
      },
      less: {
        files: _.map(grunt.config.get('less.options.paths'),
          function (p) { return p + '/*.less'; }
        ),
        tasks: ['less'],
      },
      assemble: {
        files: ['README.md', 'templates/**/*.hbs', 'templates/helpers/*.js'],
        tasks: ['assemble'],
      },
      readme: {
        files: ['docs/README.tmpl.md'],
        tasks: ['readme'],
      },
    },

    compress: {
      deploy: {
        options: {
          mode: 'gzip',
        },
        files: [{
          expand: true,
          cwd: 'build/',
          src: ['index.css'],
          dest: 'build/,
        }],
      },

    hashres: {
      deploy: {
        src: [
          'build/index.css'
        ],
        dest: 'build/**/*.html',
      },
    },

    'gh-pages': {
      deploy: {
        options: {
          base: 'build',
          repo: 'git@github.com:holonomy/holonomy.github.io',
          branch: 'master',
          message: banner,
        },
        src: ['**/*'],
      },
    },
  });

  // load npm plugins to provide necessary tasks.
  require('load-grunt-tasks')(grunt, {
    pattern: ['grunt-*', 'assemble*'],
  });

  // register grunt tasks
  grunt.registerTask('develop', ['clean', 'jshint', 'readme', 'assemble', 'less:develop', 'connect:develop', 'watch']);
  grunt.registerTask('deploy', ['clean', 'jshint', 'readme', 'assemble', 'less:deploy', 'hashres', 'gh-pages']);

  // default task to be run.
  grunt.registerTask('default', ['clean', 'readme', 'assemble', 'less:deploy', 'connect:default']);
};
<commit_msg>s/gzip/min in less report<commit_after>/*
 * holonomy-website
 * https://github.com/holonomy/website
 * licensed under the AGPLv3 license.
 */

'use strict';

var _ = require('lodash');

var banner = '/* <%= pkg.name %> <%= pkg.version %> <%= grunt.template.today("yyyy-mm-dd") %> */\n';

module.exports = function(grunt) {

  // project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    // lint JavaScript
    jshint: {
      all: ['Gruntfile.js', 'helpers/*.js'],
      options: {
        jshintrc: '.jshintrc',
      },
    },

    // build HTML from templates and data
    assemble: {
      options: {
        pkg: '<%= pkg %>',
        flatten: true,
        assets: 'static/assets',
        partials: ['templates/includes/*.hbs'],
        helpers: ['templates/helpers/*.js'],
        layout: 'templates/layouts/index.hbs',
        data: ['data/*.{json,yml}'],
      },
      build: {
        files: {'build/': ['templates/*.hbs']},
      },
    },

    // compile LESS to CSS
    less: {
      options: {
        banner: banner,
        paths: [
          'node_modules/bootstrap/less',
          'styles',
        ],
      },
      develop: {
        files: {
          'build/index.css': 'styles/index.less',
        },
      },
      deploy: {
        options: {
          cleancss: true,
          report: 'min',
          sourceMap: true,
        },
        files: {
          'build/index.css': 'styles/index.less',
        },
      },
    },

    // before generating any new files,
    // remove any previously-created files.
    clean: {
      build: ['build/**/*']
    },

    connect: {
      options: {
        port: 5000,
        base: 'build',
      },
      develop: {
        options: {
          livereload: true,
        },
      },
      default: {
        options: {
          keepalive: true,
        },
      },
    },

    watch: {
      livereload: {
        options: {
          livereload: true,
        },
        files: ['build/**/*'],
      },
      all: {
        files: ['<%= jshint.all %>'],
        tasks: ['jshint'],
      },
      grunt: {
        files: ['Gruntfile.js'],
        tasks: ['clean', 'readme', 'assemble', 'less:develop'],
      },
      less: {
        files: _.map(grunt.config.get('less.options.paths'),
          function (p) { return p + '/*.less'; }
        ),
        tasks: ['less'],
      },
      assemble: {
        files: ['README.md', 'templates/**/*.hbs', 'templates/helpers/*.js'],
        tasks: ['assemble'],
      },
      readme: {
        files: ['docs/README.tmpl.md'],
        tasks: ['readme'],
      },
    },

    compress: {
      deploy: {
        options: {
          mode: 'gzip',
        },
        files: [{
          expand: true,
          cwd: 'build/',
          src: ['index.css'],
          dest: 'build/,
        }],
      },

    hashres: {
      deploy: {
        src: [
          'build/index.css'
        ],
        dest: 'build/**/*.html',
      },
    },

    'gh-pages': {
      deploy: {
        options: {
          base: 'build',
          repo: 'git@github.com:holonomy/holonomy.github.io',
          branch: 'master',
          message: banner,
        },
        src: ['**/*'],
      },
    },
  });

  // load npm plugins to provide necessary tasks.
  require('load-grunt-tasks')(grunt, {
    pattern: ['grunt-*', 'assemble*'],
  });

  // register grunt tasks
  grunt.registerTask('develop', ['clean', 'jshint', 'readme', 'assemble', 'less:develop', 'connect:develop', 'watch']);
  grunt.registerTask('deploy', ['clean', 'jshint', 'readme', 'assemble', 'less:deploy', 'hashres', 'gh-pages']);

  // default task to be run.
  grunt.registerTask('default', ['clean', 'readme', 'assemble', 'less:deploy', 'connect:default']);
};
<|endoftext|>


<commit_before>$(document).ready(function(){
  var user1, user2, currentSession, startTime, endTime, currentInterval, totalTimeWorking, pairingDurationMs, timer;
  var sessions = [];

  $('button.duration-button').on('click', function(event){
    event.preventDefault();
    if($('input.duration').val() !== "" ){
      currentInterval = parseInt($('input.duration').val());
      pairingDurationMs = currentInterval * 60000;
    } else {
      alert("Please Enter A Real Number");
    }
  });

  $('button.start-pairing').on('click', function(){
    startPairing();
  });

  $('.pause_button').on('click', function(){
    if(timer){
      window.clearInterval(timer);
      timer = null;
    } else {
      startTimerCount();
    }
  });

  $('.exit_session').on('click', function(){
    if(currentSession.timeWorked === 0){
      endSession();
    }
    activeTime = 0;
    inActiveTime = 0;
    sessions.forEach(function(pairSession){
      activeTime += pairSession.timeWorked;
      inActiveTime += pairSession.timePaused;
    })
    sessions.push(TotalSessionInfo(activeTime, inActiveTime));
  })

  function PairingSession(user1, user2){
    this.drive = user1;
    this.navigate = user2;
    this.timeWorked = 0;
    this.timePaused = 0;
  }

  function TotalSessionInfo(activeTime, pauseTime){
    this.totalTime = pauseTime + activeTime;
    this.activeTime = activeTime;
    this.pauseTime = pauseTime;
  }

  function startPairing(){
    var date = new Date();
    startTime = date.getTime();
    currentSession = sessions.push(PairingSession(user1, user2));
    startTimerCount();
  }

  function startTimerCount(){
    timer = setInterval(function(){
      totalTimeWorking ++;
      currentInterval --;
      updateTimerCountdown();
      checkDuration();
    }, 60000);
  }

  function calculatePauseTime(){
    pause_time = endTime - startTime - totalTimeWorking;
    currentSession.timePaused = pause_time;
  }

  function stopTimer(){
    window.clearInterval(timer);
  }

  function checkDuration(){
    if(currentInterval === 0){
      currentInterval = pairingDurationMs / 60000;
      currentSession.timeWorked = 20;
      alert('Switch it ^');
    }
  }

  function updateTimerCountdown(){
    $('.countdown').val(currentInterval);
  }
});

<commit_msg>Add End Session Function to Calculate Pause Time<commit_after>$(document).ready(function(){
  var user1, user2, currentSession, startTime, endTime, currentInterval, totalTimeWorking, pairingDurationMs, timer;
  var sessions = [];

  $('button.duration-button').on('click', function(event){
    event.preventDefault();
    if($('input.duration').val() !== "" ){
      currentInterval = parseInt($('input.duration').val());
      pairingDurationMs = currentInterval * 60000;
    } else {
      alert("Please Enter A Real Number");
    }
  });

  $('button.start-pairing').on('click', function(){
    startPairing();
  });

  $('.pause_button').on('click', function(){
    if(timer){
      window.clearInterval(timer);
      timer = null;
    } else {
      startTimerCount();
    }
  });

  $('.exit_session').on('click', function(){
    if(currentSession.timeWorked === 0){
      endSession();
    }
    activeTime = 0;
    inActiveTime = 0;
    sessions.forEach(function(pairSession){
      activeTime += pairSession.timeWorked;
      inActiveTime += pairSession.timePaused;
    })
    sessions.push(TotalSessionInfo(activeTime, inActiveTime));
  })

  function PairingSession(user1, user2){
    this.drive = user1;
    this.navigate = user2;
    this.timeWorked = 0;
    this.timePaused = 0;
  }

  function TotalSessionInfo(activeTime, pauseTime){
    this.totalTime = pauseTime + activeTime;
    this.activeTime = activeTime;
    this.pauseTime = pauseTime;
  }

  function startPairing(){
    var date = new Date();
    startTime = date.getTime();
    currentSession = sessions.push(PairingSession(user1, user2));
    startTimerCount();
  }

  function startTimerCount(){
    timer = setInterval(function(){
      totalTimeWorking ++;
      currentInterval --;
      updateTimerCountdown();
      checkDuration();
    }, 60000);
  }

  function stopTimer(){
    window.clearInterval(timer);
  }

  function checkDuration(){
    if(currentInterval === 0){
      endSession();
      alert('Switch it ^');
      startPairing();
    }
  }

  function endSession(){
    var date = new Date();
    endTime = date.getTime();
    currentInterval = pairingDurationMs / 60000;
    currentSession.timeWorked = 20;
    currentSession.timePaused = startTime - endTime - currentInterval;
  }

  function updateTimerCountdown(){
    $('.countdown').val(currentInterval);
  }
});

<|endoftext|>