<commit_before>
// SmoothScroll v0.9.8
// Licensed under the terms of the MIT license.

// People involved
// - Balazs Galambosi: maintainer (CHANGELOG.txt)
// - Patrick Brunner (patrickb1991@gmail.com)
// - Michael Herf: Pulse Algorithm

// Scroll Variables (tweakable)
var framerate = 150; // [Hz]
var animtime  = 400; // [px]
var stepsize  = 120; // [px]

// Pulse (less tweakable)
// ratio of "tail" to "acceleration"
var pulseAlgorithm = true;
var pulseScale     = 8;
var pulseNormalize = 1;

// Keyboard Settings
var keyboardsupport = true;
var disableKeyboard = false;
var arrowscroll     = 50; // [px]

// Excluded pages
var exclude = "";

// Other Variables
var frame = false;
var direction = { x: 0, y: 0 };
var initdone  = false;
var activeElement;
var root;

var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36 };

/***********************************************
 * SETTINGS
 ***********************************************/

chrome.extension.connect({ name: "smoothscroll" }).
onMessage.addListener(function (settings) {

    // NOTE: + converts to {Number}
    framerate = +settings.framerate;
    animtime  = +settings.animtime;
    stepsize  = +settings.scrollsz;
    exclude   = settings.exclude;
    pulseAlgorithm  = (settings.pulseAlgorithm == "true");
    pulseScale      = +settings.pulseScale;
    keyboardsupport = (settings.keyboardsupport == "true");
    arrowscroll     = +settings.arrscroll;

    computePulseScale();

    // it seems that sometimes settings come late
    // and we need to test again for excluded pages
    initTest();

    if (keyboardsupport && !disableKeyboard) {
        addEvent("keydown", keydown);
    }

    // If extension settings were deleted somehow
    if (!framerate) {
        alert("SmoothScroll: Please restart Chrome");
    }
});

/***********************************************
 * INITIALIZE
 ***********************************************/

/**
 * Tests if smooth scrolling is allowed. Shuts down everything if not.
 */
function initTest() {

    // disable keys for google reader (spacebar conflict)
    if (document.URL.indexOf("google.com/reader/view") > -1) {
        disableKeyboard = true;
    }

    // disable everything if the page is blacklisted
    if (exclude) {
        var domains = exclude.split(/[,\n] ?/);
        for (var i = domains.length; i--;) {
            if (document.URL.indexOf(domains[i]) > -1) {
                removeEvent("mousewheel", wheel);
                disableKeyboard = true;
                break;
            }
        }
    }

    // disable keyboard support if anything above requested it
    if (disableKeyboard) {
        removeEvent("keydown", keydown);
    }
}

/**
 * Sets up scrolls array, determines if frames are involved.
 */
function init() {

    if (!document.body) return;

    var body = document.body;
    var html = document.documentElement;
    var windowHeight = window.innerHeight;
    var scrollHeight = body.scrollHeight;

    // check compat mode for root element
    root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
    activeElement = body;

    initTest();
    initdone = true;

    // Checks if this script is running in a frame
    if (top != self) {
        frame = true;
    }

    /**
     * This fixes a bug where the areas left and right to
     * the content does not trigger the onmousewheel event
     * on some pages. e.g.: html, body { height: 100% }
     */
    else if (scrollHeight > windowHeight &&
            (body.offsetHeight <= windowHeight ||
             html.offsetHeight <= windowHeight)) {
        root.style.height = "auto";
    }

    if (document.URL.indexOf("mail.google.com") > -1) {
        var s = document.createElement("style");
        s.innerHTML = ".iu { visibility: hidden }";
        document.body.appendChild(s);
    }
}

/************************************************
 * SCROLLING
 ************************************************/

var que = [];
var pending = false;

/**
 * Pushes scroll actions to the scrolling queue.
 */
function scrollArray(elem, multiplyX, multiplyY, delay) {

    delay || (delay = 1000);
    directionCheck(multiplyX, multiplyY);

    // push a scroll command
    que.push({
        x: multiplyX,
        y: multiplyY,
        lastX: (multiplyX < 0) ? 0.99 : -0.99,
        lastY: (multiplyY < 0) ? 0.99 : -0.99,
        start: +new Date
    });

    // don't act if there's a pending queue
    if (pending) {
        return;
    }

    var step = function() {

        var now = +new Date;
        var totalX = 0;
        var totalY = 0;

        for (var i = 0; i < que.length; i++) {

            var item = que[i];
            var elapsed  = now - item.start;
            var finished = (elapsed >= animtime);

            // scroll position: [0, 1]
            var position = (finished) ? 1 : elapsed / animtime;

            // easing [optional]
            if (pulseAlgorithm) {
                position = pulse(position);
            }

            // only need the difference
            var scrollX = (item.x * position - item.lastX) >> 0;
            var scrollY = (item.y * position - item.lastY) >> 0;

            // add this to the total scrolling
            totalX += scrollX;
            totalY += scrollY;

            // update last values
            item.lastX += scrollX;
            item.lastY += scrollY;

            // delete and step back if it's over
            if (finished) {
                que.splice(i, 1); i--;
            }
        }

        // scroll left
        if (multiplyX) {
            var lastLeft = elem.scrollLeft;
            elem.scrollLeft += totalX;

            // scroll left failed (edge)
            if (totalX && elem.scrollLeft === lastLeft) {
                multiplyX = 0;
            }
        }

        // scroll top
        if (multiplyY) {
            var lastTop = elem.scrollTop;
            elem.scrollTop += totalY;

            // scroll top failed (edge)
            if (totalY && elem.scrollTop === lastTop) {
                multiplyY = 0;
            }
        }

        // clean up if there's nothing left to do
        if (!multiplyX && !multiplyY) {
            que = [];
        }

        if (que.length) {
            setTimeout(step, delay / framerate + 1);
        } else {
            pending = false;
        }
    }

    // start a new queue of actions
    setTimeout(step, 0);
    pending = true;
}

/***********************************************
 * EVENTS
 ***********************************************/

/**
 * Mouse wheel handler.
 * @param {Object} event
 */
function wheel(event) {

    if (!initdone) {
        init();
    }

    var target = event.target;
    var overflowing = overflowingAncestor(target);

    // use default if there's no overflowing
    // element or default action is prevented
    if (!overflowing || event.defaultPrevented ||
        isNodeName(activeElement, "embed") ||
       (isNodeName(target, "embed") && /\.pdf/i.test(target.src))) {
        return true;
    }

    var deltaX = event.wheelDeltaX || 0;
    var deltaY = event.wheelDeltaY || 0;

    // use wheelDelta if deltaX/Y is not available
    if (!deltaX && !deltaY) {
        deltaY = event.wheelDelta || 0;
    }

    // scale by step size
    // delta is 120 most of the time
    // synaptics seems to send 1 sometimes
    if (Math.abs(deltaX) > 1.2) {
        deltaX *= stepsize / 120;
    }
    if (Math.abs(deltaY) > 1.2) {
        deltaY *= stepsize / 120;
    }

    scrollArray(overflowing, -deltaX, -deltaY);
    event.preventDefault();
}

/**
 * Keydown event handler.
 * @param {Object} event
 */
function keydown(event) {

    var target   = event.target;
    var modifier = event.ctrlKey || event.altKey || event.metaKey;

    // do nothing if user is editing text
    // or using a modifier key (except shift)
    if ( /input|textarea|embed/i.test(target.nodeName) ||
         target.isContentEditable ||
         event.defaultPrevented   ||
         modifier ) {
      return true;
    }
    // spacebar should trigger button press
    if (isNodeName(target, "button") &&
        event.keyCode === key.spacebar) {
      return true;
    }

    var shift, x = 0, y = 0;
    var elem = overflowingAncestor(activeElement);
    var clientHeight = elem.clientHeight;

    if (elem == document.body) {
        clientHeight = window.innerHeight;
    }

    switch (event.keyCode) {
        case key.up:
            y = -arrowscroll;
            break;
        case key.down:
            y = arrowscroll;
            break;
        case key.spacebar: // (+ shift)
            shift = event.shiftKey ? 1 : -1;
            y = -shift * clientHeight * 0.9;
            break;
        case key.pageup:
            y = -clientHeight * 0.9;
            break;
        case key.pagedown:
            y = clientHeight * 0.9;
            break;
        case key.home:
            y = -elem.scrollTop;
            break;
        case key.end:
            var damt = elem.scrollHeight - elem.scrollTop - clientHeight;
            y = (damt > 0) ? damt+10 : 0;
            break;
        case key.left:
            x = -arrowscroll;
            break;
        case key.right:
            x = arrowscroll;
            break;
        default:
            return true; // a key we don't care about
    }

    scrollArray(elem, x, y);
    event.preventDefault();
}

/**
 * Mousedown event only for updating activeElement
 */
function mousedown(event) {
    activeElement = event.target;
}

/***********************************************
 * HELPERS
 ***********************************************/

function addEvent(type, fn, bubble) {
    window.addEventListener(type, fn, (bubble||false));
}

function removeEvent(type, fn, bubble) {
    window.removeEventListener(type, fn, (bubble||false));
}

function isNodeName(el, tag) {
    return el.nodeName.toLowerCase() === tag.toLowerCase();
}

function directionCheck(x, y) {
    x = (x > 0) ? 1 : -1;
    y = (y > 0) ? 1 : -1;
    if (direction.x !== x || direction.y !== y) {
        que = [];
        direction.x = x;
        direction.y = y;
    }
}

var cache = {};
setInterval(function(){ cache = {}; }, 10 * 1000);

var uniqueID = (function() {
    var i = 0;
    return function (el) {
        return el.uniqueID || (el.uniqueID = i++);
    };
})();

function setCache(elems, overflowing) {
    for (var i = elems.length; i--;) {
        var uid = uniqueID(elems[i]);
        cache[uid] || (cache[uid] = overflowing);
    }
}

function overflowingAncestor(el) {
    var elems = [];
    var rootScrollHeight = root.scrollHeight;
    do {
        var cached = cache[uniqueID(el)];
        if (cached) {
            setCache(elems, cached);
            return cached;
        }

        elems.push(el);
        if (rootScrollHeight === el.scrollHeight) {
            if (!frame || root.clientHeight + 10 < rootScrollHeight) {
                setCache(elems, document.body);
                return document.body; // scrolling root in WebKit
            }
        }
        else if (el.clientHeight + 10 < el.scrollHeight) {
            overflow = getComputedStyle(el, "").getPropertyValue("overflow");
            if (overflow === "scroll" || overflow === "auto") {
                setCache(elems, el);
                return el;
            }
        }
    } while (el = el.parentNode);
}

/***********************************************
 * PULSE
 ***********************************************/

/**
 * Viscous fluid with a pulse for part and decay for the rest.
 * - Applies a fixed force over an interval (a damped acceleration), and
 * - Lets the exponential bleed away the velocity over a longer interval
 * - Michael Herf, http://stereopsis.com/stopping/
 */
function pulse_(x) {
    var val, start, expx;
    // test
    x = x * pulseScale;
    if (x < 1) { // acceleartion
        val = x - (1 - Math.exp(-x));
    } else {     // tail
        // the previous animation ended here:
        start = Math.exp(-1);
        // simple viscous drag
        x -= 1;
        expx = 1 - Math.exp(-x);
        val = start + (expx * (1 - start));
    }
    return val * pulseNormalize;
}

function computePulseScale() {
    pulseNormalize = 1 / pulse_(1);
}

/**
 * Viscous fluid with a pulse for part and decay for the rest
 */
function pulse(x) {
    if (x >= 1) return 1;
    if (x <= 0) return 0;

    if (pulseNormalize == 1) {
        computePulseScale();
    }
    return pulse_(x);
}

addEvent("mousedown", mousedown);
addEvent("mousewheel", wheel);
addEvent("load", init);
<commit_msg>some formatting<commit_after>
// SmoothScroll v0.9.8
// Licensed under the terms of the MIT license.

// People involved
// - Balazs Galambosi: maintainer (CHANGELOG.txt)
// - Patrick Brunner (patrickb1991@gmail.com)
// - Michael Herf: Pulse Algorithm

// Scroll Variables (tweakable)
var framerate = 150; // [Hz]
var animtime  = 400; // [px]
var stepsize  = 120; // [px]

// Pulse (less tweakable)
// ratio of "tail" to "acceleration"
var pulseAlgorithm = true;
var pulseScale     = 8;
var pulseNormalize = 1;

// Keyboard Settings
var keyboardsupport = true;
var disableKeyboard = false;
var arrowscroll     = 50; // [px]

// Excluded pages
var exclude = "";

// Other Variables
var frame = false;
var direction = { x: 0, y: 0 };
var initdone  = false;
var activeElement;
var root;

var key = { left: 37, up: 38, right: 39, down: 40, spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36 };

/***********************************************
 * SETTINGS
 ***********************************************/

chrome.extension.connect({ name: "smoothscroll" }).
onMessage.addListener(function (settings) {

    // NOTE: + converts to {Number}
    framerate = +settings.framerate;
    animtime  = +settings.animtime;
    stepsize  = +settings.scrollsz;
    exclude   = settings.exclude;
    pulseAlgorithm  = (settings.pulseAlgorithm == "true");
    pulseScale      = +settings.pulseScale;
    keyboardsupport = (settings.keyboardsupport == "true");
    arrowscroll     = +settings.arrscroll;

    computePulseScale();

    // it seems that sometimes settings come late
    // and we need to test again for excluded pages
    initTest();

    if (keyboardsupport && !disableKeyboard) {
        addEvent("keydown", keydown);
    }

    // If extension settings were deleted somehow
    if (!framerate) {
        alert("SmoothScroll: Please restart Chrome");
    }
});

/***********************************************
 * INITIALIZE
 ***********************************************/

/**
 * Tests if smooth scrolling is allowed. Shuts down everything if not.
 */
function initTest() {

    // disable keys for google reader (spacebar conflict)
    if (document.URL.indexOf("google.com/reader/view") > -1) {
        disableKeyboard = true;
    }

    // disable everything if the page is blacklisted
    if (exclude) {
        var domains = exclude.split(/[,\n] ?/);
        for (var i = domains.length; i--;) {
            if (document.URL.indexOf(domains[i]) > -1) {
                removeEvent("mousewheel", wheel);
                disableKeyboard = true;
                break;
            }
        }
    }

    // disable keyboard support if anything above requested it
    if (disableKeyboard) {
        removeEvent("keydown", keydown);
    }
}

/**
 * Sets up scrolls array, determines if frames are involved.
 */
function init() {

    if (!document.body) return;

    var body = document.body;
    var html = document.documentElement;
    var windowHeight = window.innerHeight;
    var scrollHeight = body.scrollHeight;

    // check compat mode for root element
    root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
    activeElement = body;

    initTest();
    initdone = true;

    // Checks if this script is running in a frame
    if (top != self) {
        frame = true;
    }

    /**
     * This fixes a bug where the areas left and right to
     * the content does not trigger the onmousewheel event
     * on some pages. e.g.: html, body { height: 100% }
     */
    else if (scrollHeight > windowHeight &&
            (body.offsetHeight <= windowHeight ||
             html.offsetHeight <= windowHeight)) {
        root.style.height = "auto";
    }

    if (document.URL.indexOf("mail.google.com") > -1) {
        var s = document.createElement("style");
        s.innerHTML = ".iu { visibility: hidden }";
        document.body.appendChild(s);
    }
}

/************************************************
 * SCROLLING
 ************************************************/

var que = [];
var pending = false;

/**
 * Pushes scroll actions to the scrolling queue.
 */
function scrollArray(elem, multiplyX, multiplyY, delay) {

    delay || (delay = 1000);
    directionCheck(multiplyX, multiplyY);

    // push a scroll command
    que.push({
        x: multiplyX,
        y: multiplyY,
        lastX: (multiplyX < 0) ? 0.99 : -0.99,
        lastY: (multiplyY < 0) ? 0.99 : -0.99,
        start: +new Date
    });

    // don't act if there's a pending queue
    if (pending) {
        return;
    }

    var step = function() {

        var now = +new Date;
        var totalX = 0;
        var totalY = 0;

        for (var i = 0; i < que.length; i++) {

            var item = que[i];
            var elapsed  = now - item.start;
            var finished = (elapsed >= animtime);

            // scroll position: [0, 1]
            var position = (finished) ? 1 : elapsed / animtime;

            // easing [optional]
            if (pulseAlgorithm) {
                position = pulse(position);
            }

            // only need the difference
            var scrollX = (item.x * position - item.lastX) >> 0;
            var scrollY = (item.y * position - item.lastY) >> 0;

            // add this to the total scrolling
            totalX += scrollX;
            totalY += scrollY;

            // update last values
            item.lastX += scrollX;
            item.lastY += scrollY;

            // delete and step back if it's over
            if (finished) {
                que.splice(i, 1); i--;
            }
        }

        // scroll left
        if (multiplyX) {
            var lastLeft = elem.scrollLeft;
            elem.scrollLeft += totalX;

            // scroll left failed (edge)
            if (totalX && elem.scrollLeft === lastLeft) {
                multiplyX = 0;
            }
        }

        // scroll top
        if (multiplyY) {
            var lastTop = elem.scrollTop;
            elem.scrollTop += totalY;

            // scroll top failed (edge)
            if (totalY && elem.scrollTop === lastTop) {
                multiplyY = 0;
            }
        }

        // clean up if there's nothing left to do
        if (!multiplyX && !multiplyY) {
            que = [];
        }

        if (que.length) {
            setTimeout(step, delay / framerate + 1);
        } else {
            pending = false;
        }
    }

    // start a new queue of actions
    setTimeout(step, 0);
    pending = true;
}

/***********************************************
 * EVENTS
 ***********************************************/

/**
 * Mouse wheel handler.
 * @param {Object} event
 */
function wheel(event) {

    if (!initdone) {
        init();
    }

    var target = event.target;
    var overflowing = overflowingAncestor(target);

    // use default if there's no overflowing
    // element or default action is prevented
    if (!overflowing || event.defaultPrevented ||
        isNodeName(activeElement, "embed") ||
       (isNodeName(target, "embed") && /\.pdf/i.test(target.src))) {
        return true;
    }

    var deltaX = event.wheelDeltaX || 0;
    var deltaY = event.wheelDeltaY || 0;

    // use wheelDelta if deltaX/Y is not available
    if (!deltaX && !deltaY) {
        deltaY = event.wheelDelta || 0;
    }

    // scale by step size
    // delta is 120 most of the time
    // synaptics seems to send 1 sometimes
    if (Math.abs(deltaX) > 1.2) {
        deltaX *= stepsize / 120;
    }
    if (Math.abs(deltaY) > 1.2) {
        deltaY *= stepsize / 120;
    }

    scrollArray(overflowing, -deltaX, -deltaY);
    event.preventDefault();
}

/**
 * Keydown event handler.
 * @param {Object} event
 */
function keydown(event) {

    var target   = event.target;
    var modifier = event.ctrlKey || event.altKey || event.metaKey;

    // do nothing if user is editing text
    // or using a modifier key (except shift)
    if ( /input|textarea|embed/i.test(target.nodeName) ||
         target.isContentEditable ||
         event.defaultPrevented   ||
         modifier ) {
      return true;
    }
    // spacebar should trigger button press
    if (isNodeName(target, "button") &&
        event.keyCode === key.spacebar) {
      return true;
    }

    var shift, x = 0, y = 0;
    var elem = overflowingAncestor(activeElement);
    var clientHeight = elem.clientHeight;

    if (elem == document.body) {
        clientHeight = window.innerHeight;
    }

    switch (event.keyCode) {
        case key.up:
            y = -arrowscroll;
            break;
        case key.down:
            y = arrowscroll;
            break;
        case key.spacebar: // (+ shift)
            shift = event.shiftKey ? 1 : -1;
            y = -shift * clientHeight * 0.9;
            break;
        case key.pageup:
            y = -clientHeight * 0.9;
            break;
        case key.pagedown:
            y = clientHeight * 0.9;
            break;
        case key.home:
            y = -elem.scrollTop;
            break;
        case key.end:
            var damt = elem.scrollHeight - elem.scrollTop - clientHeight;
            y = (damt > 0) ? damt+10 : 0;
            break;
        case key.left:
            x = -arrowscroll;
            break;
        case key.right:
            x = arrowscroll;
            break;
        default:
            return true; // a key we don't care about
    }

    scrollArray(elem, x, y);
    event.preventDefault();
}

/**
 * Mousedown event only for updating activeElement
 */
function mousedown(event) {
    activeElement = event.target;
}

/***********************************************
 * HELPERS
 ***********************************************/

function addEvent(type, fn, bubble) {
    window.addEventListener(type, fn, (bubble||false));
}

function removeEvent(type, fn, bubble) {
    window.removeEventListener(type, fn, (bubble||false));
}

function isNodeName(el, tag) {
    return el.nodeName.toLowerCase() === tag.toLowerCase();
}

function directionCheck(x, y) {
    x = (x > 0) ? 1 : -1;
    y = (y > 0) ? 1 : -1;
    if (direction.x !== x || direction.y !== y) {
        que = [];
        direction.x = x;
        direction.y = y;
    }
}

var cache = {};
setInterval(function(){ cache = {}; }, 10 * 1000);

var uniqueID = (function() {
    var i = 0;
    return function (el) {
        return el.uniqueID || (el.uniqueID = i++);
    };
})();

function setCache(elems, overflowing) {
    for (var i = elems.length; i--;) {
        var uid = uniqueID(elems[i]);
        cache[uid] || (cache[uid] = overflowing);
    }
}

function overflowingAncestor(el) {
    var elems = [];
    var rootScrollHeight = root.scrollHeight;
    do {
        var cached = cache[uniqueID(el)];
        if (cached) {
            setCache(elems, cached);
            return cached;
        }

        elems.push(el);
        if (rootScrollHeight === el.scrollHeight) {
            if (!frame || root.clientHeight + 10 < rootScrollHeight) {
                setCache(elems, document.body);
                return document.body; // scrolling root in WebKit
            }
        }
        else if (el.clientHeight + 10 < el.scrollHeight) {
            overflow = getComputedStyle(el, "").getPropertyValue("overflow");
            if (overflow === "scroll" || overflow === "auto") {
                setCache(elems, el);
                return el;
            }
        }
    } while (el = el.parentNode);
}

/***********************************************
 * PULSE
 ***********************************************/

/**
 * Viscous fluid with a pulse for part and decay for the rest.
 * - Applies a fixed force over an interval (a damped acceleration), and
 * - Lets the exponential bleed away the velocity over a longer interval
 * - Michael Herf, http://stereopsis.com/stopping/
 */
function pulse_(x) {
    var val, start, expx;
    // test
    x = x * pulseScale;
    if (x < 1) { // acceleartion
        val = x - (1 - Math.exp(-x));
    } else {     // tail
        // the previous animation ended here:
        start = Math.exp(-1);
        // simple viscous drag
        x -= 1;
        expx = 1 - Math.exp(-x);
        val = start + (expx * (1 - start));
    }
    return val * pulseNormalize;
}

function computePulseScale() {
    pulseNormalize = 1 / pulse_(1);
}

/**
 * Viscous fluid with a pulse for part and decay for the rest
 */
function pulse(x) {
    if (x >= 1) return 1;
    if (x <= 0) return 0;

    if (pulseNormalize == 1) {
        computePulseScale();
    }
    return pulse_(x);
}

addEvent("mousedown", mousedown);
addEvent("mousewheel", wheel);
addEvent("load", init);
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.comics',
  name: 'DAOControllerView',
  extends: 'foam.u2.Element',

  imports: [
    'stack'
  ],

  exports: [
    'editRecord'
  ],

  requires: [
    'foam.comics.DAOController'
  ],

  properties: [
    'data',
    'of',
    {
      name: 'controller',
      factory: function() {
        return this.DAOController.create({
          of$: this.of$,
          data$: this.data$
        });
      }
    }
  ],

  methods: [
    function editRecord(obj) {
      this.stack.push({
        class: 'foam.comics.DAOUpdateControllerView',
        of: this.of,
        data: obj.id
      });
    },
    function initE() {
      this.startContext({ data: this.controller }).
        start('table').
          start('tr').
            start('td').add(this.DAOController.PREDICATE).end().
            start('td').style({ 'vertical-align': 'top' }).add(this.DAOController.FILTERED_DAO).end().
          end().
          start('tr').start('td').end().start('td').add(this.DAOController.CREATE).end().
        end().
        endContext();
    }
  ]
});
<commit_msg>Add a scroller to the DAOControllerView but it's not very polished yet.<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.comics',
  name: 'DAOControllerView',
  extends: 'foam.u2.Element',

  imports: [
    'stack'
  ],

  exports: [
    'editRecord'
  ],

  requires: [
    'foam.graphics.ScrollCView',
    'foam.mlang.sink.Count',
    'foam.comics.DAOController',
  ],

  properties: [
    'data',
    'of',
    {
      name: 'controller',
      factory: function() {
        return this.DAOController.create({
          of$: this.of$,
          data$: this.data$
        });
      }
    },
    {
      name: 'countSink',
      expression: function(data) {
        // TODO detach the previous sink.
        var c = this.Count.create();
        data.pipe(c);
        return c;
      }
    },
    {
      name: 'scroller',
      factory: function() {
        return this.ScrollCView.create({
          size$: this.countSink$.dot('value'),
          value$: this.controller$.dot('skip'),
          extent$: this.controller$.dot('limit'), // TODO find out how many fit.
          height: 600, // TODO use window height.
          width: 40,
          handleSize: 40,
          // TODO wire up mouse wheel
          // TODO clicking away from scroller should deselect it.
        });
      }
    }
  ],

  methods: [
    function editRecord(obj) {
      this.stack.push({
        class: 'foam.comics.DAOUpdateControllerView',
        of: this.of,
        data: obj.id
      });
    },
    function initE() {
      this.startContext({ data: this.controller }).
        start('table').
          start('tr').
            start('td').add(this.DAOController.PREDICATE).end().
            start('td').style({ 'vertical-align': 'top', 'width': '100%' }).add(this.DAOController.FILTERED_DAO).end().
            start('td').add(this.scroller).end().
          end().
          start('tr').
            start('td').end().
            start('td').add(this.DAOController.CREATE).end().
        end().
        endContext();
    }
  ]
});
<|endoftext|>


<commit_before>import MdcButton from 'src/Button.vue'
import {
  createVM,
  dataPropagationTest,
  attrTest,
} from '../helpers'

describe('Button.vue', function () {
  it('renders an upgraded button', function () {
    const vm = createVM(this, `
<MdcButton class="my-button">Click me</MdcButton>
`, {
  components: { MdcButton },
})
    vm.$('button').should.have.text('Click me')
    vm.$('button').should.have.class('mdc-button')
  })

  it('keeps original tag data', dataPropagationTest(MdcButton))

  describe('attrs', function () {
    attrTest(it, 'mdc-button', MdcButton, [
      'dense',
      'raised',
      'compact',
      'primary',
      'accent',
      'theme-dark',
    ])
  })
})
<commit_msg>🔨 Button: refactor test<commit_after>import Button from 'src/Button.vue'
import {
  createVM,
  dataPropagationTest,
  attrTest,
} from '../helpers'

describe('Button.vue', function () {
  it('renders an upgraded button', function () {
    const vm = createVM(this, `
<Button class="my-button">Click me</Button>
`, {
  components: { Button },
})
    vm.$('button').should.have.text('Click me')
    vm.$('button').should.have.class('mdc-button')
  })

  it('keeps original tag data', dataPropagationTest(Button))

  describe('attrs', function () {
    attrTest(it, 'mdc-button', Button, [
      'dense',
      'raised',
      'compact',
      'primary',
      'accent',
      'theme-dark',
    ])
  })
})
<|endoftext|>


<commit_before>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

define('providers/edit', [
    'providers',
	'jquery',
	'gizmo/superdesk',
	config.guiJs('livedesk', 'models/posttype'),
	config.guiJs('livedesk', 'models/post'),
    'jquery/utils',
    'jquery/rest',
    'jquery/superdesk',
    'jquery/tmpl',
    'jquery/avatar',
	'jqueryui/draggable',
    'jqueryui/texteditor',
    'tmpl!livedesk>providers/edit',
    'tmpl!livedesk>providers/edit/item',
], function( providers, $, Gizmo, PostType, Post ) {
	var OwnCollection = Gizmo.Collection.extend({
		insertFrom: function(model) {
			this.desynced = false;
			if( !(model instanceof Gizmo.Model) ) model = Gizmo.Auth(new this.model(model));
			this._list.push(model);
			model.hash();
			var x = model.sync(model.url.root(this.options.theBlog).xfilter(this._xfilter));
			x.done(function(data) {
				model.set(data,{ silent: true}).clearChangeset();
			});
			x.model = model;
			return x;
		}
	}),
	PostView = Gizmo.View.extend({
		events: {
			"": { "dragstart": "adaptor"}
		},
		init: function(){
			var self = this;
			self.model
				.on('read', function(){
					self.render();
				})
				.on('set', function(evt, data){
					if(self.model.updater !== self) {
						self.rerender();
					}
				})
				.on('update', function(evt, data){
					/**
					 * if the updater on the model is the current view don't update the view;
					 */
					if(self.model.updater === self) {
						delete self.model.updater; return;
					}
					/**
					 * if the Change Id is received, then sync the hole model
					 */
					self.rerender();
				})
				.on('delete', this.remove, this);
		},
		rerender: function(){
			var self = this;
			self.el.fadeTo(500, '0.1', function(){
				self.render().el.fadeTo(500, '1');
			});
		},
		render: function(){
			var avatar = $.avatar.get($.superdesk.login.EMail);
			var self = this;
			if(!(this.model instanceof Gizmo.Register.Post))
				this.model = Gizmo.Auth(new Gizmo.Register.Post(this.model));
			$.tmpl('livedesk>providers/edit/item', { Post: this.model.feed(),Avatar: avatar} , function(err, out){
				self.setElement( out );
				if(!self.model.get('PublishedOn')) {
							self.el.draggable({
								revert: 'invalid',
								containment:'document',
								helper: 'clone',
								appendTo: 'body',
								zIndex: 2700
							});
				}
				self.resetEvents();
			});
			return this;
		},
		remove: function(){
			var self = this;
			self.el.fadeTo(500, '0.1', function(){
				self.el.remove();
			});
		},
		adaptor: function(evt){
			$(evt.target).parents('li').data("post", this.model);
		}
	}),
	PostsView = Gizmo.View.extend({
		init: function(){
			var self = this;
			this.posts.on('read', this.render, this);
			this.posts.model.on('insert', function(evt, model){
				self.addOne(model);
			});
			this.posts.sync();
		},
		render: function(evt, data){
			if ( data === undefined)
				data = this.posts._list;
			for(var len = data.length, i = 0; i < len; i++ )
				this.addOne(data[i]);
		},
		addOne: function(model)
		{
			var view = new PostView({model: model, _parent: this});
			this.el.prepend(view.render().el);
		},
		save: function(model)
		{
			var self = this;
			this.posts.insertFrom(model);
		},
		savepost: function(model)
		{
			var self = this,
			drd = this.posts.insertFrom(model);
			drd.done(function(){
				drd.model.publishSync();
			});
		}
	}),
	EditView = Gizmo.View.extend({
		postView: null,
		events: {
			'[ci="savepost"]': { 'click': 'savepost'},
			'[ci="save"]': { 'click': 'save'}
		},
		init: function()
		{
			var self = this,
			    PostTypes = Gizmo.Collection.extend({model: PostType});

			self.theBlog = self.blogUrl;

			self.postTypes = Gizmo.Auth(new PostTypes(self.blogUrl+'/../../../../Superdesk/PostType'));

			self.postTypes.on('read', function(){ self.render(); }).xfilter('Key').sync();

		},
		render: function(){
			var self = this;
			this.el.tmpl('livedesk>providers/edit', { PostTypes: this.postTypes.feed() }, function(){
				// editor
				fixedToolbar =
				{
					_create: function(elements)
					{
						var self = this;
						$(elements).on('toolbar-created', function()
						{
							self.plugins.toolbar.element.hide()
								.appendTo($('.edit-block .toolbar-placeholder'));
						});
						$(elements).on('focusin.texteditor keydown.texteditor click.texteditor', function(event)
						{
							self.plugins.toolbar.element.fadeIn('fast');
						});
						$(elements).on('blur.texteditor focusout.texteditor', function()
						{
							self.plugins.toolbar.element.fadeOut('fast');
						});
					}
				};
				self.el.find('.edit-block article.editable').texteditor({ plugins:
				{
					floatingToolbar: null,
					draggableToolbar: null,
					fixedToolbar: fixedToolbar
				}});
				var posts = Gizmo.Auth(new OwnCollection(
						self.theBlog+ '/Post/Owned?X-Filter=Id,AuthorName,Content,Type.Key,PublishedOn,CreatedOn,Author.Source.Name',
						Gizmo.Register.Post,
						{ theBlog: self.theBlog}
					));
				posts._xfilter = 'Id,AuthorName,Content,Type.Key,PublishedOn,CreatedOn,Author.Source.Name';
				posts.xfilter(posts._xfilter);
				self.postsView = new PostsView({ el: $(this).find('#own-posts-results'), posts: posts, _parent: self});
			} );
		},
		clear: function()
		{
			this.el.find('[name="type"]').val('normal');
		},
		savepost: function(evt){
			evt.preventDefault();
			var data = {
				Content: $.styledNodeHtml(this.el.find('.edit-block article.editable')),
				Type: this.el.find('[name="type"]').val()
			};
			this.postsView.savepost(data);
		},
		save: function(evt){
			evt.preventDefault();
			var data = {
				Content: $.styledNodeHtml(this.el.find('.edit-block article.editable')),
				Type: this.el.find('[name="type"]').val()
			};
			this.postsView.save(data);
		}
	});
	var editView = null;
    $.extend( providers.edit, { init: function(blogUrl)
    {
        editView = new EditView({ el: this.el, blogUrl: blogUrl });
        this.init = $.noop;
    }});
	return providers;
});<commit_msg>NR-250: Latest fixes for 1.0 beta release<commit_after>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

define('providers/edit', [
    'providers',
	'jquery',
	'gizmo/superdesk',
	config.guiJs('livedesk', 'models/posttype'),
	config.guiJs('livedesk', 'models/post'),
    'jquery/utils',
    'jquery/rest',
    'jquery/superdesk',
    'jquery/tmpl',
    'jquery/avatar',
	'jqueryui/draggable',
    'jqueryui/texteditor',
    'tmpl!livedesk>providers/edit',
    'tmpl!livedesk>providers/edit/item',
], function( providers, $, Gizmo, PostType, Post ) {
	var OwnCollection = Gizmo.Collection.extend({
		insertFrom: function(model) {
			this.desynced = false;
			if( !(model instanceof Gizmo.Model) ) model = Gizmo.Auth(new this.model(model));
			this._list.push(model);
			model.hash();
			var x = model.sync(model.url.root(this.options.theBlog).xfilter(this._xfilter));
			x.done(function(data) {
				model.set(data,{ silent: true}).clearChangeset();
			});
			x.model = model;
			return x;
		}
	}),
	PostView = Gizmo.View.extend({
		events: {
			"": { "dragstart": "adaptor"}
		},
		init: function(){
			var self = this;
			self.model
				.on('read', function(){
					self.render();
				})
				.on('set', function(evt, data){
					if(self.model.updater !== self) {
						self.rerender();
					}
				})
				.on('update', function(evt, data){
					/**
					 * if the updater on the model is the current view don't update the view;
					 */
					if(self.model.updater === self) {
						delete self.model.updater; return;
					}
					/**
					 * if the Change Id is received, then sync the hole model
					 */
					self.rerender();
				})
				.on('delete', this.remove, this);
		},
		rerender: function(){
			var self = this;
			self.el.fadeTo(500, '0.1', function(){
				self.render().el.fadeTo(500, '1');
			});
		},
		render: function(){
			var avatar = $.avatar.get($.superdesk.login.EMail);
			var self = this;
			if(!(this.model instanceof Gizmo.Register.Post))
				this.model = Gizmo.Auth(new Gizmo.Register.Post(this.model));
			$.tmpl('livedesk>providers/edit/item', { Post: this.model.feed(),Avatar: avatar} , function(err, out){
				self.setElement( out );
				if(!self.model.get('PublishedOn')) {
							self.el.draggable({
								revert: 'invalid',
								containment:'document',
								helper: 'clone',
								appendTo: 'body',
								zIndex: 2700
							});
				}
				self.resetEvents();
			});
			return this;
		},
		remove: function(){
			var self = this;
			self.el.fadeTo(500, '0.1', function(){
				self.el.remove();
			});
		},
		adaptor: function(evt){
			$(evt.target).parents('li').data("post", this.model);
		}
	}),
	PostsView = Gizmo.View.extend({
		init: function(){
			var self = this;
			this.posts.on('read', this.render, this);
			this.posts.model.on('insert', function(evt, model){
				self.addOne(model);
			});
			this.posts.sync();
		},
		render: function(evt, data){
			if ( data === undefined)
				data = this.posts._list;
			for(var len = data.length, i = 0; i < len; i++ ) {
				this.addOne(data[i]);
			}
		},
		addOne: function(model)
		{
			var view = new PostView({model: model, _parent: this});
			this.el.prepend(view.render().el);
		},
		save: function(model)
		{
			var self = this;
			this.posts.insertFrom(model);
		},
		savepost: function(model)
		{
			var self = this,
			drd = this.posts.insertFrom(model);
			drd.done(function(){
				drd.model.publishSync();
			});
		}
	}),
	EditView = Gizmo.View.extend({
		postView: null,
		events: {
			'[ci="savepost"]': { 'click': 'savepost'},
			'[ci="save"]': { 'click': 'save'}
		},
		init: function()
		{
			var self = this,
			    PostTypes = Gizmo.Collection.extend({model: PostType});

			self.theBlog = self.blogUrl;

			self.postTypes = Gizmo.Auth(new PostTypes(self.blogUrl+'/../../../../Superdesk/PostType'));

			self.postTypes.on('read', function(){ self.render(); }).xfilter('Key').sync();

		},
		render: function(){
			var self = this;
			this.el.tmpl('livedesk>providers/edit', { PostTypes: this.postTypes.feed() }, function(){
				// editor
				fixedToolbar =
				{
					_create: function(elements)
					{
						var self = this;
						$(elements).on('toolbar-created', function()
						{
							self.plugins.toolbar.element.hide()
								.appendTo($('.edit-block .toolbar-placeholder'));
						});
						$(elements).on('focusin.texteditor keydown.texteditor click.texteditor', function(event)
						{
							self.plugins.toolbar.element.fadeIn('fast');
						});
						$(elements).on('blur.texteditor focusout.texteditor', function()
						{
							self.plugins.toolbar.element.fadeOut('fast');
						});
					}
				};
				self.el.find('.edit-block article.editable').texteditor({ plugins:
				{
					floatingToolbar: null,
					draggableToolbar: null,
					fixedToolbar: fixedToolbar
				}});
				var posts = Gizmo.Auth(new OwnCollection(
						self.theBlog+ '/Post/Owned?asc=createdOn',
						Gizmo.Register.Post,
						{ theBlog: self.theBlog}
					));
				posts._xfilter = 'Id,AuthorName,Content,Type.Key,PublishedOn,CreatedOn,Author.Source.Name';
				//posts.asc('createdOn');
				posts.xfilter(posts._xfilter);
				self.postsView = new PostsView({ el: $(this).find('#own-posts-results'), posts: posts, _parent: self});
			} );
		},
		clear: function()
		{
			this.el.find('[name="type"]').val('normal');
		},
		savepost: function(evt){
			evt.preventDefault();
			var data = {
				Content: $.styledNodeHtml(this.el.find('.edit-block article.editable')),
				Type: this.el.find('[name="type"]').val()
			};
			this.postsView.savepost(data);
		},
		save: function(evt){
			evt.preventDefault();
			var data = {
				Content: $.styledNodeHtml(this.el.find('.edit-block article.editable')),
				Type: this.el.find('[name="type"]').val()
			};
			this.postsView.save(data);
		}
	});
	var editView = null;
    $.extend( providers.edit, { init: function(blogUrl)
    {
        editView = new EditView({ el: this.el, blogUrl: blogUrl });
        this.init = $.noop;
    }});
	return providers;
});<|endoftext|>


<commit_before>import React from 'react';
import cx from 'classnames';
import { VelocityComponent, VelocityTransitionGroup } from 'velocity-react';

const BUILD_TOOL_SVG = (
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 50 50">
    <path d="M 14.34375 1.9375 C 13.87175 1.9375 13.44275 2.289 13.34375 2.75 C 13.24475 3.213 13.5045 3.684 13.9375 3.875 C 13.9755 3.892 17.795 5.61325 22.25 10.28125 C 24.248 12.37425 26.3485 15.14825 24.9375 16.53125 C 24.9045 16.56725 24.74 16.759 24.125 17.375 C 24.068 17.432 24.048 17.47525 24 17.53125 L 23.96875 17.5625 L 31.625 25.21875 L 32.34375 24.53125 C 32.35875 24.51725 32.362 24.48375 32.375 24.46875 L 33.5 23.34375 C 33.551 23.39375 33.82725 23.671 34.90625 24.75 C 35.31225 25.156 35.21675 26.16125 35.09375 26.40625 C 34.71875 27.15725 34.79025 27.9465 35.28125 28.4375 C 35.47625 28.6335 38.75 31.90625 38.75 31.90625 C 39.289 32.44525 40.00875 32.71875 40.71875 32.71875 C 41.42775 32.71875 42.11525 32.44525 42.65625 31.90625 L 48.1875 26.40625 C 49.2685 25.32625 49.2675 23.54975 48.1875 22.46875 L 44.71875 19 C 44.21475 18.495 43.4575 18.38975 42.8125 18.71875 C 42.5095 18.87275 41.4395 18.97175 41.0625 18.59375 L 39.625 17.15625 C 39.952 16.69025 40.128 16.1395 40.125 15.5625 C 40.128 14.8155 39.8385 14.11675 39.3125 13.59375 L 35.5625 9.84375 C 35.5415 9.82275 35.4755 9.781 35.4375 9.75 L 34.65625 8.96875 C 33.02225 7.33475 31.513 5.79225 31.125 5.40625 C 27.811 2.09025 15.71175 1.9415 14.34375 1.9375 z M 22.5625 18.96875 L 3 38.53125 C 1.921 39.60925 1.921 41.3545 3 42.4375 L 6.75 46.1875 C 7.29 46.7275 8.00975 47 8.71875 47 C 9.42775 47 10.11625 46.7275 10.65625 46.1875 L 30.21875 26.65625 L 22.5625 18.96875 z"></path>
  </svg>
);

class BuildTool extends React.Component {
  render() {
    const props = this.props;
    let description;

    switch (props.buildTool) {
      case 'gulp':
        description = (

            Gulp — The streaming build system.
             Browserify — Lets you require('modules') in
            the browser by bundling up all of your dependencies.

        );
        break;
      case 'webpack':
        description = (

            Webpack — A module bundler that
            bundles javascript and other assets for the browser.

        );
        break;
      case 'npm':
        description = (

            NPM
            — A build process using only npm and your package.json file.

        );
        break;
      default:
        description =

;
    }

    let reactNote;

    if (props.buildTool && props.buildTool === 'none' && props.jsFramework === 'react') {
      reactNote = (

          React server-side rendering is enabled only when using a build tool.

      );
    }

    let cssPreprocessorNote;

    if (props.buildTool && props.buildTool === 'none') {
      switch (props.cssPreprocessor) {
        case 'sass':
          cssPreprocessorNote = (

              Sass will be compiled via node-sass-middleware package.

          );
          break;
        case 'less':
          cssPreprocessorNote = (

              LESS will be compiled via less.js-middleware package.

          );
          break;
        case 'postcss':
          cssPreprocessorNote = (

              PostCSS will be compiled via postcss-middleware package.

          );
          break;
      }
    }

    const recommended = props.beginner ? (

    ) : null;

    return (

{BUILD_TOOL_SVG}{!props.buildTool || props.buildTool === 'none' ? 'Build Tool' : props.buildTool}

          {description}

              <input type="radio" name="buildToolRadios" value="none" onChange={props.handleChange} checked={props.buildTool === 'none'}/>
              None

              <input type="radio" name="buildToolRadios" value="gulp" onChange={props.handleChange} checked={props.buildTool === 'gulp'}/>
              Gulp + Browserify

              <input type="radio" name="buildToolRadios" value="webpack" onChange={props.handleChange} checked={props.buildTool === 'webpack'}/>
              Webpack

              <input type="radio" name="buildToolRadios" value="npm" onChange={props.handleChange} checked={props.buildTool === 'npm'}/>
              NPM
              {recommended}

          <VelocityTransitionGroup enter={{ animation: 'transition.slideRightIn' }}>{reactNote}</VelocityTransitionGroup>
          <VelocityTransitionGroup enter={{ animation: 'transition.slideLeftIn' }}>{cssPreprocessorNote}</VelocityTransitionGroup>

    );
  }
}

export default BuildTool;
<commit_msg>Do not show None choice for build tool with js framework enabled<commit_after>import React from 'react';
import cx from 'classnames';
import { VelocityComponent, VelocityTransitionGroup } from 'velocity-react';

const BUILD_TOOL_SVG = (
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 50 50">
    <path d="M 14.34375 1.9375 C 13.87175 1.9375 13.44275 2.289 13.34375 2.75 C 13.24475 3.213 13.5045 3.684 13.9375 3.875 C 13.9755 3.892 17.795 5.61325 22.25 10.28125 C 24.248 12.37425 26.3485 15.14825 24.9375 16.53125 C 24.9045 16.56725 24.74 16.759 24.125 17.375 C 24.068 17.432 24.048 17.47525 24 17.53125 L 23.96875 17.5625 L 31.625 25.21875 L 32.34375 24.53125 C 32.35875 24.51725 32.362 24.48375 32.375 24.46875 L 33.5 23.34375 C 33.551 23.39375 33.82725 23.671 34.90625 24.75 C 35.31225 25.156 35.21675 26.16125 35.09375 26.40625 C 34.71875 27.15725 34.79025 27.9465 35.28125 28.4375 C 35.47625 28.6335 38.75 31.90625 38.75 31.90625 C 39.289 32.44525 40.00875 32.71875 40.71875 32.71875 C 41.42775 32.71875 42.11525 32.44525 42.65625 31.90625 L 48.1875 26.40625 C 49.2685 25.32625 49.2675 23.54975 48.1875 22.46875 L 44.71875 19 C 44.21475 18.495 43.4575 18.38975 42.8125 18.71875 C 42.5095 18.87275 41.4395 18.97175 41.0625 18.59375 L 39.625 17.15625 C 39.952 16.69025 40.128 16.1395 40.125 15.5625 C 40.128 14.8155 39.8385 14.11675 39.3125 13.59375 L 35.5625 9.84375 C 35.5415 9.82275 35.4755 9.781 35.4375 9.75 L 34.65625 8.96875 C 33.02225 7.33475 31.513 5.79225 31.125 5.40625 C 27.811 2.09025 15.71175 1.9415 14.34375 1.9375 z M 22.5625 18.96875 L 3 38.53125 C 1.921 39.60925 1.921 41.3545 3 42.4375 L 6.75 46.1875 C 7.29 46.7275 8.00975 47 8.71875 47 C 9.42775 47 10.11625 46.7275 10.65625 46.1875 L 30.21875 26.65625 L 22.5625 18.96875 z"></path>
  </svg>
);

class BuildTool extends React.Component {
  render() {
    const props = this.props;
    let description;

    switch (props.buildTool) {
      case 'gulp':
        description = (

            Gulp — The streaming build system.
             Browserify — Lets you require('modules') in
            the browser by bundling up all of your dependencies.

        );
        break;
      case 'webpack':
        description = (

            Webpack — A module bundler that
            bundles javascript and other assets for the browser.

        );
        break;
      case 'npm':
        description = (

            NPM
            — A build process using only npm and your package.json file.

        );
        break;
      default:
        description =

;
    }

    let reactNote;

    if (props.buildTool && props.buildTool === 'none' && props.jsFramework === 'react') {
      reactNote = (

          React server-side rendering is enabled only when using a build tool.

      );
    }

    let cssPreprocessorNote;

    if (props.buildTool && props.buildTool === 'none') {
      switch (props.cssPreprocessor) {
        case 'sass':
          cssPreprocessorNote = (

              Sass will be compiled via node-sass-middleware package.

          );
          break;
        case 'less':
          cssPreprocessorNote = (

              LESS will be compiled via less.js-middleware package.

          );
          break;
        case 'postcss':
          cssPreprocessorNote = (

              PostCSS will be compiled via postcss-middleware package.

          );
          break;
      }
    }

    const recommended = props.beginner ? (

    ) : null;

    const noneRadio = props.jsFramework === 'none' ? (

        <input type="radio" name="buildToolRadios" value="none" onChange={props.handleChange} checked={props.buildTool === 'none'}/>
        None

    ) : null;

    return (

{BUILD_TOOL_SVG}{!props.buildTool || props.buildTool === 'none' ? 'Build Tool' : props.buildTool}

          {description}

            {noneRadio}

              <input type="radio" name="buildToolRadios" value="gulp" onChange={props.handleChange} checked={props.buildTool === 'gulp'}/>
              Gulp + Browserify

              <input type="radio" name="buildToolRadios" value="webpack" onChange={props.handleChange} checked={props.buildTool === 'webpack'}/>
              Webpack

              <input type="radio" name="buildToolRadios" value="npm" onChange={props.handleChange} checked={props.buildTool === 'npm'}/>
              NPM
              {recommended}

          <VelocityTransitionGroup enter={{ animation: 'transition.slideRightIn' }}>{reactNote}</VelocityTransitionGroup>
          <VelocityTransitionGroup enter={{ animation: 'transition.slideLeftIn' }}>{cssPreprocessorNote}</VelocityTransitionGroup>

    );
  }
}

export default BuildTool;
<|endoftext|>


<commit_before>var iphone4sLCD =      55;
var iphone5sGlass =    45;
var iphone5sLCD =      60;
var iphone6Glass =     49;
var iphone6LCD =       60;
var iphone6PlusGlass = 60;
var iphone6PlusLCD =   69;
var iphone6SGlass =    60;
var iphone6SLCD =      79;
var iphone6SPlusGlass= 70;
var iphone6SPlusLCD =  85;
var iphone7Glass =     79;
var iphone7LCD =       89;
var iphone7PlusGlass = 95;
var iphone7PlusLCD =   99;
var iphone8Glass =     99;
var iphone8LCD =       110;
var iphone8PlusGlass = 109;
var iphone8PlusLCD =   120;
var ipad1Glass =       90;
var ipad234Glass =     85;
var ipadMiniGlass =    75;
var ipadAirGlass =     90;
var ipadAir2Glass =    195;
var ipadAir2LCD =      230;

//Samsung
var galaxyS3LCD =      80;
var galaxyS4Glass =    45;
var galaxyS4LCD =      90;
var galaxyS5Glass =    95;
var galaxyS5LCD =      135;
var galaxyS5NeoGlass =  85;
var galaxyS5NeoLCD =   155;
var galaxyS6Glass =    109;
var galaxyS6LCD =      180;
var galaxyS6EdgeLCD =  175;
var galaxyS7Glass =    120;
var galaxyS7LCD =      210;
var galaxyS7EdgeLCD =  300;
var galaxyS8Glass =    205;
var galaxyS8LCD =      255;
var galaxyS8PGlass =   240;
var galaxyS8PLCD =     335;
var galaxyNote3Glass = 80;
var galaxyNote3LCD =   160;
var galaxyNote4Glass = 85;
var galaxyNote4LCD =   175;
var galaxyNote5Glass = 130;
var galaxyNote5LCD =   215;
//LG
var nexus4Glass =     75;
var nexus4LCD =       85;
var nexus5LCD =       65;
var nexus5XLCD =      75;
var LGG2Glass =       85;
var LGG2LCD =         95;
var LGG3Glass =       75;
var LGG3LCD =         80;
var LGG4LCD =         75;
var LGG5LCD =         80;
var V20Glass =        135;
var V20LCD =          140;

//OnePlus
var OnePlus1LCD =  100;
var OnePlus2LCD = 110;
var OnePlus3LCD =  175;
var OnePlus5LCD = 210;
var OnePlus5TLCD = 175;
var OnePlusXLCD = 215;
var OnePlus6LCD = 230;

//Sony
var Z3LCD =            100;
var Z4LCD =            115;
var Z5LCD =            120;

//Blackberry
var bbZ10 =            45;
var bbZ30 =            105;
var bbQ10 =            50;
var bbQ20 =            80;
var bbQ30 =            130;

//HTC
var M9LCD =            100;
var M10LCD =           120;

//Huawei
var mate7LCD =         110;
var mate8LCD =         110;
var mate9LCD =         120;
var p9LCD =            115;
var p10LCD =           130;
var huaweiG7LCD =      95;
var p20LCD=            220;
var p20PROLCD=         360;
var p20LiteLCD =       140;

//Pixel
var pixelLCD =         150;
var pixelXLLCD =       180;
var pixel2LCD =        225;
var pixel2XLLCD =      240;
var Nexus6LCD =        190;
var Nexus6PLCD =       220;

//Services
var diagnostic =         25;
var waterDamageL =       65;
var waterDamageH =       75;
var waterDamageSamsung = 75;
var advancedDiagnostic = 60;
var systemRestore =      50;
<commit_msg>Update prices.js<commit_after>var iphone4sLCD =      55;
var iphone5sGlass =    45;
var iphone5sLCD =      60;
var iphone6Glass =     49;
var iphone6LCD =       60;
var iphone6PlusGlass = 60;
var iphone6PlusLCD =   69;
var iphone6SGlass =    60;
var iphone6SLCD =      79;
var iphone6SPlusGlass= 70;
var iphone6SPlusLCD =  85;
var iphone7Glass =     79;
var iphone7LCD =       89;
var iphone7PlusGlass = 95;
var iphone7PlusLCD =   99;
var iphone8Glass =     99;
var iphone8LCD =       110;
var iphone8PlusGlass = 109;
var iphone8PlusLCD =   120;
var ipad1Glass =       90;
var ipad234Glass =     85;
var ipadMiniGlass =    75;
var ipadAirGlass =     90;
var ipadAir2Glass =    195;
var ipadAir2LCD =      230;

//Samsung
var galaxyS3LCD =      80;
var galaxyS4Glass =    45;
var galaxyS4LCD =      90;
var galaxyS5Glass =    95;
var galaxyS5LCD =      135;
var galaxyS5NeoGlass =  85;
var galaxyS5NeoLCD =   155;
var galaxyS6Glass =    109;
var galaxyS6LCD =      180;
var galaxyS6EdgeLCD =  175;
var galaxyS7Glass =    120;
var galaxyS7LCD =      210;
var galaxyS7EdgeLCD =  300;
var galaxyS8Glass =    205;
var galaxyS8LCD =      255;
var galaxyS8PGlass =   240;
var galaxyS8PLCD =     335;
var galaxyNote3Glass = 80;
var galaxyNote3LCD =   160;
var galaxyNote4Glass = 85;
var galaxyNote4LCD =   175;
var galaxyNote5Glass = 130;
var galaxyNote5LCD =   215;
//LG
var nexus4Glass =     75;
var nexus4LCD =       85;
var nexus5LCD =       65;
var nexus5XLCD =      75;
var LGG2Glass =       85;
var LGG2LCD =         95;
var LGG3Glass =       75;
var LGG3LCD =         80;
var LGG4LCD =         75;
var LGG5LCD =         80;
var V20Glass =        135;
var V20LCD =          140;

//OnePlus
var OnePlus1LCD =  100;
var OnePlus2LCD = 110;
var OnePlus3LCD =  175;
var OnePlus5LCD = 210;
var OnePlus5TLCD = 175;
var OnePlusXLCD = 215;
var OnePlus6LCD = 230;

//Sony
var Z3LCD =            100;
var Z4LCD =            115;
var Z5LCD =            120;

//Blackberry
var bbZ10 =            45;
var bbZ30 =            105;
var bbQ10 =            50;
var bbQ20 =            80;
var bbQ30 =            130;

//HTC
var M9LCD =            100;
var M10LCD =           120;

//Huawei
var mate7LCD =         105;
var mate8LCD =         110;
var mate9LCD =         115;
var p9LCD =            105;
var p10LCD =           125;
var huaweiG7LCD =      105;
var p20LCD=            210;
var p20PROLCD=         370;
var p20LiteLCD =       135;

//Pixel
var pixelLCD =         140;
var pixelXLLCD =       160;
var pixel2LCD =        210;
var pixel2XLLCD =      225;
var Nexus6LCD =        190;
var Nexus6PLCD =       170;

//Services
var diagnostic =         25;
var waterDamageL =       65;
var waterDamageH =       75;
var waterDamageSamsung = 75;
var advancedDiagnostic = 60;
var systemRestore =      50;
<|endoftext|>


<commit_before>// Geometry-related functions, for the hex and isometric grids.

function toTileNum(position) {
	return position.y * 200 + position.x
}

function fromTileNum(tile) {
	return {x: tile % 200, y: Math.floor(tile / 200)}
}

function tileToScreen(x, y) {
	x = 99 - x // this algorithm expects x to be reversed
	var sx = 4752 + (32 * y) - (48 * x)
	var sy = (24 * y) + (12 * x)

   return {x: sx, y: sy}
}

function tileFromScreen(x, y) {
	var off_x = -4800 + x
	var off_y = y
	var xx = off_x - off_y * 4 / 3
	var tx = xx / 64

	if (xx >= 0) tx++
	tx = -tx
	var yy = off_y + off_x / 4
	var ty = yy / 32
	if (yy < 0) ty--

	return {x: 99 - Math.round(tx), y: Math.round(ty)}
}

function hexToScreen(x, y) {
	var sx = 4816 - ((((x + 1) >> 1) << 5) + ((x >> 1) << 4) - (y << 4))
	var sy = ((12 * (x >> 1)) + (y * 12)) + 11

	return {x: sx, y: sy}
}

function hexFromScreen(x, y) {
	var x0 = 4800
	var y0 = 0
	var nx, ny

	if (x - x0 < 0)
		nx = (x - x0 + 1) / 16 - 1
	else
		nx = (x - x0) / 16

	if (y - y0 < 0)
		ny = (y - y0 + 1) / 12 - 1
	else
		ny = (y - y0) / 12

	if (Math.abs(nx) % 2 != Math.abs(ny) % 2)
		nx--;

	var xhBase = x0 + 16 * nx
	var yhBase = y0 + 12 * ny

	var hx = (4 * (yhBase - y0) - 3 * (xhBase - x0)) / 96
	var hy = (yhBase - y0) / 12 - hx / 2

	var dx = x - xhBase
	var dy = y - yhBase

	switch(dy)
	{
	  case 0:
	     if (dx < 12)
	     {
	    hy--;
	    break;
	 }
	 if (dx > 18)
	     {
	    if (hx % 2 == 1)
	       hy--;
	        hx--;
	        break;
	     }

	  case 1:
	     if (dx < 8)
	     {
	    hx--;
	    break;
	 }
	 if (dx > 23)
	     {
	    if (hx % 2 == 1)
	       hy--;
	    hx--;
	    break;
	 }

	  case 2:
	 if (dx < 4)
	     {
	    hy--;
	    break;
	 }
	     if (dx > 28)
	     {
	    if (hx % 2 == 1)
	       hy--;
	        hx--;
	    break;
	 }
	  default:
	     break;
	}

	return {x: Math.round(hx), y: Math.round(hy)}
}

function hexNeighbors(position) {
	var neighbors = []
	var x = position.x
	var y = position.y

	function n(x, y) {
		neighbors.push({x: x, y: y})
	}

	if(x % 2 === 0) {
	  n(x-1,y)
	  n(x-1,y+1)
	  n(x,y+1)
	  n(x+1,y+1)
	  n(x+1,y)
	  n(x,y-1)
	} else{
	  n(x-1,y-1)
	  n(x-1,y)
	  n(x,y+1)
	  n(x+1,y)
	  n(x+1,y-1)
	  n(x,y-1)
	}

	return neighbors
}

function hexInDirection(position, dir) {
	return hexNeighbors(position)[dir]
}

function directionOfDelta(xa, ya, xb, yb) {
	var neighbors = hexNeighbors({x: xa, y: ya})
	for(var i = 0; i < neighbors.length; i++) {
		if(neighbors[i].x === xb && neighbors[i].y === yb)
			return i
	}

	return null
}

function coordGridToCube(grid) {
	//even-q layout -> cube layout
	x = grid.x
	z = grid.y - (grid.x + (grid.x&1)) / 2
	y = -x-z
	return {x:x,y:y,z:z}
}

function hexDistance(a, b) {
	//we convert our hex coordinates into cube coordinates and then we only have to see which of the 3 axis is the longest
    cubeA = coordGridToCube(a)
    cubeB = coordGridToCube(b)
	return Math.max(Math.abs(cubeA.x - cubeB.x), Math.abs(cubeA.y - cubeB.y),Math.abs(cubeA.z - cubeB.z))
}

function hexOppositeDirection(direction) {
	return (direction + 3) % 6
}

// The adjacent hex around a nearest to b
function hexNearestNeighbor(a, b) {
	var neighbors = hexNeighbors(a)
	var min = Infinity, minIdx = -1
	for(var i = 0; i < neighbors.length; i++) {
		var dist = hexDistance(neighbors[i], b)
		if(dist < min) {
			min = dist
			minIdx = i
		}
	}
	if(minIdx === -1)
		return null
	return {hex: neighbors[minIdx], distance: min, direction: minIdx}
}

// Draws a line between a and b, returning the list of coordinates (including b)
function hexLine(a, b) {
	var path = []
	var position = {x: a.x, y: a.y}
	while(true) {
		path.push(position)
		if(position.x === b.x && position.y === b.y)
			return path
		var nearest = hexNearestNeighbor(position, b)
		if(nearest === null)
			return null
		position = nearest.hex
	}
	throw "unreachable"
}<commit_msg>minor cleanup<commit_after>// Geometry-related functions, for the hex and isometric grids.

function toTileNum(position) {
	return position.y * 200 + position.x
}

function fromTileNum(tile) {
	return {x: tile % 200, y: Math.floor(tile / 200)}
}

function tileToScreen(x, y) {
	x = 99 - x // this algorithm expects x to be reversed
	var sx = 4752 + (32 * y) - (48 * x)
	var sy = (24 * y) + (12 * x)

   return {x: sx, y: sy}
}

function tileFromScreen(x, y) {
	var off_x = -4800 + x
	var off_y = y
	var xx = off_x - off_y * 4 / 3
	var tx = xx / 64

	if (xx >= 0) tx++
	tx = -tx
	var yy = off_y + off_x / 4
	var ty = yy / 32
	if (yy < 0) ty--

	return {x: 99 - Math.round(tx), y: Math.round(ty)}
}

function hexToScreen(x, y) {
	var sx = 4816 - ((((x + 1) >> 1) << 5) + ((x >> 1) << 4) - (y << 4))
	var sy = ((12 * (x >> 1)) + (y * 12)) + 11

	return {x: sx, y: sy}
}

function hexFromScreen(x, y) {
	var x0 = 4800
	var y0 = 0
	var nx, ny

	if (x - x0 < 0)
		nx = (x - x0 + 1) / 16 - 1
	else
		nx = (x - x0) / 16

	if (y - y0 < 0)
		ny = (y - y0 + 1) / 12 - 1
	else
		ny = (y - y0) / 12

	if (Math.abs(nx) % 2 != Math.abs(ny) % 2)
		nx--;

	var xhBase = x0 + 16 * nx
	var yhBase = y0 + 12 * ny

	var hx = (4 * (yhBase - y0) - 3 * (xhBase - x0)) / 96
	var hy = (yhBase - y0) / 12 - hx / 2

	var dx = x - xhBase
	var dy = y - yhBase

	switch(dy)
	{
	  case 0:
	     if (dx < 12)
	     {
	    hy--;
	    break;
	 }
	 if (dx > 18)
	     {
	    if (hx % 2 == 1)
	       hy--;
	        hx--;
	        break;
	     }

	  case 1:
	     if (dx < 8)
	     {
	    hx--;
	    break;
	 }
	 if (dx > 23)
	     {
	    if (hx % 2 == 1)
	       hy--;
	    hx--;
	    break;
	 }

	  case 2:
	 if (dx < 4)
	     {
	    hy--;
	    break;
	 }
	     if (dx > 28)
	     {
	    if (hx % 2 == 1)
	       hy--;
	        hx--;
	    break;
	 }
	  default:
	     break;
	}

	return {x: Math.round(hx), y: Math.round(hy)}
}

function hexNeighbors(position) {
	var neighbors = []
	var x = position.x
	var y = position.y

	function n(x, y) {
		neighbors.push({x: x, y: y})
	}

	if(x % 2 === 0) {
	  n(x-1,y)
	  n(x-1,y+1)
	  n(x,y+1)
	  n(x+1,y+1)
	  n(x+1,y)
	  n(x,y-1)
	} else{
	  n(x-1,y-1)
	  n(x-1,y)
	  n(x,y+1)
	  n(x+1,y)
	  n(x+1,y-1)
	  n(x,y-1)
	}

	return neighbors
}

function hexInDirection(position, dir) {
	return hexNeighbors(position)[dir]
}

function directionOfDelta(xa, ya, xb, yb) {
	var neighbors = hexNeighbors({x: xa, y: ya})
	for(var i = 0; i < neighbors.length; i++) {
		if(neighbors[i].x === xb && neighbors[i].y === yb)
			return i
	}

	return null
}

function hexGridToCube(grid) {
	//even-q layout -> cube layout
	var z = grid.y - (grid.x + (grid.x & 1)) / 2
	var y = -grid.x - z
	return {x: grid.x, y: y, z: z}
}

function hexDistance(a, b) {
	// we convert our hex coordinates into cube coordinates and then
	// we only have to see which of the 3 axes is the longest

    var cubeA = hexGridToCube(a)
    var cubeB = hexGridToCube(b)
	return Math.max(Math.abs(cubeA.x - cubeB.x),
	                Math.abs(cubeA.y - cubeB.y),
	                Math.abs(cubeA.z - cubeB.z))
}

function hexOppositeDirection(direction) {
	return (direction + 3) % 6
}

// The adjacent hex around a nearest to b
function hexNearestNeighbor(a, b) {
	var neighbors = hexNeighbors(a)
	var min = Infinity, minIdx = -1
	for(var i = 0; i < neighbors.length; i++) {
		var dist = hexDistance(neighbors[i], b)
		if(dist < min) {
			min = dist
			minIdx = i
		}
	}
	if(minIdx === -1)
		return null
	return {hex: neighbors[minIdx], distance: min, direction: minIdx}
}

// Draws a line between a and b, returning the list of coordinates (including b)
function hexLine(a, b) {
	var path = []
	var position = {x: a.x, y: a.y}
	while(true) {
		path.push(position)
		if(position.x === b.x && position.y === b.y)
			return path
		var nearest = hexNearestNeighbor(position, b)
		if(nearest === null)
			return null
		position = nearest.hex
	}
	throw "unreachable"
}<|endoftext|>


<commit_before>'use strict';

// Script dependencies
const util = require('util');
const async = require('async');
const request = require('request');
const webpageCapture = require('webpage-capture');

// Script rules
let rules = require('./rules');

// Webshot default options
let webshotOptions = {
  outputType: 'base64',
  onlySuccess: false,
  crop: true
};

// Social scanner
const scanner = (target, options = {}, callback) => {
  // Exit if no input target
  if (!target || target === '') {
    throw new Error('The target parameter is missing.');
  }

  // Init the main variables
  const username = target;

  // Optionally restrict the rules number
  if (Array.isArray(options.restrict) && options.restrict.length > 0) {
    rules = rules.filter(r => {
      return options.restrict.indexOf(r.name) > -1;
    });
  }

  // Run the social scanner for each url
  async.map(rules, (item, cb) => {
    // Assign the username value to tmp variable
    let usr = username;

    // Optionally apply rule filters
    if (Array.isArray(item.filters)) {
      item.filters.forEach(f => {
        usr = f(username);
      });
    }

    // Build the url
    const url = util.format(item.url, usr);

    // Do the http request
    request(url, (err, response, body) => {
      // Exit on request error
      if (err || (response && response.statusCode !== 200)) {
        cb(null, {
          name: item.name,
          error: {
            status: response ? response.statusCode : null,
            message: response ? response.statusMessage : null,
            error: err
          }
        });
        return;
      }
      cb(null, {
        name: item.name,
        error: false,
        body: body
      });
    });

  // Once all the requests are done process with the Script
  // optionally begin to capture each results
  }, (err, results) => {
    // If results doesn't need to be captured as screenshots
    // just return the results array as it is
    if (!options.capture) {
      return callback(err, results);
    }

    // Update default settings
    webshotOptions = Object.assign(webshotOptions, options.screenshotOptions || {}, {
      output: options.output ? `${options.output}/${username}` : `./output/${username}`
    });

    // Capture results
    webpageCapture(results, webshotOptions, (err, res) => {
      callback(err, res);
    });
  });
};

// Export module
module.exports = scanner;
<commit_msg>Some code fixes<commit_after>'use strict';

// Script dependencies
const util = require('util');
const async = require('async');
const request = require('request');
const webpageCapture = require('webpage-capture');

// Script rules
const rules = require('./rules');

// Webshot default options
const webshotOptions = {
  outputType: 'base64',
  onlySuccess: false,
  crop: true
};

/**
 * Return all the rules used by the scanner
 * this method is intended for public use since omits the filters property
 * @return {array} An array of rule objects
 */
const getRules = () => {
  return rules.map(r => ({
    name: r.name,
    category: r.category,
    url: r.url
  }));
};

/**
 * The main module method than take a target username
 * and perform a deep scan following various rules
 * described in the rules file, optionally applying filters to
 * the username value to match the website schema defined in rule
 * @param  {String}   target       The username to scan for
 * @param  {Object}   [options={}] The scanner options
 * @param  {Function} callback     The callback to execute when everything has done
 */
const scanner = (target, options = {}, callback) => {
  // Exit if no input target
  if (!target || target === '') {
    throw new Error('The target parameter is missing.');
  }

  // Init the main variables
  let username = target;
  let filteredRules = [];

  // Optionally restrict the rules number
  if (Array.isArray(options.restrict) && options.restrict.length > 0) {
    filteredRules = rules.filter(r => {
      return options.restrict.indexOf(r.name) > -1;
    });
  }

  // Build the request options
  let requestOptions = {
    strictSSL: false,
    followRedirect: true,
    timeout: options.timeout || 2000
  };

  // Run the social scanner for each url
  async.map(filteredRules, (item, cb) => {
    // Assign the username value to tmp variable
    let usr = username;

    // Optionally apply rule filters
    if (Array.isArray(item.filters)) {
      item.filters.forEach(f => {
        usr = f(username);
      });
    }

    // Build the url
    const url = util.format(item.url, usr);

    // Add current url to global request options
    requestOptions.url = url;

    // Do the http request
    request(requestOptions, (err, response, body) => {
      // Exit on request error
      if (err || (response && response.statusCode !== 200)) {
        cb(null, {
          name: item.name,
          category: item.category,
          address: url,
          error: {
            status: response ? response.statusCode : null,
            message: response ? response.statusMessage : null,
            error: err
          }
        });
        return;
      }
      cb(null, {
        name: item.name,
        category: item.category,
        address: url,
        error: false,
        body: body
      });
    });

  // Once all the requests are done process with the Script
  // optionally begin to capture each results
  }, (err, results) => {
    // If results doesn't need to be captured as screenshots
    // just return the results array as it is
    if (!options.capture) {
      return callback(err, results);
    }

    // Update default settings
    let captureOptions = Object.assign({}, webshotOptions, options.screenshotOptions || {}, {
      output: options.output ? `${options.output}/${username}` : `./output/${username}`
    });

    // Capture results
    webpageCapture(results, captureOptions, (err, res) => {
      callback(err, res);
    });
  });
};

// Export module
module.exports = scanner;

// Export method to get module rules
module.exports.getRules = getRules;
<|endoftext|>


<commit_before>var twitter = require('twit');
var express = require('express');
var http = require('http');
var socketio = require('socket.io');
var sentiment = require('sentiment');

var TwitterStreamService = function(server){
	var self = this;
	self.io = socketio(server);
	self.clientCount = 0;

	self.twitter_api =
			new twitter({
				consumer_key: process.env.OPENSHIFT_APP_TWITTER_CONSUMER_KEY,
				consumer_secret: process.env.OPENSHIFT_APP_TWITTER_CONSUMER_SECRET,
				access_token: process.env.OPENSHIFT_APP_TWITTER_ACCESS_TOKEN,
				access_token_secret: process.env.OPENSHIFT_APP_TWITTER_ACCESS_TOKEN_SECRET
			});
	self.twitter_stream = null;

	self.SetupSocketCallback = function(){
		self.io.on('connection', function (socket) {
			console.log(new Date() + ' - A new client is connected.');
			if(self.twitter_stream !== null && self.clientCount === 0){
				self.twitter_stream.start();
				console.log(new Date() + ' - Restarted streaming.');
			}
			self.clientCount ++;
			socket.emit("connected");

		  	socket.on('start-streaming', function() {
		  		console.log(new Date() + ' - Received new event <start-streaming>.');
		  		if(self.twitter_stream === null)
			    	self.SetupTwitterStreamCallback(socket);
		  	});
		  	socket.on('get-trends', function(){
		  		console.log(new Date() + ' - Received new event <get-trend>.');
		  		self.GetTwitterTrends(socket);
		  	});
		  	socket.on('disconnect', function() {
				console.log(new Date() + ' - A client is disconnected');
				self.clientCount --;
				if(self.clientCount < 1){
					self.twitter_stream.stop();
					console.log(new Date() + ' - All clients are disconnected. Stopped streaming.');
				}
			});
		});
	}

	self.SetupTwitterStreamCallback = function(socket){
      	self.twitter_stream = self.twitter_api.stream(
      		'statuses/filter',
      		{'locations':'-180,-90,180,90', 'language':'en'});

      	self.twitter_stream.on('tweet', function(tweet) {
      		//console.log(new Date() + ' - Received new tweet.');
          	if (tweet.coordinates && tweet.coordinates !== null){
          		tweet.sentiment = sentiment(tweet.text);
              	socket.broadcast.emit("new-tweet", tweet);
              	socket.emit('new-tweet', tweet);
          	}
         });

      	self.twitter_stream.on('error', function(error) {
      		console.log(new Date() + ' - Twitter stream error: ' + error.message);
		});

      	self.twitter_stream.on('connect', function(request) {
		    console.log(new Date() + ' - Connected to Twitter stream API.');
		});

		self.twitter_stream.on('reconnect', function (request, response, connectInterval) {
		  	console.log(new Date() + ' - Trying to reconnect to Twitter stream API in ' + connectInterval + ' ms.');
		});

      	self.twitter_stream.on('limit', function(limitMessage) {
        	console.log(new Date() + ' - Twitter stream limit error: ' + limitMessage);
      	});

      	self.twitter_stream.on('warning', function(warningMessage) {
       	 	console.log(new Date() + ' - Twitter stream warning: ' + warningMessage);
      	});

      	self.twitter_stream.on('disconnect', function(disconnectMessage) {
        	console.log(new Date() + ' - Disconnected to Twitter stream API.');
      	});

      	console.log(new Date() + " - Initialized twitter streaming.");
	}

	self.GetTwitterTrends = function(socket){
		self.twitter_api.get(
			'trends/place',
			{'id' : 1},
			function(error, trends, response){
				if(error){
					console.log(new Date() + ' - Error when retrieving twitter trends: ' + error);
					throw error;
				}
				console.log(new Date() + ' - Received twitter trends.');
				socket.broadcast.emit("new-trends", trends);
		        socket.emit('new-trends', trends[0].trends);
			});
		console.log(new Date() + ' - Retrieving twitter trends.');
	}

	self.SetupSocketCallback();
}

var Application = function(){
	var self = this;

	self.Initialize = function(){
		self.ip        = process.env.OPENSHIFT_NODEJS_IP || 'localhost';
        self.port      = process.env.OPENSHIFT_NODEJS_PORT || 3000;

		self.app = express();
		self.app.use(express.static(__dirname + '/client'));

		self.server = http.Server(self.app);
		self.socketService = new TwitterStreamService(self.server);
	}

	self.Start = function(){
		self.server.listen(self.port, self.ip, function() {
            console.log(new Date() + ' - Server started. Listening on ' + self.ip + ':' + self.port);
        });
	}
}

var app = new Application();
app.Initialize();
app.Start();
<commit_msg>added cache for twitter trends data<commit_after>var twitter = require('twit');
var express = require('express');
var http = require('http');
var socketio = require('socket.io');
var sentiment = require('sentiment');

var TwitterStreamService = function(server){
	var self = this;
	self.io = socketio(server);
	self.clientCount = 0;

	self.twitter_stream = null;
	self.twitter_trends = {
		'created_at': null,
		'trends': null
	};
	self.twitter_api =
			new twitter({
				consumer_key: process.env.OPENSHIFT_APP_TWITTER_CONSUMER_KEY,
				consumer_secret: process.env.OPENSHIFT_APP_TWITTER_CONSUMER_SECRET,
				access_token: process.env.OPENSHIFT_APP_TWITTER_ACCESS_TOKEN,
				access_token_secret: process.env.OPENSHIFT_APP_TWITTER_ACCESS_TOKEN_SECRET
			});

	self.SetupSocketCallback = function(){
		self.io.on('connection', function (socket) {
			console.log(new Date() + ' - A new client is connected.');
			if(self.twitter_stream !== null && self.clientCount === 0){
				self.twitter_stream.start();
				console.log(new Date() + ' - Restarted streaming.');
			}
			self.clientCount ++;
			socket.emit("connected");

		  	socket.on('start-streaming', function() {
		  		console.log(new Date() + ' - Received new event <start-streaming>.');
		  		if(self.twitter_stream === null)
			    	self.SetupTwitterStreamCallback(socket);
		  	});
		  	socket.on('get-trends', function(){
		  		console.log(new Date() + ' - Received new event <get-trend>.');
		  		self.GetTwitterTrends(socket);
		  	});
		  	socket.on('disconnect', function() {
				console.log(new Date() + ' - A client is disconnected');
				self.clientCount --;
				if(self.clientCount < 1){
					self.twitter_stream.stop();
					console.log(new Date() + ' - All clients are disconnected. Stopped streaming.');
				}
			});
		});
	}

	self.SetupTwitterStreamCallback = function(socket){
      	self.twitter_stream = self.twitter_api.stream(
      		'statuses/filter',
      		{'locations':'-180,-90,180,90', 'language':'en'});

      	self.twitter_stream.on('tweet', function(tweet) {
      		//console.log(new Date() + ' - Received new tweet.');
          	if (tweet.coordinates && tweet.coordinates !== null){
          		tweet.sentiment = sentiment(tweet.text);
              	socket.broadcast.emit("new-tweet", tweet);
              	socket.emit('new-tweet', tweet);
          	}
         });

      	self.twitter_stream.on('error', function(error) {
      		console.log(new Date() + ' - Twitter stream error: ' + error.message);
		});

      	self.twitter_stream.on('connect', function(request) {
		    console.log(new Date() + ' - Connected to Twitter stream API.');
		});

		self.twitter_stream.on('reconnect', function (request, response, connectInterval) {
		  	console.log(new Date() + ' - Trying to reconnect to Twitter stream API in ' + connectInterval + ' ms.');
		});

      	self.twitter_stream.on('limit', function(limitMessage) {
        	console.log(new Date() + ' - Twitter stream limit error: ' + limitMessage);
      	});

      	self.twitter_stream.on('warning', function(warningMessage) {
       	 	console.log(new Date() + ' - Twitter stream warning: ' + warningMessage);
      	});

      	self.twitter_stream.on('disconnect', function(disconnectMessage) {
        	console.log(new Date() + ' - Disconnected to Twitter stream API.');
      	});

      	console.log(new Date() + " - Initialized twitter streaming.");
	}

	self.GetTwitterTrends = function(socket){
		if(self.twitter_trends.trends === null || (new Date() - self.twitter_trends.created_at) > 60000){
			console.log(new Date() + ' - Retrieving twitter trends.');
			self.twitter_api.get(
				'trends/place',
				{'id' : 1},
				function(error, trends, response){
					if(error){
						console.log(new Date() + ' - Error when retrieving twitter trends: ' + error);
						throw error;
					}
					console.log(new Date() + ' - Received twitter trends.');

					self.twitter_trends.created_at = new Date();
					self.twitter_trends.trends = trends[0].trends;
					console.log(new Date() + ' - Updated twitter trends cache.');

					socket.broadcast.emit("new-trends", self.twitter_trends.trends);
			        socket.emit('new-trends', self.twitter_trends.trends);
				});
		}
		else{
			socket.broadcast.emit("new-trends", self.twitter_trends.trends);
			socket.emit('new-trends', self.twitter_trends.trends);
		}
	}

	self.SetupSocketCallback();
}

var Application = function(){
	var self = this;

	self.Initialize = function(){
		self.ip        = process.env.OPENSHIFT_NODEJS_IP || 'localhost';
        self.port      = process.env.OPENSHIFT_NODEJS_PORT || 3000;

		self.app = express();
		self.app.use(express.static(__dirname + '/client'));

		self.server = http.Server(self.app);
		self.socketService = new TwitterStreamService(self.server);
	}

	self.Start = function(){
		self.server.listen(self.port, self.ip, function() {
            console.log(new Date() + ' - Server started. Listening on ' + self.ip + ':' + self.port);
        });
	}
}

var app = new Application();
app.Initialize();
app.Start();
<|endoftext|>


<commit_before>// Copyright (c) 2016 CanalTP
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

'use strict';

// fake includes
var summary;
var response;

var utils = {};

utils.isDatetimeType = function(str) {
    return $.inArray(str, ['since', 'until']) !== -1 || str.match(/datetime$/);
};

utils.htmlEncode = function(value) {
    return $('
').text(value).html();
};

utils.durationToString = function(duration) {
    var res = '';
    var seconds = duration % 60;
    var minutes = Math.floor(duration / 60) % 60;
    var hours = Math.floor(duration / (60 * 60)) % 24;
    var days = Math.floor(duration / (24 * 60 * 60));

    if (days !== 0) { res += sprintf('%sd', days); }
    if (hours !== 0) { res += sprintf('%sh', hours); }
    if (minutes !== 0) { res += sprintf('%smin', minutes); }
    if (seconds !== 0) { res += sprintf('%ss', seconds); }

    if (! res) {
        return '0s';
    } else {
        return res;
    }
};

utils.isTemplate = function(str) {
    return str.slice(0, 1) === '{' && str.slice(-1) === '}';
};

utils.flatMap = function(array, f) {
    var result = [];

    array.forEach(function(obj, i, array) {
        result = result.concat(f(obj, i, array));
    });
    return result;
};

$.notify.addStyle('navitia', {
    html: '

',
    classes: {
        error: {
            'color': '#B94A48',
            'background-color': '#F2DEDE',
            'border-color': '#EED3D7',
        },
        success: {
            'color': '#468847',
            'background-color': '#DFF0D8',
            'border-color': '#D6E9C6',
        },
        info: {
            'color': '#3A87AD',
            'background-color': '#D9EDF7',
            'border-color': '#BCE8F1',
        },
        warn: {
            'color': '#C09853',
            'background-color': '#FCF8E3',
            'border-color': '#FBEED5',
        }
    }
});

utils.errorMessage = function(url, xhr, status/*, error*/) {
    var message;
    if (url.startsWith('http:') && window.location.protocol === 'https:') {
        message = $('').text(
            'You cannot request the API using http if ' +
                'you are connected to navitia-playground ' +
                'using https.  Please request the API using ' +
                'https or connect to navitia-playground ' +
                'using http.');
    } else if (xhr.readyState === 0) {
        message = $('').text('Network error');
    } else if (xhr.responseJSON) {
        message = $(summary.run(new response.Context(xhr.responseJSON),
                                'response',
                                xhr.responseJSON));
    } else {
        message = $('').text(status);
    }
    return message;
};

utils.notifyOnError = function(typeError, url, xhr, status, error) {
    if (xhr.status === 401) {
        $('#token').addClass('templateInput');
    }
    var message = utils.errorMessage(url, xhr, status, error);
    $.notify({
        text: $('').text(sprintf('%s error: ', typeError)).append(message)
    }, {
        position: 'right bottom',
        style: 'navitia',
    });
};

utils.notifyWarn = function(message) {
    $.notify({
        text: message
    }, {
        position: 'right bottom',
        className: 'warn',
        style: 'navitia',
    });
};

utils.notifyInfo = function(message) {
    $.notify({
        text: message
    }, {
        position: 'right bottom',
        className: 'info',
        style: 'navitia',
    });
};

utils.getType = function(key) {
    if (!key || typeof key !== 'string') {
        return null;
    }
    // hardcoded cases:
    switch (key) {
    case 'places_nearby': return 'place';
    case 'addresses': return 'address';
    case 'from': return 'place';
    case 'to': return 'place';
    }
    // generic plural
    if (key.slice(-1) === 's') { return key.slice(0, -1); }

    // just the key
    return key;
};

utils.getTextColor = function(json) {
    function _toNum(c, i) { return +('0x' + c.slice(i, i + 2)); }

    if (json.text_color) {
        return '#' + json.text_color;
    }
    if (json.color) {
        var c = json.color;
        var grey = 0.21 * _toNum(c, 0) + 0.72 * _toNum(c, 2) + 0.07 * _toNum(c, 4);
        if (grey < 128) {
            return 'white';
        }
    }
    return 'black';
};

utils.computeColorFromRatio = function(ratio) {
    var r = 255;
    var g = 255;
    if (ratio < 1/2) {
        r = Math.ceil(255 * ratio * 2);
    } else {
        g = Math.ceil(255 * (1 - ratio) * 2);
    }
    return {red: r, green: g, blue: 0};
};

utils.toCssColor = function(c, alpha) {
    if (alpha) {
        return sprintf('rgba(%s, %s, %s, %s)', c.red, c.green, c.blue, alpha);
    } else {
        return sprintf('#%02x%02x%02x', c.red, c.green, c.blue);
    }
};

utils.getColorFromRatio = function(ratio) {
    return utils.toCssColor(utils.computeColorFromRatio(ratio));
};

utils.manageToken = function(token) {
  return token ? { Authorization: 'Basic ' + btoa(token) } : {};
};

utils.deepClone = function(obj) {
    return JSON.parse(JSON.stringify(obj));
};
<commit_msg>ES5 fix<commit_after>// Copyright (c) 2016 CanalTP
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

'use strict';

// fake includes
var summary;
var response;

var utils = {};

utils.isDatetimeType = function(str) {
    return $.inArray(str, ['since', 'until']) !== -1 || str.match(/datetime$/);
};

utils.htmlEncode = function(value) {
    return $('
').text(value).html();
};

utils.durationToString = function(duration) {
    var res = '';
    var seconds = duration % 60;
    var minutes = Math.floor(duration / 60) % 60;
    var hours = Math.floor(duration / (60 * 60)) % 24;
    var days = Math.floor(duration / (24 * 60 * 60));

    if (days !== 0) { res += sprintf('%sd', days); }
    if (hours !== 0) { res += sprintf('%sh', hours); }
    if (minutes !== 0) { res += sprintf('%smin', minutes); }
    if (seconds !== 0) { res += sprintf('%ss', seconds); }

    if (! res) {
        return '0s';
    } else {
        return res;
    }
};

utils.isTemplate = function(str) {
    return str.slice(0, 1) === '{' && str.slice(-1) === '}';
};

utils.flatMap = function(array, f) {
    var result = [];

    array.forEach(function(obj, i, array) {
        result = result.concat(f(obj, i, array));
    });
    return result;
};

$.notify.addStyle('navitia', {
    html: '

',
    classes: {
        error: {
            'color': '#B94A48',
            'background-color': '#F2DEDE',
            'border-color': '#EED3D7',
        },
        success: {
            'color': '#468847',
            'background-color': '#DFF0D8',
            'border-color': '#D6E9C6',
        },
        info: {
            'color': '#3A87AD',
            'background-color': '#D9EDF7',
            'border-color': '#BCE8F1',
        },
        warn: {
            'color': '#C09853',
            'background-color': '#FCF8E3',
            'border-color': '#FBEED5',
        }
    }
});

utils.errorMessage = function(url, xhr, status/*, error*/) {
    var message;
    if (url.lastIndexOf('http:', 0) === 0 && window.location.protocol === 'https:') {
        message = $('').text(
            'You cannot request the API using http if ' +
                'you are connected to navitia-playground ' +
                'using https.  Please request the API using ' +
                'https or connect to navitia-playground ' +
                'using http.');
    } else if (xhr.readyState === 0) {
        message = $('').text('Network error');
    } else if (xhr.responseJSON) {
        message = $(summary.run(new response.Context(xhr.responseJSON),
                                'response',
                                xhr.responseJSON));
    } else {
        message = $('').text(status);
    }
    return message;
};

utils.notifyOnError = function(typeError, url, xhr, status, error) {
    if (xhr.status === 401) {
        $('#token').addClass('templateInput');
    }
    var message = utils.errorMessage(url, xhr, status, error);
    $.notify({
        text: $('').text(sprintf('%s error: ', typeError)).append(message)
    }, {
        position: 'right bottom',
        style: 'navitia',
    });
};

utils.notifyWarn = function(message) {
    $.notify({
        text: message
    }, {
        position: 'right bottom',
        className: 'warn',
        style: 'navitia',
    });
};

utils.notifyInfo = function(message) {
    $.notify({
        text: message
    }, {
        position: 'right bottom',
        className: 'info',
        style: 'navitia',
    });
};

utils.getType = function(key) {
    if (!key || typeof key !== 'string') {
        return null;
    }
    // hardcoded cases:
    switch (key) {
    case 'places_nearby': return 'place';
    case 'addresses': return 'address';
    case 'from': return 'place';
    case 'to': return 'place';
    }
    // generic plural
    if (key.slice(-1) === 's') { return key.slice(0, -1); }

    // just the key
    return key;
};

utils.getTextColor = function(json) {
    function _toNum(c, i) { return +('0x' + c.slice(i, i + 2)); }

    if (json.text_color) {
        return '#' + json.text_color;
    }
    if (json.color) {
        var c = json.color;
        var grey = 0.21 * _toNum(c, 0) + 0.72 * _toNum(c, 2) + 0.07 * _toNum(c, 4);
        if (grey < 128) {
            return 'white';
        }
    }
    return 'black';
};

utils.computeColorFromRatio = function(ratio) {
    var r = 255;
    var g = 255;
    if (ratio < 1/2) {
        r = Math.ceil(255 * ratio * 2);
    } else {
        g = Math.ceil(255 * (1 - ratio) * 2);
    }
    return {red: r, green: g, blue: 0};
};

utils.toCssColor = function(c, alpha) {
    if (alpha) {
        return sprintf('rgba(%s, %s, %s, %s)', c.red, c.green, c.blue, alpha);
    } else {
        return sprintf('#%02x%02x%02x', c.red, c.green, c.blue);
    }
};

utils.getColorFromRatio = function(ratio) {
    return utils.toCssColor(utils.computeColorFromRatio(ratio));
};

utils.manageToken = function(token) {
  return token ? { Authorization: 'Basic ' + btoa(token) } : {};
};

utils.deepClone = function(obj) {
    return JSON.parse(JSON.stringify(obj));
};
<|endoftext|>