<commit_before>/*
 * Copyright 2012 The Toolkitchen Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

/**
 * This module contains the handlers for native platform events.
 * From here, the dispatcher is called to create unified pointer events.
 * Included are touch events (v1) and mouse events.
 */
(function(scope) {
  var dispatcher = scope.dispatcher;
  var pointermap = scope.pointermap;
  var touchMap = Array.prototype.map.call.bind(Array.prototype.map);
  // handler block for native touch events
  var touchEvents = {
    events: [
      'touchstart',
      'touchmove',
      'touchend',
      'touchcancel'
    ],
    firstTouch: null,
    isPrimaryTouch: function(inTouch) {
      return this.firstTouch === inTouch.identifier;
    },
    touchToPointer: function(inTouch) {
      var e = dispatcher.cloneEvent(inTouch);
      // Spec specifies that pointerId 1 is reserved for Mouse.
      // Touch identifiers can start at 0.
      // Add 2 to the touch identifier for compatibility.
      e.pointerId = inTouch.identifier + 2;
      e.target = this.findTarget(e);
      e.bubbles = true;
      e.cancelable = true;
      e.button = 0;
      e.buttons = 1;
      e.isPrimary = this.isPrimaryTouch(inTouch);
      e.pointerType = dispatcher.POINTER_TYPE_TOUCH;
      return e;
    },
    processTouches: function(inEvent, inFunction) {
      var tl = inEvent.changedTouches;
      var pointers = touchMap(tl, this.touchToPointer, this);
      pointers.forEach(inFunction, this);
    },
    findTarget: function(inEvent) {
      // TODO (dfreedman): support shadow.elementFromPoint here, when available
      return document.elementFromPoint(inEvent.clientX, inEvent.clientY);
    },
    touchstart: function(inEvent) {
      if (this.firstTouch === null) {
        this.firstTouch = inEvent.changedTouches[0].identifier;
      }
      this.processTouches(inEvent, this.overDown);
    },
    overDown: function(inPointer) {
      var p = pointermap.addPointer(inPointer.pointerId);
      dispatcher.over(inPointer);
      dispatcher.down(inPointer);
      p.out = inPointer;
    },
    touchmove: function(inEvent) {
      // must preventDefault first touchmove or document will scroll otherwise
      // Per Touch event spec section 5.6
      // http://www.w3.org/TR/touch-events/#the-touchmove-event
      inEvent.preventDefault();
      this.processTouches(inEvent, this.moveOverOut);
    },
    moveOverOut: function(inPointer) {
      var event = inPointer;
      var pointer = pointermap.getPointerById(event.pointerId);
      var outEvent = pointer.out;
      dispatcher.move(event);
      if (outEvent && outEvent.target !== event.target) {
        outEvent.relatedTarget = event.target;
        event.relatedTarget = outEvent.target;
        dispatcher.out(outEvent);
        dispatcher.over(event);
      }
      pointer.out = event;
    },
    touchend: function(inEvent) {
      this.processTouches(inEvent, this.upOut);
      var touch = inEvent.changedTouches[0];
      this.removeFirstTouch(touch);
    },
    upOut: function(inPointer) {
      dispatcher.up(inPointer);
      dispatcher.out(inPointer);
      pointermap.removePointer(inPointer.pointerId);
    },
    touchcancel: function(inEvent) {
      this.processTouches(inEvent, this.cancelOut);
      var touch = inEvent.changedTouches[0];
      this.removeFirstTouch(touch);
    },
    cancel: function(inPointer) {
      dispatcher.cancel(inPointer);
      dispatcher.out(inPointer);
      pointermap.removePointer(inPointer.pointerId);
    },
    removeFirstTouch: function(inTouch) {
      if (this.isPrimaryTouch(inTouch)) {
        this.firstTouch = null;
      }
    }
  };

  // handler block for native mouse events
  var mouseEvents = {
    // Mouse is required to have a pointerId of 1
    POINTER_ID: 1,
    events: [
      'mousedown',
      'mousemove',
      'mouseup',
      'mouseover',
      'mouseout'
    ],
    prepareEvent: function(inEvent) {
      var e = dispatcher.cloneEvent(inEvent);
      e.pointerId = this.POINTER_ID;
      e.isPrimary = true;
      e.pointerType = dispatcher.POINTER_TYPE_MOUSE;
      return e;
    },
    mousedown: function(inEvent) {
      if (pointermap.getPointerIndex(this.POINTER_ID) == -1) {
        var e = this.prepareEvent(inEvent);
        var p = pointermap.addPointer(this.POINTER_ID);
        p.button = inEvent.button;
        dispatcher.down(e);
      }
    },
    mousemove: function(inEvent) {
      var e = this.prepareEvent(inEvent);
      dispatcher.move(e);
    },
    mouseup: function(inEvent) {
      var p = pointermap.getPointerById(this.POINTER_ID);
      if (p && p.button === inEvent.button) {
        var e = this.prepareEvent(inEvent);
        dispatcher.up(e);
        pointermap.removePointer(this.POINTER_ID);
      }
    },
    mouseover: function(inEvent) {
      var e = this.prepareEvent(inEvent);
      dispatcher.over(e);
    },
    mouseout: function(inEvent) {
      var e = this.prepareEvent(inEvent);
      dispatcher.out(e);
    }
  };

  // only activate if this platform does not have pointer events
  if (window.navigator.pointerEnabled === undefined) {
    // We fork the initialization of dispatcher event listeners here because
    // current native touch event systems emulate mouse events. These
    // touch-emulated mouse events behave differently than normal mouse events.
    //
    // Touch-emulated mouse events will only occur if the target element has
    // either a native click handler, or the onclick attribute is set. In
    // addition, the touch-emulated mouse events fire only after the finger has
    // left the screen, negating any live-tracking ability a developer might want.
    //
    // The only way to disable mouse event emulation by native touch systems is to
    // preventDefault every touch event, which we feel is inelegant.
    //
    // Therefore we choose to only listen to native touch events if they exist.

    if ('ontouchstart' in window) {
      dispatcher.registerSource('touch', touchEvents);
    } else {
      dispatcher.registerSource('mouse', mouseEvents);
    }
    dispatcher.registerTarget(document);
    Object.defineProperty(window.navigator, 'pointerEnabled', {value: true, enumerable: true});
  }
})(window.__PointerEventShim__);
<commit_msg>Clean up primary pointer tracking for touch events<commit_after>/*
 * Copyright 2012 The Toolkitchen Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

/**
 * This module contains the handlers for native platform events.
 * From here, the dispatcher is called to create unified pointer events.
 * Included are touch events (v1) and mouse events.
 */
(function(scope) {
  var dispatcher = scope.dispatcher;
  var pointermap = scope.pointermap;
  var touchMap = Array.prototype.map.call.bind(Array.prototype.map);
  // handler block for native touch events
  var touchEvents = {
    events: [
      'touchstart',
      'touchmove',
      'touchend',
      'touchcancel'
    ],
    firstTouch: null,
    isPrimaryTouch: function(inTouch) {
      return this.firstTouch === inTouch.identifier;
    },
    removePrimaryTouch: function(inTouch) {
      if (this.isPrimaryTouch(inTouch)) {
        this.firstTouch = null;
      }
    },
    touchToPointer: function(inTouch) {
      var e = dispatcher.cloneEvent(inTouch);
      // Spec specifies that pointerId 1 is reserved for Mouse.
      // Touch identifiers can start at 0.
      // Add 2 to the touch identifier for compatibility.
      e.pointerId = inTouch.identifier + 2;
      e.target = this.findTarget(e);
      e.bubbles = true;
      e.cancelable = true;
      e.button = 0;
      e.buttons = 1;
      e.isPrimary = this.isPrimaryTouch(inTouch);
      e.pointerType = dispatcher.POINTER_TYPE_TOUCH;
      return e;
    },
    processTouches: function(inEvent, inFunction) {
      var tl = inEvent.changedTouches;
      var pointers = touchMap(tl, this.touchToPointer, this);
      pointers.forEach(inFunction, this);
    },
    findTarget: function(inEvent) {
      // TODO (dfreedman): support shadow.elementFromPoint here, when available
      return document.elementFromPoint(inEvent.clientX, inEvent.clientY);
    },
    touchstart: function(inEvent) {
      if (this.firstTouch === null) {
        this.firstTouch = inEvent.changedTouches[0].identifier;
      }
      this.processTouches(inEvent, this.overDown);
    },
    overDown: function(inPointer) {
      var p = pointermap.addPointer(inPointer.pointerId);
      dispatcher.over(inPointer);
      dispatcher.down(inPointer);
      p.out = inPointer;
    },
    touchmove: function(inEvent) {
      // must preventDefault first touchmove or document will scroll otherwise
      // Per Touch event spec section 5.6
      // http://www.w3.org/TR/touch-events/#the-touchmove-event
      inEvent.preventDefault();
      this.processTouches(inEvent, this.moveOverOut);
    },
    moveOverOut: function(inPointer) {
      var event = inPointer;
      var pointer = pointermap.getPointerById(event.pointerId);
      var outEvent = pointer.out;
      dispatcher.move(event);
      if (outEvent && outEvent.target !== event.target) {
        outEvent.relatedTarget = event.target;
        event.relatedTarget = outEvent.target;
        dispatcher.out(outEvent);
        dispatcher.over(event);
      }
      pointer.out = event;
    },
    touchend: function(inEvent) {
      this.processTouches(inEvent, this.upOut);
    },
    upOut: function(inPointer) {
      dispatcher.up(inPointer);
      dispatcher.out(inPointer);
      pointermap.removePointer(inPointer.pointerId);
      this.removePrimaryTouch(inPointer);
    },
    touchcancel: function(inEvent) {
      this.processTouches(inEvent, this.cancelOut);
    },
    cancelOut: function(inPointer) {
      dispatcher.cancel(inPointer);
      dispatcher.out(inPointer);
      pointermap.removePointer(inPointer.pointerId);
      this.removePrimaryTouch(inPointer);
    }
  };

  // handler block for native mouse events
  var mouseEvents = {
    // Mouse is required to have a pointerId of 1
    POINTER_ID: 1,
    events: [
      'mousedown',
      'mousemove',
      'mouseup',
      'mouseover',
      'mouseout'
    ],
    prepareEvent: function(inEvent) {
      var e = dispatcher.cloneEvent(inEvent);
      e.pointerId = this.POINTER_ID;
      e.isPrimary = true;
      e.pointerType = dispatcher.POINTER_TYPE_MOUSE;
      return e;
    },
    mousedown: function(inEvent) {
      if (pointermap.getPointerIndex(this.POINTER_ID) == -1) {
        var e = this.prepareEvent(inEvent);
        var p = pointermap.addPointer(this.POINTER_ID);
        p.button = inEvent.button;
        dispatcher.down(e);
      }
    },
    mousemove: function(inEvent) {
      var e = this.prepareEvent(inEvent);
      dispatcher.move(e);
    },
    mouseup: function(inEvent) {
      var p = pointermap.getPointerById(this.POINTER_ID);
      if (p && p.button === inEvent.button) {
        var e = this.prepareEvent(inEvent);
        dispatcher.up(e);
        pointermap.removePointer(this.POINTER_ID);
      }
    },
    mouseover: function(inEvent) {
      var e = this.prepareEvent(inEvent);
      dispatcher.over(e);
    },
    mouseout: function(inEvent) {
      var e = this.prepareEvent(inEvent);
      dispatcher.out(e);
    }
  };

  // only activate if this platform does not have pointer events
  if (window.navigator.pointerEnabled === undefined) {
    // We fork the initialization of dispatcher event listeners here because
    // current native touch event systems emulate mouse events. These
    // touch-emulated mouse events behave differently than normal mouse events.
    //
    // Touch-emulated mouse events will only occur if the target element has
    // either a native click handler, or the onclick attribute is set. In
    // addition, the touch-emulated mouse events fire only after the finger has
    // left the screen, negating any live-tracking ability a developer might want.
    //
    // The only way to disable mouse event emulation by native touch systems is to
    // preventDefault every touch event, which we feel is inelegant.
    //
    // Therefore we choose to only listen to native touch events if they exist.

    if ('ontouchstart' in window) {
      dispatcher.registerSource('touch', touchEvents);
    } else {
      dispatcher.registerSource('mouse', mouseEvents);
    }
    dispatcher.registerTarget(document);
    Object.defineProperty(window.navigator, 'pointerEnabled', {value: true, enumerable: true});
  }
})(window.__PointerEventShim__);
<|endoftext|>


<commit_before>function init() {

    angular.module('ngTick', [])
        .directive('clock', [
            '$filter', function ($filter) {

                return {
                    scope: {
                        format: '@',
                        handle: '@clock',
                        trigger: '='
                    },
                    link: function (scope, elem, attrs) {

                        if (scope.handle)
                            scope.$root[scope.handle] = scope;

                        var filter = $filter('date');

                        var start = new Date().getTime();

                        var timer = new Tock({
                            callback: function () {

                                var tick = timer.lap();

                                var date = new Date(start + tick);

                                scope.format ? elem.text(filter(new Date(date), scope.format)) : elem.text(new Date(date));
                            }
                        });

                        scope.start = function () {
                            if (!timer.go)
                                timer.start();
                        }

                        if (!scope.trigger)
                            scope.start();
                    }
                }
            }
        ])
        .directive('ticker', ['$filter', 'engine', function ($filter, engine) {
            return {
                scope: {
                    format: '@',
                    handle: '@ticker',
                    trigger: '='
                },
                link: function (scope, elem, attrs) {

                    var relative = $filter('relativeTime');

                    var filter = $filter('date');

                    var timer = engine.tick({
                        onTick: function (ms) {

                            var time = relative(ms);
                            time = scope.format ? filter(time, scope.format) : time;
                            elem.text(time);
                        },
                        onLap: function (lap, ms) {
                            if (scope.handle)
                                scope.$root[scope.handle].$emit(scope.handle + ':lap', { 'lap': lap, 'elapsed': ms });
                        }
                    });

                    if (scope.handle)
                        scope.$root[scope.handle] = scope;

                    scope.start = function () {

                        if (scope.handle && !timer.ticking)
                            scope.$root[scope.handle].$emit(scope.handle + ':start');

                        timer.start();
                    }

                    scope.stop = function () {

                        if (scope.handle && timer.ticking)
                            scope.$root[scope.handle].$emit(scope.handle + ':stop');

                        timer.stop();
                    }

                    scope.reset = function () {

                        if (scope.handle)
                            scope.$root[scope.handle].$emit(scope.handle + ':reset');

                        timer.reset();
                    }

                    scope.lap = function () {
                        timer.lap();
                    }

                    scope.$on('$destroy', function () {

                        if (scope.handle && timer.ticking)
                            scope.$root[scope.handle].$emit(scope.handle + ':stop');

                        timer.stop();
                    });

                    if (!scope.trigger)
                        scope.start();
                }
            }
        }
        ])
        .directive('countdown', [
            '$filter', 'tickHelper', '$timeout', 'engine', function ($filter, tickHelper, $timeout, engine) {
                return {
                    scope: {
                        format: '@',
                        handle: '@countdown',
                        duration: '@',
                        trigger: '=',
                        interval: '='
                    },
                    link: function (scope, elem, attrs) {

                        var countdown = engine.countdown();

                        console.log(countdown);

                        if (!scope.trigger)
                            scope.start();

                        //console.log('countdown');

                        //if (scope.handle)
                        //    scope.$root[scope.handle] = scope;

                        //var filter = $filter('date');

                        //var relative = $filter('relativeTime');

                        //var moment;

                        //var duration = tickHelper.getDuration(JSON.parse(scope.duration.replace(/'/g, '"'))) || 0;

                        //var timer = new Tock({
                        //    countdown: true,
                        //    interval: scope.interval || 1,
                        //    callback: function () {

                        //        moment = timer.lap();

                        //        var round = scope.interval ? (Math.ceil(timer.lap() / scope.interval) * scope.interval) >= 0 ? Math.ceil(timer.lap() / scope.interval) * scope.interval : 0 : timer.lap();

                        //        var time = relative(round);

                        //        time = filter(time, scope.format);

                        //        elem.text(time);
                        //    },
                        //    complete: function () {
                        //        if (scope.handle)
                        //            scope.$root[scope.handle].$emit(scope.handle + ':end');

                        //        moment = null;
                        //    }
                        //});

                        //scope.reset = function () {
                        //    timer.stop();
                        //    timer.reset();
                        //    timer.start(duration);

                        //    if (scope.handle)
                        //        scope.$root[scope.handle].$emit(scope.handle + ':reset');
                        //}

                        //scope.resume = function () {
                        //    if (!timer.go && moment)
                        //        timer.start(moment);

                        //    if (scope.handle)
                        //        scope.$root[scope.handle].$emit(scope.handle + ':resume');
                        //}

                        //scope.start = function () {
                        //    if (!timer.go)
                        //        timer.start(duration);

                        //    if (scope.handle)
                        //        scope.$root[scope.handle].$emit(scope.handle + ':start');
                        //}

                        //scope.stop = function () {
                        //    if (timer.go)
                        //        timer.stop();

                        //    if (scope.handle)
                        //        scope.$root[scope.handle].$emit(scope.handle + ':stop');
                        //}

                        //scope.$on('$destroy', function () {
                        //    timer.stop();
                        //});
                    }
                }
            }
        ])
        .directive('binaryClock', [
            '$timeout', function ($timeout) {
                return {
                    restrict: 'E',
                    template:
                        '
' +
                            '
' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '
' +
                            '
',
                    link: function (scope, elem, attrs) {

                        $timeout(function () {
                            function setBit(field, value) {

                                var ele = document.getElementById(field);

                                if (ele.classList.contains('blank'))
                                    return false;

                                if (value)
                                    ele.setAttribute('class', 'bit on');
                                else
                                    ele.setAttribute('class', 'bit off');
                            }

                            function setBCDs(field, value) {

                                setBit(field + '-forties', value >= 40);

                                if (value >= 40) {
                                    value -= 40;
                                }
                                setBit(field + '-twenties', value >= 20);
                                if (value >= 20) {
                                    value -= 20;
                                }
                                setBit(field + '-tens', value >= 10);
                                if (value >= 10) {
                                    value -= 10;
                                }
                                setBit(field + '-eights', value >= 8);
                                if (value >= 8) {
                                    value -= 8;
                                }
                                setBit(field + '-fours', value >= 4);
                                if (value >= 4) {
                                    value -= 4;
                                }
                                setBit(field + '-twos', value >= 2);
                                if (value >= 2) {
                                    value -= 2;
                                }
                                setBit(field + '-ones', value >= 1);
                            }

                            function tick() {
                                var now = new Date();
                                setBCDs('hours', now.getHours());
                                setBCDs('minutes', now.getMinutes());
                                setBCDs('seconds', now.getSeconds());
                            }

                            tick();

                            var timer = new Tock({
                                countdown: true,
                                interval: 100,
                                callback: function () {
                                    tick();
                                }
                            });

                            timer.start(Date.now());

                            //self.setInterval(tick, 500); // - mayhaps?
                        });
                    }
                }
            }
        ])
        .factory('tickHelper', [
            function () {

                function getDuration(chunk) {

                    var now = new Date();

                    now.setMilliseconds(now.getMilliseconds() + (chunk.ms ? chunk.ms : 0));
                    now.setSeconds(now.getSeconds() + (chunk.s || 0));
                    now.setMinutes(now.getMinutes() + (chunk.m || 0));
                    now.setHours(now.getHours() + (chunk.h || 0));

                    return Math.abs(now - new Date());
                }

                return {
                    'getDuration': getDuration
                }
            }
        ])
        .filter('relativeTime', [
            function () {
                return function (ms) {
                    return new Date(1970, 0, 1, 0).setMilliseconds(ms);
                };
            }
        ])
        .factory('engine', function () {

            function instance(options) {

                options = options || {};

                var self = this;
                var start;
                var time;
                var interval = options.interval || 10;
                var thread;
                var phantom = 0;
                var lap = false;
                var circuit = 0;
                var lapped = 0;

                self.ticking = false;

                function tick() {

                    if (self.ticking) {
                        time += interval;
                        lapped += interval;

                        if (lap) {
                            lap = false;
                            self.onLap(circuit, lapped);
                            lapped = 0;
                        }

                        self.onTick(time);
                        var diff = (Date.now() - start) - time;
                        setTimeout(tick, (interval - diff));
                    }
                }

                this.onTick = options.onTick || function () { };

                this.onLap = options.onLap || function () { };

                this.reset = function () {
                    clearTimeout(thread);
                    self.ticking = false;
                    phantom = 0;
                    lapped = 0;
                    circuit = 0;
                    self.onTick(0);
                    return this;
                }

                this.start = function () {
                    if (!self.ticking) {
                        time = phantom > 0 ? phantom : 0;
                        start = Date.now() - time;
                        self.ticking = true;
                        thread = setTimeout(tick, interval);
                    }

                    return this;
                }

                this.stop = function () {
                    if (self.ticking) {
                        phantom = time;
                        self.ticking = false;
                        lapped = 0;
                    }

                    return this;
                }

                this.lap = function () {
                    if (self.ticking) {
                        lap = true;
                        circuit += 1;
                    }

                    return this;
                }
            }

            return {
                'tick': function (options) {
                    return new instance(options);
                }
            }
        });
}

(function () {
    if (typeof define === 'function' && define.amd) { // RequireJS aware
        define(['angular'], function () {
            init();
        });
    } else {
        init();
    }
}());<commit_msg>creating new countdown object<commit_after>function init() {

    angular.module('ngTick', [])
        .directive('clock', [
            '$filter', function ($filter) {

                return {
                    scope: {
                        format: '@',
                        handle: '@clock',
                        trigger: '='
                    },
                    link: function (scope, elem, attrs) {

                        if (scope.handle)
                            scope.$root[scope.handle] = scope;

                        var filter = $filter('date');

                        var start = new Date().getTime();

                        var timer = new Tock({
                            callback: function () {

                                var tick = timer.lap();

                                var date = new Date(start + tick);

                                scope.format ? elem.text(filter(new Date(date), scope.format)) : elem.text(new Date(date));
                            }
                        });

                        scope.start = function () {
                            if (!timer.go)
                                timer.start();
                        }

                        if (!scope.trigger)
                            scope.start();
                    }
                }
            }
        ])
        .directive('ticker', ['$filter', 'engine', function ($filter, engine) {
            return {
                scope: {
                    format: '@',
                    handle: '@ticker',
                    trigger: '='
                },
                link: function (scope, elem, attrs) {

                    var relative = $filter('relativeTime');

                    var filter = $filter('date');

                    var timer = engine.timer({
                        onTick: function (ms) {

                            var time = relative(ms);
                            time = scope.format ? filter(time, scope.format) : time;
                            elem.text(time);
                        },
                        onLap: function (lap, ms) {
                            if (scope.handle)
                                scope.$root[scope.handle].$emit(scope.handle + ':lap', { 'lap': lap, 'elapsed': ms });
                        }
                    });

                    console.log(timer);

                    if (scope.handle)
                        scope.$root[scope.handle] = scope;

                    scope.start = function () {

                        if (scope.handle && !timer.ticking)
                            scope.$root[scope.handle].$emit(scope.handle + ':start');

                        timer.start();
                    }

                    scope.stop = function () {

                        if (scope.handle && timer.ticking)
                            scope.$root[scope.handle].$emit(scope.handle + ':stop');

                        timer.stop();
                    }

                    scope.reset = function () {

                        if (scope.handle)
                            scope.$root[scope.handle].$emit(scope.handle + ':reset');

                        timer.reset();
                    }

                    scope.lap = function () {
                        timer.lap();
                    }

                    scope.$on('$destroy', function () {

                        if (scope.handle && timer.ticking)
                            scope.$root[scope.handle].$emit(scope.handle + ':stop');

                        timer.stop();
                    });

                    if (!scope.trigger)
                        scope.start();
                }
            }
        }
        ])
        .directive('countdown', [
            '$filter', 'tickHelper', '$timeout', 'engine', function ($filter, tickHelper, $timeout, engine) {
                return {
                    scope: {
                        format: '@',
                        handle: '@countdown',
                        duration: '@',
                        trigger: '=',
                        interval: '='
                    },
                    link: function (scope, elem, attrs) {

                        var countdown = engine.countdown();

                        console.log(countdown);

                        if (!scope.trigger)
                            scope.start();

                        //console.log('countdown');

                        //if (scope.handle)
                        //    scope.$root[scope.handle] = scope;

                        //var filter = $filter('date');

                        //var relative = $filter('relativeTime');

                        //var moment;

                        //var duration = tickHelper.getDuration(JSON.parse(scope.duration.replace(/'/g, '"'))) || 0;

                        //var timer = new Tock({
                        //    countdown: true,
                        //    interval: scope.interval || 1,
                        //    callback: function () {

                        //        moment = timer.lap();

                        //        var round = scope.interval ? (Math.ceil(timer.lap() / scope.interval) * scope.interval) >= 0 ? Math.ceil(timer.lap() / scope.interval) * scope.interval : 0 : timer.lap();

                        //        var time = relative(round);

                        //        time = filter(time, scope.format);

                        //        elem.text(time);
                        //    },
                        //    complete: function () {
                        //        if (scope.handle)
                        //            scope.$root[scope.handle].$emit(scope.handle + ':end');

                        //        moment = null;
                        //    }
                        //});

                        //scope.reset = function () {
                        //    timer.stop();
                        //    timer.reset();
                        //    timer.start(duration);

                        //    if (scope.handle)
                        //        scope.$root[scope.handle].$emit(scope.handle + ':reset');
                        //}

                        //scope.resume = function () {
                        //    if (!timer.go && moment)
                        //        timer.start(moment);

                        //    if (scope.handle)
                        //        scope.$root[scope.handle].$emit(scope.handle + ':resume');
                        //}

                        //scope.start = function () {
                        //    if (!timer.go)
                        //        timer.start(duration);

                        //    if (scope.handle)
                        //        scope.$root[scope.handle].$emit(scope.handle + ':start');
                        //}

                        //scope.stop = function () {
                        //    if (timer.go)
                        //        timer.stop();

                        //    if (scope.handle)
                        //        scope.$root[scope.handle].$emit(scope.handle + ':stop');
                        //}

                        //scope.$on('$destroy', function () {
                        //    timer.stop();
                        //});
                    }
                }
            }
        ])
        .directive('binaryClock', [
            '$timeout', function ($timeout) {
                return {
                    restrict: 'E',
                    template:
                        '
' +
                            '
' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '

	' +
                            '

	' +
                            '
' +
                            '
' +
                            '
' +
                            '
',
                    link: function (scope, elem, attrs) {

                        $timeout(function () {
                            function setBit(field, value) {

                                var ele = document.getElementById(field);

                                if (ele.classList.contains('blank'))
                                    return false;

                                if (value)
                                    ele.setAttribute('class', 'bit on');
                                else
                                    ele.setAttribute('class', 'bit off');
                            }

                            function setBCDs(field, value) {

                                setBit(field + '-forties', value >= 40);

                                if (value >= 40) {
                                    value -= 40;
                                }
                                setBit(field + '-twenties', value >= 20);
                                if (value >= 20) {
                                    value -= 20;
                                }
                                setBit(field + '-tens', value >= 10);
                                if (value >= 10) {
                                    value -= 10;
                                }
                                setBit(field + '-eights', value >= 8);
                                if (value >= 8) {
                                    value -= 8;
                                }
                                setBit(field + '-fours', value >= 4);
                                if (value >= 4) {
                                    value -= 4;
                                }
                                setBit(field + '-twos', value >= 2);
                                if (value >= 2) {
                                    value -= 2;
                                }
                                setBit(field + '-ones', value >= 1);
                            }

                            function tick() {
                                var now = new Date();
                                setBCDs('hours', now.getHours());
                                setBCDs('minutes', now.getMinutes());
                                setBCDs('seconds', now.getSeconds());
                            }

                            tick();

                            var timer = new Tock({
                                countdown: true,
                                interval: 100,
                                callback: function () {
                                    tick();
                                }
                            });

                            timer.start(Date.now());

                            //self.setInterval(tick, 500); // - mayhaps?
                        });
                    }
                }
            }
        ])
        .factory('tickHelper', [
            function () {

                function getDuration(chunk) {

                    var now = new Date();

                    now.setMilliseconds(now.getMilliseconds() + (chunk.ms ? chunk.ms : 0));
                    now.setSeconds(now.getSeconds() + (chunk.s || 0));
                    now.setMinutes(now.getMinutes() + (chunk.m || 0));
                    now.setHours(now.getHours() + (chunk.h || 0));

                    return Math.abs(now - new Date());
                }

                return {
                    'getDuration': getDuration
                }
            }
        ])
        .filter('relativeTime', [
            function () {
                return function (ms) {
                    return new Date(1970, 0, 1, 0).setMilliseconds(ms);
                };
            }
        ])
        .factory('engine', function () {

            function timer(options) {

                options = options || {};

                var self = this;
                var start;
                var time;
                var interval = options.interval || 10;
                var thread;
                var phantom = 0;
                var lap = false;
                var circuit = 0;
                var lapped = 0;

                self.ticking = false;

                function tick() {

                    if (self.ticking) {
                        time += interval;
                        lapped += interval;

                        if (lap) {
                            lap = false;
                            self.onLap(circuit, lapped);
                            lapped = 0;
                        }

                        self.onTick(time);
                        var diff = (Date.now() - start) - time;
                        setTimeout(tick, (interval - diff));
                    }
                }

                this.onTick = options.onTick || function () { };

                this.onLap = options.onLap || function () { };

                this.reset = function () {
                    clearTimeout(thread);
                    self.ticking = false;
                    phantom = 0;
                    lapped = 0;
                    circuit = 0;
                    self.onTick(0);
                    return this;
                }

                this.start = function () {
                    if (!self.ticking) {
                        time = phantom > 0 ? phantom : 0;
                        start = Date.now() - time;
                        self.ticking = true;
                        thread = setTimeout(tick, interval);
                    }

                    return this;
                }

                this.stop = function () {
                    if (self.ticking) {
                        phantom = time;
                        self.ticking = false;
                        lapped = 0;
                    }

                    return this;
                }

                this.lap = function () {
                    if (self.ticking) {
                        lap = true;
                        circuit += 1;
                    }

                    return this;
                }
            }

            function countdown(options) {

                options = options || {};

                this.ticking = false;
            }

            return {
                'timer': function (options) {
                    return new timer(options);
                },
                'countdown': function (options) {
                    return new countdown(options);
                }
        }
        });
}

(function () {
    if (typeof define === 'function' && define.amd) { // RequireJS aware
        define(['angular'], function () {
            init();
        });
    } else {
        init();
    }
}());<|endoftext|>


<commit_before>(function () {
  "use strict";

  var lineReader        = require('../lib/line_reader'),
      assert            = require('assert'),
      testFilePath      = __dirname + '/test_file.txt',
      separatorFilePath = __dirname + '/test_separator_file.txt',
      multibyteFilePath = __dirname + '/test_multibyte_file.txt',
      emptyFilePath     = __dirname + '/empty_file.txt',
      i                 = 0,
      j                 = 0,
      k                 = 0,
      testSeparatorFile = ['foo', 'bar\n', 'baz\n'],
      testFile = [
        'Jabberwocky',
        '',
        '’Twas brillig, and the slithy toves',
        'Did gyre and gimble in the wabe;',
        '',
        ''
      ];

  lineReader.eachLine(testFilePath, function (line, last) {
    assert.equal(testFile[i], line, 'Each line should be what we expect');
    i += 1;

    if (i === 6) {
      assert.ok(last);
    } else {
      assert.ok(!last);
    }
  });

  lineReader.eachLine(separatorFilePath, function (line, last) {
    assert.equal(testSeparatorFile[j], line);
    j += 1;

    if (j === 3) {
      assert.ok(last);
    } else {
      assert.ok(!last);
    }
  }, ';');

  lineReader.eachLine(testFilePath, function (line, last) {
    assert.equal(testFile[k], line, 'Each line should be what we expect');
    k += 1;

    if (k === 2) {
      return false;
    }
  });

  process.on('exit', function () {
    assert.equal(i, 6, 'Should read 6 lines from test file');
    assert.equal(j, 3, 'Should read 3 lines from separator test file');
    assert.equal(k, 2, 'Should read 2 lines from test file when false is returned');

    console.log('OK');
  });

  lineReader.eachLine(emptyFilePath, function (line) {
    assert.ok(false, 'Empty file should not cause any callbacks');
  });

  lineReader.open(testFilePath, function (reader) {
    assert.ok(reader.hasNextLine());

    assert.ok(reader.hasNextLine(), 'Calling hasNextLine multiple times should be ok');

    reader.nextLine(function (line) {
      assert.equal('Jabberwocky', line);
      assert.ok(reader.hasNextLine());
      reader.nextLine(function (line) {
        assert.equal('', line);
        assert.ok(reader.hasNextLine());
        reader.nextLine(function (line) {
          assert.equal('’Twas brillig, and the slithy toves', line);
          assert.ok(reader.hasNextLine());
          reader.nextLine(function (line) {
            assert.equal('Did gyre and gimble in the wabe;', line);
            assert.ok(reader.hasNextLine());
            reader.nextLine(function (line) {
              assert.equal('', line);
              assert.ok(reader.hasNextLine());
              reader.nextLine(function (line) {
                assert.equal('', line);
                assert.ok(!reader.hasNextLine());

                assert.throws(function () {
                  reader.nextLine(function () {});
                }, Error, "Should be able to read next line at EOF");
              });
            });
          });
        });
      });
    });
  });

  lineReader.open(multibyteFilePath, function(reader) {
    assert.ok(reader.hasNextLine());
    reader.nextLine(function(line) {
      assert.equal('ふうりうの初やおくの田植うた', line,
                   "Should read multibyte characters on buffer boundary");
    });
  }, '\n', 'utf8', 2);

}());
<commit_msg>Rewrite tests in mocha<commit_after>var lineReader        = require('../lib/line_reader'),
    assert            = require('assert'),
    testFilePath      = __dirname + '/test_file.txt',
    separatorFilePath = __dirname + '/test_separator_file.txt',
    multibyteFilePath = __dirname + '/test_multibyte_file.txt',
    emptyFilePath     = __dirname + '/empty_file.txt',
    testSeparatorFile = ['foo', 'bar\n', 'baz\n'],
    testFile = [
      'Jabberwocky',
      '',
      '’Twas brillig, and the slithy toves',
      'Did gyre and gimble in the wabe;',
      '',
      ''
    ];

describe("lineReader", function() {
  describe("eachLine", function() {
    it("should read lines using the defalut separator", function(done) {
      var i = 0;

      lineReader.eachLine(testFilePath, function (line, last) {
        assert.equal(testFile[i], line, 'Each line should be what we expect');
        i += 1;

        if (i === 6) {
          assert.ok(last);
        } else {
          assert.ok(!last);
        }
      }).then(function() {
        assert.equal(6, i);
        done();
      });
    });

    it("should separate files using given separator", function(done) {
      var i = 0;
      lineReader.eachLine(separatorFilePath, function (line, last) {
        assert.equal(testSeparatorFile[i], line);
        i += 1;

        if (i === 3) {
          assert.ok(last);
        } else {
          assert.ok(!last);
        }
      }, ';').then(function() {
        assert.equal(3, i);
        done();
      });
    });

    it("should allow early termination of line reading", function(done) {
      var i = 0;
      lineReader.eachLine(testFilePath, function (line, last) {
        assert.equal(testFile[i], line, 'Each line should be what we expect');
        i += 1;

        if (i === 2) {
          return false;
        }
      }).then(function() {
        assert.equal(2, i);
        done();
      });
    });

    it("should not call callback on empty file", function(done) {
      lineReader.eachLine(emptyFilePath, function (line) {
        assert.ok(false, "Empty file should not cause any callbacks");
      }).then(function() {
        done()
      });
    });

  });

  describe("open", function() {
    it("should return a reader object and allow calls to nextLine", function(done) {
      lineReader.open(testFilePath, function (reader) {
        assert.ok(reader.hasNextLine());

        assert.ok(reader.hasNextLine(), 'Calling hasNextLine multiple times should be ok');

        reader.nextLine(function (line) {
          assert.equal('Jabberwocky', line);
          assert.ok(reader.hasNextLine());
          reader.nextLine(function (line) {
            assert.equal('', line);
            assert.ok(reader.hasNextLine());
            reader.nextLine(function (line) {
              assert.equal('’Twas brillig, and the slithy toves', line);
              assert.ok(reader.hasNextLine());
              reader.nextLine(function (line) {
                assert.equal('Did gyre and gimble in the wabe;', line);
                assert.ok(reader.hasNextLine());
                reader.nextLine(function (line) {
                  assert.equal('', line);
                  assert.ok(reader.hasNextLine());
                  reader.nextLine(function (line) {
                    assert.equal('', line);
                    assert.ok(!reader.hasNextLine());

                    assert.throws(function () {
                      reader.nextLine(function () {
                      });
                    }, Error, "Should be able to read next line at EOF");

                    done();
                  });
                });
              });
            });
          });
        });
      });
    });

    it("should read multibyte characters on the buffer boundary", function(done) {
      lineReader.open(multibyteFilePath, function(reader) {
        assert.ok(reader.hasNextLine());
        reader.nextLine(function(line) {
          assert.equal('ふうりうの初やおくの田植うた', line,
                       "Should read multibyte characters on buffer boundary");
          done();
        });
      }, '\n', 'utf8', 2);
    });
  });
});
<|endoftext|>


<commit_before>"use strict";

const fs = require("fs-extra");
const path = require("path");
const mkdirp = require("mkdirp");
const rimraf = require("rimraf");

const Saver = require("./saver.js");

const config_file = "config.json";

/**
 * skip any folder which contains a '.before-dawn-skip' file
 * this way we can have templates and documentation and things like
 * that which won't get loaded into the app by mistake.
 */
var skipFolder = function(p) {
  return fs.existsSync(path.join(p, ".before-dawn-skip"));
};

module.exports = class SaverListManager {
  constructor(opts, logger) {
    this.prefs = opts.prefs;
    this.loadedScreensavers = [];
    this._firstLoad = false;

    if ( logger !== undefined ) {
      this.logger = logger;
    }
    else {
      this.logger = function() {};
    }

    if ( typeof(opts) === "string" ) {
      opts = {
        base: opts
      }
    }

    this.baseDir = this.prefs.baseDir;
  }

  get firstLoad() {
    return this._firstLoad;
  };

  get defaultSaversDir() {
    return path.join(this.baseDir, "savers");
  };

  setup(load_savers) {
    let _self = this;
    return new Promise(function (resolve, reject) {
      var configPath = path.join(_self.baseDir, config_file);
      var saversDir = _self.defaultSaversDir;
      var results = {
        first: false,
        setup: false
      };

      _self.logger("saversDir: " + saversDir, fs.existsSync(saversDir));
      _self.logger("configPath: " + configPath);

      // check for/create our main directory
      // and our savers directory (which is a subdir
      // of the main dir)
      mkdirp(saversDir, function(err, made) {
        if ( err ) {
          _self.logger("err!", err);
          return reject(err);
        }

        // check if we just created the folder,
        // if there's no config yet,
        // or if the savers folder was empty
        if ( made === true || ! fs.existsSync(configPath) || fs.readdirSync(saversDir).length === 0 ) {
          //_firstLoad = true;
          results.first = true;
        }

        results.setup = true;

        resolve(results);
      });
    });
  };

  /**
   * reload all our data/config/etc
   */
  reload(load_savers) {
    this.logger("savers.reload");
    return setup(load_savers).then(handlePackageChecks);
  };

  reset() {
    this.loadedScreensavers = [];
  };

  /**
   * search for all screensavers we can find on the filesystem. if cb is specified,
   * call it with data when done. if reload == true, don't use cached data.
   */
  list(cb, force) {
    let _self = this;
    var folders = this.prefs.sources;
    var glob, pattern, savers;

    var promises = [];

    // exclude system screensavers from the cache check
    // @todo get rid of this
    var systemScreensaverCount = 1;

    // use cached data if available
    if ( this.loadedScreensavers.length > systemScreensaverCount &&
        ( typeof(force) === "undefined" || force === false ) ) {
      cb(this.loadedScreensavers);
      return;
    }

    glob = require("glob");

    // note: using /**/ here instead of /*/ would
    // also match all subdirectories, which might be desirable
    // or even required, but is a lot slower, so not doing it
    // for now
    pattern = "{" + folders.join(",") + ",}/*/saver.json";
    savers = glob.sync(pattern);

    for ( var i = 0; i < savers.length; i++ ) {
      var f = savers[i];
      var folder = path.dirname(f);
      var doLoad = ! folder.split(path.sep).reverse()[0].match(/^__/) &&
                  ! skipFolder(folder);

      if ( doLoad ) {
        promises.push(this.loadFromFile(f));
      }
    }

    // filter out failed promises here
    // @see https://davidwalsh.name/promises-results
    promises = promises.map(p => p.catch(() => undefined))

    Promise.all(promises).then(function(data) {
      // remove any undefined screensavers
      _self.loadedScreensavers = data.
        filter(s => s !== undefined).
        sort((a, b) => {
          return a.name.toLowerCase().localeCompare(b.name.toLowerCase())
        });
      cb(_self.loadedScreensavers);
    });
  };

  /**
   * look up a screensaver by key, and return it
   */
  getByKey(key) {
    var result = this.loadedScreensavers.find((obj) => {
      return obj.key === key;
    });
    return result;
  };

  /**
   * load screensaver data from filesystem
   */
  loadFromFile(src, settings) {
    let _self = this;

    return new Promise(function (resolve, reject) {
      fs.readFile(src, {encoding: "utf8"}, (err, content) => {
        if ( err ) {
          _self.logger("loadFromFile err", src, err);
          reject(err);
        }
        else {
          try {
            var contents = JSON.parse(content);
            var stub = path.dirname(src);
            var s = _self.loadFromData(contents, stub, settings);

            if ( s.valid ) {
              resolve(s);
            }
            else {
              _self.logger("loadFromFile not valid! " + src);
              reject();
            }
          }
          catch(e) {
            _self.logger("loadFromFile exception", e);
            reject(e);
          }
        }
      });
    });
  };

  loadFromData(contents, stub, settings) {
    var src = this.prefs.localSource;

    if ( typeof(stub) !== "undefined" ) {
      contents.path = stub;
      contents.key = path.join(stub, "saver.json");
    }

    contents.editable = false;
    if ( typeof(src) !== "undefined" && src !== "" ) {
      contents.editable = (contents.key.indexOf(src) === 0);
    }

    if ( typeof(contents.settings) === "undefined" ) {
      if ( settings === undefined ) {
        // ensure that all screensavers have options set
        if ( this.prefs.options[contents.key] === undefined ) {
          this.prefs.options[contents.key] = {};
        }

        settings = this.prefs.options[contents.key];
      }
      contents.settings = settings;
    }

    return new Saver(contents);
  };

  /**
   * generate a screensaver template
   */
  create(src, opts) {
    var destDir = this.prefs.localSource;

    var contents = fs.readdirSync(src);
    var defaults = {
      "source": "index.html",
      "options": []
    };

    if ( destDir === "" ) {
      throw new Error("No local directory specified!");
    }

    opts = Object.assign({}, defaults, opts);
    opts.key = opts.name.toLowerCase().
                    replace(/[^a-z0-9]+/gi, "-").
                    replace(/-$/, "").
                    replace(/^-/, "");

    var dest = path.join(destDir, opts.key);
    fs.mkdirpSync(dest);

    contents.forEach(function(content) {
      fs.copySync(path.join(src, content), path.join(dest, content));
    });

    //
    // generate JSON file
    //
    var configDest = path.join(dest, "saver.json");
    var content = fs.readFileSync( configDest );
    contents = Object.assign({}, JSON.parse(content), opts);

    fs.writeFileSync(configDest, JSON.stringify(contents, null, 2));

    // add dest in case someone needs it
    // but don't persist that data because that would be icky
    opts.dest = path.join(dest, "saver.json");

    return opts;
  };

  /**
   * delete a screensaver -- this removes the directory that contains all files
   * for the screensaver.
   */
  delete(s, cb) {
    var k = s.key;
    var p = path.dirname(k);

    var cbWrapped = function() {
      reload(cb);
    };

    // make sure we're deleting a screensaver that exists and is
    // actually editable
    if ( typeof(s) !== "undefined" && s.editable === true ) {
      rimraf(p, cbWrapped);
    }
    else {
      cbWrapped();
    }
  };
}

// var handlePackageChecks = function(opts) {
//   var first = opts.first;
//   var setup = opts.setup;

//   this.logger("handlePackageChecks", opts);
//   if ( first || setup ) {
//     this.logger("need to setup packages");
//     return setupPackages();
//   }
//   return Promise.resolve();
// }

// /**
//  * reload all our data/config/etc
//  */
// var setupPackages = function() {
//   this.logger("setupPackages");
//   return new Promise((resolve, reject) => {
//     updatePackage().then((data) => {
//       _firstLoad = false;

//       if ( data.downloaded === true ) {
//         setConfig("sourceUpdatedAt", data.updated_at);
//       }

//       listAll(function(data) {
//         var current = nconf.get("saver");
//         if (
//           ( current === undefined || getCurrentData() === undefined ) &&
//           data.length > 0
//         ) {
//           setConfig("saver", data[0].key);
//           writeSync();
//         }

//         resolve();
//       });
//     });
//   });
// };

// var getPackage = function() {
//   var source = getSource();
//   var sourceUpdatedAt = getSourceUpdatedAt();
//   return new Package({
//     repo:source,
//     updated_at:sourceUpdatedAt,
//     dest:defaultSaversDir()
//   });
// };

// var updatePackage = function(p) {
//   var lastCheckAt = getUpdateCheckTimestamp();
//   var now = new Date().getTime();
//   var diff = now - lastCheckAt;

//   if ( p === undefined ) {
//     p = getPackage();
//   }

//   this.logger("lastCheckAt: " + lastCheckAt + " - " + diff + " - " + PACKAGE_WAIT_TIME);
//   // don't bother checking if there's no source repo specified,
//   // or if we've pinged it recently
//   if ( typeof(p.repo) === "undefined" || p.repo === "" || diff < PACKAGE_WAIT_TIME ) {
//     this.logger("skip package check for now: " + diff);
//     return Promise.resolve({downloaded: false});
//   }
//   else {
//     setUpdateCheckTimestamp(now);
//     writeSync();

//     // @todo handle local check here
//     this.logger("check package: " + p.repo);
//     return p.checkLatestRelease();
//   }
// };

// var loadCurrent = function(cb) {
//   var current = nconf.get("saver");
//   listAll(function(data) {
//     var result = data.find((obj) => {
//       return obj.key === key;
//     });

//     cb(result);
//   });
// };

// /**
//  * pick a random screensaver
//  */
// var getRandomScreensaver = function() {
//   var tmp = this.loadedScreensavers.filter((s) => {
//     return ( typeof(s.preload) === "undefined" );
//   });
//   var idx = Math.floor(Math.random() * tmp.length);

//   return tmp[idx];
// };

// /**
//  * run any defined preload scripts for the screensaver.
//  * for now, there's really just one and it's basically
//  * hardcoded -- replace the given screensaver with a random one.
//  */
// var applyPreload = function(saver) {
//   if ( saver.preload && saver.preload === "random" ) {
//     return getRandomScreensaver();
//   }

//   return saver;
// };
<commit_msg>Remove dead code<commit_after>"use strict";

const fs = require("fs-extra");
const path = require("path");
const mkdirp = require("mkdirp");
const rimraf = require("rimraf");

const Saver = require("./saver.js");

const config_file = "config.json";

/**
 * skip any folder which contains a '.before-dawn-skip' file
 * this way we can have templates and documentation and things like
 * that which won't get loaded into the app by mistake.
 */
var skipFolder = function(p) {
  return fs.existsSync(path.join(p, ".before-dawn-skip"));
};

module.exports = class SaverListManager {
  constructor(opts, logger) {
    this.prefs = opts.prefs;
    this.loadedScreensavers = [];
    this._firstLoad = false;

    if ( logger !== undefined ) {
      this.logger = logger;
    }
    else {
      this.logger = function() {};
    }

    if ( typeof(opts) === "string" ) {
      opts = {
        base: opts
      }
    }

    this.baseDir = this.prefs.baseDir;
  }

  get firstLoad() {
    return this._firstLoad;
  };

  get defaultSaversDir() {
    return path.join(this.baseDir, "savers");
  };

  setup(load_savers) {
    let _self = this;
    return new Promise(function (resolve, reject) {
      var configPath = path.join(_self.baseDir, config_file);
      var saversDir = _self.defaultSaversDir;
      var results = {
        first: false,
        setup: false
      };

      _self.logger("saversDir: " + saversDir, fs.existsSync(saversDir));
      _self.logger("configPath: " + configPath);

      // check for/create our main directory
      // and our savers directory (which is a subdir
      // of the main dir)
      mkdirp(saversDir, function(err, made) {
        if ( err ) {
          _self.logger("err!", err);
          return reject(err);
        }

        // check if we just created the folder,
        // if there's no config yet,
        // or if the savers folder was empty
        if ( made === true || ! fs.existsSync(configPath) || fs.readdirSync(saversDir).length === 0 ) {
          //_firstLoad = true;
          results.first = true;
        }

        results.setup = true;

        resolve(results);
      });
    });
  };

  /**
   * reload all our data/config/etc
   */
  reload(load_savers) {
    this.logger("savers.reload");
    return setup(load_savers).then(handlePackageChecks);
  };

  reset() {
    this.loadedScreensavers = [];
  };

  /**
   * search for all screensavers we can find on the filesystem. if cb is specified,
   * call it with data when done. if reload == true, don't use cached data.
   */
  list(cb, force) {
    let _self = this;
    var folders = this.prefs.sources;
    var glob, pattern, savers;

    var promises = [];

    // exclude system screensavers from the cache check
    // @todo get rid of this
    var systemScreensaverCount = 1;

    // use cached data if available
    if ( this.loadedScreensavers.length > systemScreensaverCount &&
        ( typeof(force) === "undefined" || force === false ) ) {
      cb(this.loadedScreensavers);
      return;
    }

    glob = require("glob");

    // note: using /**/ here instead of /*/ would
    // also match all subdirectories, which might be desirable
    // or even required, but is a lot slower, so not doing it
    // for now
    pattern = "{" + folders.join(",") + ",}/*/saver.json";
    savers = glob.sync(pattern);

    for ( var i = 0; i < savers.length; i++ ) {
      var f = savers[i];
      var folder = path.dirname(f);
      var doLoad = ! folder.split(path.sep).reverse()[0].match(/^__/) &&
                  ! skipFolder(folder);

      if ( doLoad ) {
        promises.push(this.loadFromFile(f));
      }
    }

    // filter out failed promises here
    // @see https://davidwalsh.name/promises-results
    promises = promises.map(p => p.catch(() => undefined))

    Promise.all(promises).then(function(data) {
      // remove any undefined screensavers
      _self.loadedScreensavers = data.
        filter(s => s !== undefined).
        sort((a, b) => {
          return a.name.toLowerCase().localeCompare(b.name.toLowerCase())
        });
      cb(_self.loadedScreensavers);
    });
  };

  /**
   * look up a screensaver by key, and return it
   */
  getByKey(key) {
    var result = this.loadedScreensavers.find((obj) => {
      return obj.key === key;
    });
    return result;
  };

  /**
   * load screensaver data from filesystem
   */
  loadFromFile(src, settings) {
    let _self = this;

    return new Promise(function (resolve, reject) {
      fs.readFile(src, {encoding: "utf8"}, (err, content) => {
        if ( err ) {
          _self.logger("loadFromFile err", src, err);
          reject(err);
        }
        else {
          try {
            var contents = JSON.parse(content);
            var stub = path.dirname(src);
            var s = _self.loadFromData(contents, stub, settings);

            if ( s.valid ) {
              resolve(s);
            }
            else {
              _self.logger("loadFromFile not valid! " + src);
              reject();
            }
          }
          catch(e) {
            _self.logger("loadFromFile exception", e);
            reject(e);
          }
        }
      });
    });
  };

  loadFromData(contents, stub, settings) {
    var src = this.prefs.localSource;

    if ( typeof(stub) !== "undefined" ) {
      contents.path = stub;
      contents.key = path.join(stub, "saver.json");
    }

    contents.editable = false;
    if ( typeof(src) !== "undefined" && src !== "" ) {
      contents.editable = (contents.key.indexOf(src) === 0);
    }

    if ( typeof(contents.settings) === "undefined" ) {
      if ( settings === undefined ) {
        // ensure that all screensavers have options set
        if ( this.prefs.options[contents.key] === undefined ) {
          this.prefs.options[contents.key] = {};
        }

        settings = this.prefs.options[contents.key];
      }
      contents.settings = settings;
    }

    return new Saver(contents);
  };

  /**
   * generate a screensaver template
   */
  create(src, opts) {
    var destDir = this.prefs.localSource;

    var contents = fs.readdirSync(src);
    var defaults = {
      "source": "index.html",
      "options": []
    };

    if ( destDir === "" ) {
      throw new Error("No local directory specified!");
    }

    opts = Object.assign({}, defaults, opts);
    opts.key = opts.name.toLowerCase().
                    replace(/[^a-z0-9]+/gi, "-").
                    replace(/-$/, "").
                    replace(/^-/, "");

    var dest = path.join(destDir, opts.key);
    fs.mkdirpSync(dest);

    contents.forEach(function(content) {
      fs.copySync(path.join(src, content), path.join(dest, content));
    });

    //
    // generate JSON file
    //
    var configDest = path.join(dest, "saver.json");
    var content = fs.readFileSync( configDest );
    contents = Object.assign({}, JSON.parse(content), opts);

    fs.writeFileSync(configDest, JSON.stringify(contents, null, 2));

    // add dest in case someone needs it
    // but don't persist that data because that would be icky
    opts.dest = path.join(dest, "saver.json");

    return opts;
  };

  /**
   * delete a screensaver -- this removes the directory that contains all files
   * for the screensaver.
   */
  delete(s, cb) {
    var k = s.key;
    var p = path.dirname(k);

    var cbWrapped = function() {
      reload(cb);
    };

    // make sure we're deleting a screensaver that exists and is
    // actually editable
    if ( typeof(s) !== "undefined" && s.editable === true ) {
      rimraf(p, cbWrapped);
    }
    else {
      cbWrapped();
    }
  };
}
<|endoftext|>


<commit_before>import React from 'react'
import loadScript from 'load-script'

import Base from './Base'

const SDK_URL = '//www.youtube.com/iframe_api'
const SDK_GLOBAL = 'YT'
const MATCH_URL = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/
const PLAYER_ID = 'youtube-player'
const BLANK_VIDEO_URL = 'https://www.youtube.com/watch?v=GlCmAC4MHek'
const DEFAULT_PLAYER_VARS = {
  autoplay: 0,
  controls: 0,
  showinfo: 0
}

let count = 0

export default class YouTube extends Base {
  static displayName = 'YouTube'
  static canPlay (url) {
    return MATCH_URL.test(url)
  }
  playerId = PLAYER_ID + '-' + count++
  componentDidMount () {
    if (!this.props.url && this.props.youtubeConfig.preload) {
      this.preloading = true
      this.load(BLANK_VIDEO_URL)
    }
    super.componentDidMount()
  }
  getSDK () {
    if (window[SDK_GLOBAL]) {
      return Promise.resolve(window[SDK_GLOBAL])
    }
    return new Promise((resolve, reject) => {
      const previousOnReady = window.onYouTubeIframeAPIReady
      window.onYouTubeIframeAPIReady = function () {
        if (previousOnReady) previousOnReady()
        resolve(window[SDK_GLOBAL])
      }
      loadScript(SDK_URL, err => {
        if (err) reject(err)
      })
    })
  }
  load (url, playing) {
    const id = url && url.match(MATCH_URL)[1]
    if (this.isReady) {
      this.stop()
      if (playing) {
        this.player.loadVideoById(id)
      } else {
        this.player.cueVideoById(id)
      }
      return
    }
    this.getSDK().then(YT => {
      this.player = new YT.Player(this.playerId, {
        width: '100%',
        height: '100%',
        videoId: id,
        playerVars: { ...DEFAULT_PLAYER_VARS, ...this.props.youtubeConfig.playerVars },
        events: {
          onReady: this.onReady,
          onStateChange: this.onStateChange,
          onError: this.props.onError
        }
      })
    })
  }
  onStateChange = state => {
    const YT = window[SDK_GLOBAL]
    if (state.data === YT.PlayerState.PLAYING) this.props.onPlay()
    if (state.data === YT.PlayerState.PAUSED) this.props.onPause()
    if (state.data === YT.PlayerState.BUFFERING) this.props.onBuffer()
    if (state.data === YT.PlayerState.ENDED) this.props.onEnded()
  }
  play () {
    if (!this.isReady) return
    this.player.playVideo()
  }
  pause () {
    if (!this.isReady) return
    this.player.pauseVideo()
  }
  stop () {
    if (!this.isReady) return
    this.player.stopVideo()
  }
  seekTo (fraction) {
    if (!this.isReady) return
    this.player.seekTo(this.player.getDuration() * fraction)
  }
  setVolume (fraction) {
    if (!this.isReady) return
    this.player.setVolume(fraction * 100)
  }
  getFractionPlayed () {
    if (!this.isReady) return null
    return this.player.getCurrentTime() / this.player.getDuration()
  }
  getFractionLoaded () {
    if (!this.isReady) return null
    return this.player.getVideoLoadedFraction()
  }
  render () {
    const style = { display: this.props.url ? 'block' : 'none' }
    return

  }
}
<commit_msg>Stronger guards for YouTube player methods<commit_after>import React from 'react'
import loadScript from 'load-script'

import Base from './Base'

const SDK_URL = '//www.youtube.com/iframe_api'
const SDK_GLOBAL = 'YT'
const MATCH_URL = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/
const PLAYER_ID = 'youtube-player'
const BLANK_VIDEO_URL = 'https://www.youtube.com/watch?v=GlCmAC4MHek'
const DEFAULT_PLAYER_VARS = {
  autoplay: 0,
  controls: 0,
  showinfo: 0
}

let count = 0

export default class YouTube extends Base {
  static displayName = 'YouTube'
  static canPlay (url) {
    return MATCH_URL.test(url)
  }
  playerId = PLAYER_ID + '-' + count++
  componentDidMount () {
    if (!this.props.url && this.props.youtubeConfig.preload) {
      this.preloading = true
      this.load(BLANK_VIDEO_URL)
    }
    super.componentDidMount()
  }
  getSDK () {
    if (window[SDK_GLOBAL]) {
      return Promise.resolve(window[SDK_GLOBAL])
    }
    return new Promise((resolve, reject) => {
      const previousOnReady = window.onYouTubeIframeAPIReady
      window.onYouTubeIframeAPIReady = function () {
        if (previousOnReady) previousOnReady()
        resolve(window[SDK_GLOBAL])
      }
      loadScript(SDK_URL, err => {
        if (err) reject(err)
      })
    })
  }
  load (url, playing) {
    const id = url && url.match(MATCH_URL)[1]
    if (this.isReady) {
      this.stop()
      if (playing) {
        this.player.loadVideoById(id)
      } else {
        this.player.cueVideoById(id)
      }
      return
    }
    this.getSDK().then(YT => {
      this.player = new YT.Player(this.playerId, {
        width: '100%',
        height: '100%',
        videoId: id,
        playerVars: { ...DEFAULT_PLAYER_VARS, ...this.props.youtubeConfig.playerVars },
        events: {
          onReady: this.onReady,
          onStateChange: this.onStateChange,
          onError: this.props.onError
        }
      })
    })
  }
  onStateChange = state => {
    const YT = window[SDK_GLOBAL]
    if (state.data === YT.PlayerState.PLAYING) this.props.onPlay()
    if (state.data === YT.PlayerState.PAUSED) this.props.onPause()
    if (state.data === YT.PlayerState.BUFFERING) this.props.onBuffer()
    if (state.data === YT.PlayerState.ENDED) this.props.onEnded()
  }
  play () {
    if (!this.isReady || !this.player.playVideo) return
    this.player.playVideo()
  }
  pause () {
    if (!this.isReady || this.player.pauseVideo) return
    this.player.pauseVideo()
  }
  stop () {
    if (!this.isReady || !this.player.stopVideo) return
    this.player.stopVideo()
  }
  seekTo (fraction) {
    if (!this.isReady || !this.player.seekTo) return
    this.player.seekTo(this.player.getDuration() * fraction)
  }
  setVolume (fraction) {
    if (!this.isReady || !this.player.setVolume) return
    this.player.setVolume(fraction * 100)
  }
  getFractionPlayed () {
    if (!this.isReady || !this.player.getCurrentTime) return null
    return this.player.getCurrentTime() / this.player.getDuration()
  }
  getFractionLoaded () {
    if (!this.isReady || !this.player.getVideoLoadedFraction) return null
    return this.player.getVideoLoadedFraction()
  }
  render () {
    const style = { display: this.props.url ? 'block' : 'none' }
    return

  }
}
<|endoftext|>


<commit_before>import { join } from 'path'
import { createWriteStream, readFileSync } from 'fs'
import { exec as execSync } from 'shelljs'
import {
  isInitialized,
  isFile,
  forEach,
  exec,
  chdir,
  separator,
  log,
  splitVersion
} from '../../util'
import { start, stop, change_sequence as changeSeq } from 'simple-spinner'
import { checkRelease } from './check-release'
import { generateChangelog } from './generate-changelog'

changeSeq(['    ', '.   ', '..  ', '... ', '....', ' ...', '  ..', '   .'])

export const plugin = function release (program, config, directory) {
  program.command('release')
    .option('--check', 'only checks what needs to be released')
    .description('Publishes updates')
    .action((options) => action(config, directory, options))
}

function incrementName (code) {
  switch (code) {
    case 1: return 'patch'
    case 2: return 'minor'
    case 3: return 'major'
    default: return false
  }
}

function isDirectoryClean () {
  const isUnstagedChanges = execSync('git diff --exit-code', {silent: true}).code
  const isStagedChanged = execSync('git diff --cached --exit-code', {silent: true}).code
  return !(isUnstagedChanges || isStagedChanged)
}

function action (config, directory, options) {
  isInitialized(config)

  const releaseBranch = config && typeof config.releaseBranch === 'string'
    ? config.releaseBranch
    : 'master'

  const { code } = execSync('git checkout ' + releaseBranch, { silent: true })

  if (code !== 0) {
    throw new Error('Could not switch to ' + releaseBranch)
  }

  const packages = config.packages.map(getPkg(directory))

  checkRelease(packages).then(function (status) {
    const packageNames = Object.keys(status)

    forEach(packageNames, function (packageName) {
      const index = packages.indexOf(packageName)
      const relativePath = config.packages[index]

      const packageDirectory = join(directory, relativePath)
      const pkg = require(join(packageDirectory, 'package.json'))
      const currentVersion = splitVersion(require(join(packageDirectory, 'package.json')).version)
      const method = incrementName(status[packageName].increment)
      const newVersion = getNewVersion(currentVersion, status[packageName].increment)

      const check = options && options.check || false

      const changelog = join(packageDirectory, 'CHANGELOG.md')
      let previousFile = '\n'

      if (isFile(changelog)) {
        previousFile = readFileSync(changelog, 'utf8')
      }

      if (isDirectoryClean()) {
        if (!method || check || pkg.private) {
          console.log(separator(packageName))
          if (pkg.private) {
            log('Package is private; only generating changelog')
          }
        }

        if (!method || check || pkg.private) {
          generateChangelog({
            commits: status[packageName].commits,
            file: check === true ? process.stdout : createFileStream(changelog),
            version: newVersion,
            url: pkg.repository.url.replace('.git', '').replace('git+', '') || pkg.repository,
            bugs: pkg.bugs,
            previousFile
          }).then(() => {
            if (!check) {
              execute(
                'git add CHANGELOG.md',
                'git commit -m "docs(CHANGELOG): append to changelog"',
                `git tag -f ${newVersion}`,
                `git push origin ${releaseBranch}`,
                'git push origin --tags'
              )
            }
            console.log(separator())
          })
          return
        }

        // preform release
        chdir(packageDirectory)

        console.log(separator(packageName))
        process.stdout.write('    Running your tests')
        start()
        exec('npm test')
          .then(handleTestOutput(method, packageName, newVersion))
          .catch(handleTestError)
          .then(handleVersionOutput(method))
          .then(({ code, err }) => {
            if (code === 0) {
              // generateChangelog
              return generateChangelog({
                commits: status[packageName].commits,
                file: createFileStream(changelog),
                version: newVersion,
                url: pkg.repository.url.replace('.git', '').replace('git+', '') || pkg.repository,
                bugs: pkg.bugs,
                previousFile
              }).then((file) => {
                return execute(
                  'git add .',
                  `git commit -m "chore(release): release ${newVersion}"`,
                  `git push origin ${releaseBranch}`,
                  'git push origin --tags'
                )
              })
              .then(output => {
                stop()
                process.stdout.write('    Publishing package')
                start()
                handlePublishOutput(releaseBranch)(output)
              })
              .then(() => {
                console.log(separator())
              })
            } else {
              console.log(err)
            }
            console.log(separator())
          })

        chdir(directory)
      } else {
        log('RELEASE ERROR: Working directory must be clean to push release!')
      }
    })
  })
}

function execute (...commands) {
  function _exec (cmd) {
    const { code, stderr } = execSync(cmd, { silent: true })
    if (code === 0 && commands.length > 0) {
      const _cmd = commands.shift()
      return _exec(_cmd)
    }
    if (code !== 0) {
      console.log('\n' + stderr + '\n')
    }
  }

  return _exec(commands.shift())
}

function createFileStream (file) {
  if (!isFile(file)) {
    exec(`touch ${file}`, { silent: true, async: false })
  }
  return createWriteStream(file)
}

function handleTestOutput (method, packageName, newVersion) {
  return function ({ code, out, err }) {
    if (code === 0) {
      stop()
      log(out)
      process.stdout.write('    Running npm version')
      start()
      return exec(`npm version ${method} -m 'release(${packageName}): ${newVersion}'`)
    } else {
      throw out
    }
  }
}

function handleTestError (err) {
  stop()
  log('\n')
  log('Running your tests have failed: \n')
  log('    ' + err)
  console.log(separator())
}

function handleVersionOutput (method) {
  return function ({code, out, err}) {
    if (code === 0) {
      stop()
      log(out)
      process.stdout.write('    Generating Changelog')
      start()
      return exec('npm publish --access=public')
    } else {
      stop()
      log('\n')
      log(`npm version ${method} has failed: \n`)
      log()
      log(err)
      log()
      throw err
    }
  }
}

function handlePublishOutput (releaseBranch) {
  return function ({ code, out, err }) {
    stop()
    if (code === 0) {
      log(out)
      return exec(`git push origin ${releaseBranch}`)
    } else {
      log('Publishing your package has failed: \n')
      log(err)
      console.log(separator())
    }
  }
}

function getNewVersion ([major, minor, patch], increment) {
  switch (increment) {
    case 1: return `v${major}.${minor}.${parseInt(patch) + 1}`
    case 2: return `v${major}.${parseInt(minor) + 1}.0`
    case 3: return `v${parseInt(major) + 1}.0.0`
    default: return `v${major}.${minor}.${patch}`
  }
}

const getPkg = dir => name =>
  require(join(dir, name, 'package.json')).name
<commit_msg>fix(northbrook): publish seems to hang even when run successfully<commit_after>import { join } from 'path'
import { createWriteStream, readFileSync } from 'fs'
import { exec as execSync } from 'shelljs'
import {
  isInitialized,
  isFile,
  forEach,
  exec,
  chdir,
  separator,
  log,
  splitVersion
} from '../../util'
import { start, stop, change_sequence as changeSeq } from 'simple-spinner'
import { checkRelease } from './check-release'
import { generateChangelog } from './generate-changelog'

changeSeq(['    ', '.   ', '..  ', '... ', '....', ' ...', '  ..', '   .'])

export const plugin = function release (program, config, directory) {
  program.command('release')
    .option('--check', 'only checks what needs to be released')
    .description('Publishes updates')
    .action((options) => action(config, directory, options))
}

function incrementName (code) {
  switch (code) {
    case 1: return 'patch'
    case 2: return 'minor'
    case 3: return 'major'
    default: return false
  }
}

function isDirectoryClean () {
  const isUnstagedChanges = execSync('git diff --exit-code', {silent: true}).code
  const isStagedChanged = execSync('git diff --cached --exit-code', {silent: true}).code
  return !(isUnstagedChanges || isStagedChanged)
}

function action (config, directory, options) {
  isInitialized(config)

  const releaseBranch = config && typeof config.releaseBranch === 'string'
    ? config.releaseBranch
    : 'master'

  const { code } = execSync('git checkout ' + releaseBranch, { silent: true })

  if (code !== 0) {
    throw new Error('Could not switch to ' + releaseBranch)
  }

  const packages = config.packages.map(getPkg(directory))

  checkRelease(packages).then(function (status) {
    const packageNames = Object.keys(status)

    forEach(packageNames, function (packageName) {
      const index = packages.indexOf(packageName)
      const relativePath = config.packages[index]

      const packageDirectory = join(directory, relativePath)
      const pkg = require(join(packageDirectory, 'package.json'))
      const currentVersion = splitVersion(require(join(packageDirectory, 'package.json')).version)
      const method = incrementName(status[packageName].increment)
      const newVersion = getNewVersion(currentVersion, status[packageName].increment)

      const check = options && options.check || false

      const changelogOptions = {
        commits: status[packageName].commits,
        file: createFileStream(changelog),
        version: newVersion,
        url: pkg.repository.url.replace('.git', '').replace('git+', '') || pkg.repository,
        bugs: pkg.bugs,
        previousFile
      }

      const changelog = join(packageDirectory, 'CHANGELOG.md')
      let previousFile = '\n'

      if (isFile(changelog)) {
        previousFile = readFileSync(changelog, 'utf8')
      }

      if (isDirectoryClean()) {
        if (!method || check || pkg.private) {
          console.log(separator(packageName))
          if (pkg.private) {
            log('Package is private; only generating changelog')
          }
        }

        if (!method || check || pkg.private) {
          generateChangelog(changelogOptions).then(() => {
            if (!check) {
              execute(
                'git add CHANGELOG.md',
                'git commit -m "docs(CHANGELOG): append to changelog"',
                `git tag -f ${newVersion}`,
                `git push origin ${releaseBranch}`,
                'git push origin --tags'
              )
            }
            console.log(separator())
          })
          return
        }

        // preform release
        chdir(packageDirectory)

        console.log(separator(packageName))
        process.stdout.write('    Running your tests')
        start()
        exec('npm test')
          .then(handleTestOutput(method, packageName, newVersion))
          .catch(handleTestError)
          .then(handleVersionOutput(method, releaseBranch, newVersion, changelogOptions))
          .then(handleChangelogOutput)
          .then(({ code, err, out }) => {
            stop()
            if (code === 0) {
              console.log(out)
            } else {
              console.log(err)
            }
            console.log(separator())
          })
          .then(() => { chdir(directory) })
          .catch(() => { chdir(directory) })
      } else {
        log('RELEASE ERROR: Working directory must be clean to push release!')
      }
    })
  })
}

function execute (...commands) {
  function _exec (cmd) {
    const { code, stderr } = execSync(cmd, { silent: true })
    if (code === 0 && commands.length > 0) {
      const _cmd = commands.shift()
      return _exec(_cmd)
    }
    if (code !== 0) {
      console.log('\n' + stderr + '\n')
    }
  }

  return _exec(commands.shift())
}

function createFileStream (file) {
  if (!isFile(file)) {
    exec(`touch ${file}`, { silent: true, async: false })
  }
  return createWriteStream(file)
}

function handleTestOutput (method, packageName, newVersion) {
  return function ({ code, out, err }) {
    if (code === 0) {
      stop()
      log(out)
      process.stdout.write('    Running npm version')
      start()
      return exec(`npm version ${method} -m 'release(${packageName}): ${newVersion}'`)
    } else {
      throw out
    }
  }
}

function handleTestError (err) {
  stop()
  log('\n')
  log('Running your tests have failed: \n')
  log('    ' + err)
  console.log(separator())
}

function handleVersionOutput (method, releaseBranch, newVersion, options) {
  return function ({code, out, err}) {
    if (code === 0) {
      stop()
      log(out)
      process.stdout.write('    Generating Changelog')
      start()
      return generateChangelog(options).then((file) => {
        return execute(
          'git add .',
          `git commit -m "chore(release): ${newVersion}"`,
          `git push origin ${releaseBranch}`,
          'git push origin --tags'
        )
      })
    } else {
      stop()
      log('\n')
      log(`npm version ${method} has failed: \n`)
      log()
      log(err)
      log()
      throw err
    }
  }
}

function handleChangelogOutput (method) {
  return function ({ code, out, err }) {
    stop()
    if (code === 0) {
      process.stdout.write('   Publishing your package')
      start()
      return exec(`npm publish ${method}`)
    } else {
      log('Publishing your package has failed: \n')
      log(err)
      console.log(separator())
    }
  }
}

function getNewVersion ([major, minor, patch], increment) {
  switch (increment) {
    case 1: return `v${major}.${minor}.${parseInt(patch) + 1}`
    case 2: return `v${major}.${parseInt(minor) + 1}.0`
    case 3: return `v${parseInt(major) + 1}.0.0`
    default: return `v${major}.${minor}.${patch}`
  }
}

const getPkg = dir => name =>
  require(join(dir, name, 'package.json')).name
<|endoftext|>


<commit_before><commit_msg>Add stream to both connections for firefox<commit_after><|endoftext|>


<commit_before>/*
PLAN OF FRAMEWORK:
-------------------------------
NOTES:
-As this is a one-page app framework, "page" shall henceforth mean a division of the site such that only one "page" is displayed at a time

1. HTML has custom tags, or tags with certian attributes applied, which break up the document
	1.a. Include dynamic tags to separate out into "pages", possibly:
		1.a.i. qk-page
	1.b. Include static, permanent tags, possibly:
		1.b.i. qk-const
	1.c. (Possibly, we see where this ended up for Angular) Include conditional, Angular-style tags
2. Custom CSS stylesheet bindings per page
3. JS parsing of all of this info
4. Custom DOM abstractor
5. JS using info to carry out info as parsed
-------------------------------
*/
var qk = new function(){

	var pages;
	var currentPage;

	this.go = function(args){
		main.go(args);
	}

	this.goTo = function(to){
		protos.gotoNoArg(to);
	}

	var protos = new function(){
		this.show = function(){
			this.style.display = "block";
		}
		this.hide = function(){
			this.style.display = "none";
		}
		this.gotoNoArg = function(){
			pages[currentPage].hide();
			currentPage = main.pageById(this.dataset.to);
			pages[currentPage].show();
		}
		this.gotoArg = function(toPage){
			pages[currentPage].hide();
			currentPage = toPage;
			pages[currentPage].show();
		}
		this.bindData = function(data, bindTo){
			dataBind.bind(data, bindTo);
		}
	};

	var dataBind = new function(){
		//do all of the magic template-style data binding here.
	}

	var main = new function(){

		/**Define qk-page's prototype **/
		var qkpageProto = Object.create(HTMLElement.prototype);
		qkpageProto.show = protos.show;
		qkpageProto.hide = protos.hide;
		/**End qk-page's prototype **/

		/**Define qk-const's prototype **/
		var qkconstProto = Object.create(HTMLElement.prototype);
		qkconstProto.show = protos.show;
		qkconstProto.hide = protos.hide;
		/**End qk-const's prototype **/

		/**Define qk-link's prototype **/
		var qklinkProto = Object.create(HTMLElement.prototype);
		qklinkProto.goto = protos.gotoNoArg;
		/**End qk-link's prototype **/

		/**Define qk-data's prototype **/
		var qkdataProto = Object.create(HTMLElement.prototype);
		qkdataProto.bind = protos.bindData;
		/**End qk-data's prototype **/

		var registerElements = function(){
			document.registerElement('qk-page', {
				prototype: qkpageProto
			});
			document.registerElement('qk-const', {
				prototype: qkconstProto
			});
			document.registerElement('qk-link', {
				prototype: qklinkProto
			});
			document.registerElement('qk-data', {
				prototype: qkdataProto
			});
		};

		var registerListeners = function(){
			Array.prototype.slice.call(document.querySelectorAll('qk-link')).forEach(function(current){
				current.addEventListener('click', function(){
					current.goto();
				})
			});
		}

		var displayHome = function(home){
			var hId = currentPage = main.pageById(home);
			for(var i = 0, l = pages.length; i < l; i++){
				pages[i].hide();
			}
			pages[hId].show();
		};

		var fetchPages = function(){
			pages = document.querySelectorAll('qk-page');
		}

		this.pageById = function(id){
			for(var i = 0, l = pages.length; i < l; i++){
				if(pages[i].dataset.id===id)
					return i;
			}
			return -1;
		};

		this.go = function(args){
			registerElements();
			registerListeners();
			fetchPages();
			displayHome(args.home);
		};
	};

}<commit_msg>more namespacing<commit_after>/*
PLAN OF FRAMEWORK:
-------------------------------
NOTES:
-As this is a one-page app framework, "page" shall henceforth mean a division of the site such that only one "page" is displayed at a time

1. HTML has custom tags, or tags with certian attributes applied, which break up the document
	1.a. Include dynamic tags to separate out into "pages", possibly:
		1.a.i. qk-page
	1.b. Include static, permanent tags, possibly:
		1.b.i. qk-const
	1.c. (Possibly, we see where this ended up for Angular) Include conditional, Angular-style tags
2. Custom CSS stylesheet bindings per page
3. JS parsing of all of this info
4. Custom DOM abstractor
5. JS using info to carry out info as parsed
-------------------------------
*/
var qk = new function(){

	var pages;
	var currentPage;

	this.go = function(args){
		main.go(args);
	};

	this.goTo = function(to){
		protos.gotoNoArg(to);
	};

	var protos = new function(){
		this.show = function(){
			this.style.display = "block";
		};
		this.hide = function(){
			this.style.display = "none";
		};
		this.gotoNoArg = function(){
			pages[currentPage].hide();
			currentPage = main.pageById(this.dataset.to);
			pages[currentPage].show();
		};
		this.gotoArg = function(toPage){
			pages[currentPage].hide();
			currentPage = toPage;
			pages[currentPage].show();
		};
		this.bindData = function(data, bindTo){
			dataBind.bind(data, bindTo);
		};
	};

	var dataBind = new function(){
		//do all of the magic template-style data binding here.
		this.bind = function(data, bindTo){

		};
	};

	var main = new function(){

		var registerElements = function(){
			/**Define qk-page's prototype **/
			var qkpageProto = Object.create(HTMLElement.prototype);
			qkpageProto.show = protos.show;
			qkpageProto.hide = protos.hide;
			/**End qk-page's prototype **/

			/**Define qk-const's prototype **/
			var qkconstProto = Object.create(HTMLElement.prototype);
			qkconstProto.show = protos.show;
			qkconstProto.hide = protos.hide;
			/**End qk-const's prototype **/

			/**Define qk-link's prototype **/
			var qklinkProto = Object.create(HTMLElement.prototype);
			qklinkProto.goto = protos.gotoNoArg;
			/**End qk-link's prototype **/

			/**Define qk-data's prototype **/
			var qkdataProto = Object.create(HTMLElement.prototype);
			qkdataProto.bind = protos.bindData;
			/**End qk-data's prototype **/

			document.registerElement('qk-page', {
				prototype: qkpageProto
			});
			document.registerElement('qk-const', {
				prototype: qkconstProto
			});
			document.registerElement('qk-link', {
				prototype: qklinkProto
			});
			document.registerElement('qk-data', {
				prototype: qkdataProto
			});
		};

		var registerListeners = function(){
			Array.prototype.slice.call(document.querySelectorAll('qk-link')).forEach(function(current){
				current.addEventListener('click', function(){
					current.goto();
				})
			});
		};

		var displayHome = function(home){
			var hId = currentPage = main.pageById(home);
			for(var i = 0, l = pages.length; i < l; i++){
				pages[i].hide();
			}
			pages[hId].show();
		};

		var fetchPages = function(){
			pages = document.querySelectorAll('qk-page');
		};

		this.pageById = function(id){
			for(var i = 0, l = pages.length; i < l; i++){
				if(pages[i].dataset.id===id)
					return i;
			}
			return -1;
		};

		this.go = function(args){
			registerElements();
			registerListeners();
			fetchPages();
			displayHome(args.home);
		};
	};

};<|endoftext|>


<commit_before>if (enyo && enyo.version) {
	enyo.version.layout = "2.3.0-rc.1";
}
<commit_msg>Update version string to 2.3.0-rc.2<commit_after>if (enyo && enyo.version) {
	enyo.version.layout = "2.3.0-rc.2";
}
<|endoftext|>