<commit_before>/*global $:false */
/*global TreeMirror:false */
import Ember from 'ember';

import ApplicationUtils from '../../mixins/application-utils';
import WebDocument from '../web-document';
import interactionEvent from '../../utils/interaction-event';

function paintCanvasMessage(canvas) {
    var ctx = canvas.getContext('2d');

    var pattern = document.createElement('canvas');
    pattern.width = 20;
    pattern.height = 20;
    var pctx = pattern.getContext('2d');
    pctx.fillStyle = "#ccc";
    pctx.fillRect(0,0,10,10);
    pctx.fillRect(10,10,10,10);
    pattern = ctx.createPattern(pattern, "repeat");

    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.font = '16px sans-serif';
    ctx.fillStyle = 'black';
    ctx.fillText('Displaying the content of the canvas is not supported', 10, canvas.height / 2);
}
function treeMirrorDelegate(){
    return {
        createElement: function(tagName) {
            var node = null;
            if(tagName === 'SCRIPT' || tagName === 'META' || tagName === 'BASE') {
                node = document.createElement('NOSCRIPT');
            } else if(tagName === 'FORM') {
                node = document.createElement(tagName);
                $(node).on('submit', ()=>false);
            } else if (tagName === 'IFRAME' || tagName === 'FRAME') {
                node = document.createElement(tagName);
                node.setAttribute('src', '/static/frames-not-supported.html');
            } else if (tagName === 'CANVAS') {
                node = document.createElement(tagName);
                paintCanvasMessage(node);
            }
            return node;
        },
        setAttribute: function(node, attrName, value){
            if(
                /^on/.test(attrName) ||  // Disallow JS attributes
                ((node.tagName === 'FRAME' || node.tagName === 'IFRAME') &&
                (attrName === 'src' || attrName === 'srcdoc')) // Frames not supported
            ) {
                return true;
            }

            try{
                node.setAttribute(attrName, value);
            }catch(e){
                console.log(e, attrName, value);
            }

            if(node.tagName === 'CANVAS' && (attrName === 'width' || attrName === 'height')) {
                paintCanvasMessage(node);
            }

            return true;
        }
    };
}

export default WebDocument.extend(ApplicationUtils, {
    ws_deferreds: {},
    connect: function() {
        var ws = this.get('ws');

        ws.addCommand('loadStarted', function() {
            this.showLoading(true);
        }.bind(this));

        ws.addCommand('metadata', function(data) {
            if (data.id && this.get('ws_deferreds.' + data.id)) {
                var deferred = this.get('ws_deferreds.' + data.id);
                this.set('ws_deferreds.' + data.id, undefined);
                if (data.error) {
                    deferred.reject(data);
                } else {
                    deferred.resolve(data);
                }
            }
            this[data.loading ? 'showLoading' : 'hideLoading']();

            var listener = this.get('listener');
            if(listener && listener.updateExtractedItems) {
                listener.updateExtractedItems(data.items || []);
                listener.set('followedLinks', data.links || []);
                var pageMap = listener.get('pageMap');
                // Handle page change in browser tab on the server caused by event
                if (!pageMap[data.fp] || data.fp !== listener.get('loadedPageFp')) {
                    pageMap[data.fp] = data;
                    this.installEventHandlersForBrowsing();
                    this.hideLoading();
                    listener.get('browseHistory').pushObject(data.fp);
                    Ember.run.later(this, function() {
                        var doc = this.getIframeNode().contentWindow.document;
                        doc.onscroll = this.redrawNow.bind(this);
                    }, 500);
                }
                listener.set('loadedPageFp', data.fp);
            }
            this.set('loadedPageFp', data.fp);
            this.set('followedLinks', data.links);
            this.set('currentUrl', data.url);
            Ember.run.next(this, function() {
                this.redrawNow();
            });
        }.bind(this));

        ws.addCommand('mutation', function(data){
            data = data._data;
            var action = data[0];
            var args = data.slice(1);
            if(action === 'initialize') {
                this.iframePromise = this.clearIframe().then(function(){
                    var doc = this.getIframeNode().contentWindow.document;
                    this.treeMirror = new TreeMirror(doc, treeMirrorDelegate(this));
                }.bind(this));
            }
            this.iframePromise.then(function() {
                this.treeMirror[action].apply(this.treeMirror, args);
            }.bind(this));
        }.bind(this));

        ws.addCommand('cookies', msg => this.saveCookies(msg._data));
    }.on('init'),

    connectionStatusType: 'warning',
    connectionStatusMessage: '',
    reconnectInteractions: null,
    _reconnectClock: function() {
        if (Number(this.get('connectionStatusTime')) >= 2) {
            this.decrementProperty('connectionStatusTime');
            this.set('connectionStatusMessage', 'Reconnecting to server in ' + this.get('connectionStatusTime') + ' seconds.');
            this.set('connectionAction', true);
        } else {
            this.set('connectionStatusMessage', 'Reconnecting...');
            this.set('connectionAction', null);
        }
    },

    connectionLost: function() {
        if (this.get('ws.closed')) {
            Ember.run.later(this, function() {
                this.set('showConnectionLost', true);
            }, 500);
            if (this.get('reconnectInteractions') === null) {
                var reconnect = this.get('canvas._interactionsBlocked');
                this.setInteractionsBlocked(true);
                this.set('reconnectInteractions', reconnect);
            }            this.set('connectionStatusTime', this.get('ws.reconnectTimeout') / 1000);
            this.addObserver('clock.second', this, this._reconnectClock);
        } else {
            this.removeObserver('clock.second', this, this._reconnectClock);
        }
    }.observes('ws.closed'),

    connectionReEstablished: function() {
        if (this.get('ws.opened')) {
            if (this.get('reconnectInteractions') !== null) {
                var reconnect = this.get('reconnectInteractions');
                this.set('reconnectInteractions', null);
                this.setInteractionsBlocked(reconnect);
                var listener = this.get('listener');
                if(listener && listener.reload && listener.get('loadedPageFp')) {
                    listener.reload();
                }
            }
            this.set('connectionStatusMessage', null);
            this.set('connectionAction', null);
            this.set('showConnectionLost', false);
        }
    }.observes('ws.opened'),

    connectionConnecting: function() {
        if (this.get('ws.connecting')) {
            this.set('connectionStatusMessage', 'Reconnecting...');
            this.set('connectionAction', null);
        }
    }.observes('ws.connecting'),

    fetchDocument: function(url, spider, fp, command) {
        var unique_id = this.shortGuid(),
            deferred = new Ember.RSVP.defer(),
            ifWindow = this.getIframeNode().contentWindow;
        this.set('ws_deferreds.' + unique_id, deferred);
        this.get('ws').send({
            _meta: {
                spider: spider,
                project: this.get('slyd.project'),
                id: unique_id,
                viewport: ifWindow.innerWidth + 'x' + ifWindow.innerHeight,
                user_agent: navigator.userAgent,
                cookies: this.cookies
            },
            _command: command || 'load',
            url: url
        });
        return deferred.promise;
    },

    setInteractionsBlocked: function(blocked) {
        if (this.get('reconnectInteractions') !== null) {
            this.set('reconnectInteractions', blocked);
            return;
        }
        if (this.get('canvas.interactionsBlocked') !== blocked) {
            this.set('canvas.interactionsBlocked', blocked);
        }
    },

    setIframeContent: function(doc) {
        if(typeof doc !== 'string') {
            return;
        }
        var iframe = Ember.$('#' + this.get('iframeId'));
        iframe.attr('srcdoc', doc);
        this.set('document.iframe', iframe);
    },

    clearIframe: function() {
        var defer = new Ember.RSVP.defer();
        var iframe = this.getIframeNode();
        var id = this.shortGuid();
        // Using a empty static page because using srcdoc or an data:uri gives
        // permission problems and/or broken baseURI behaviour in different browsers.
        iframe.setAttribute('src', '/static/empty-frame.html?' + id);
        iframe.removeAttribute('srcdoc');
        // Using a message to workaround onload bug on some browsers (cough IE cough).
        var $win = $(window).bind('message', function onMessage(e){
            if(e.originalEvent.data.frameReady === id){
                $win.unbind('message', onMessage);
                defer.resolve();
            }
        });
        return defer.promise;
    },

    frameEventListeners: [],
    addFrameEventListener: function(event, fn, useCapture=false){
        this.frameEventListeners.push([event, fn, useCapture]);
        this.getIframe()[0].addEventListener(event, fn, useCapture);
    },

    installEventHandlersForBrowsing: function() {
        this._super();
        var iframe = this.getIframe();
        iframe.on('keyup.portia keydown.portia keypress.portia input.portia ' +
                  'mousedown.portia mouseup.portia', this.postEvent.bind(this));
        this.addFrameEventListener("scroll", e => Ember.run.throttle(this, this.postEvent, e, 200), true);
        this.addFrameEventListener('focus', this.postEvent.bind(this), true);
        this.addFrameEventListener('blur', this.postEvent.bind(this), true);
        this.addFrameEventListener('change', this.postEvent.bind(this), true);
    },

    uninstallEventHandlers: function(){
        var frameDoc = this.getIframe()[0];
        this.frameEventListeners.forEach(([event, fn, useCapture]) => {
            frameDoc.removeEventListener(event, fn, useCapture);
        });
        this.frameEventListeners = [];
        this._super();
    },

    clickHandlerBrowse: function(evt) {
        if(evt.which > 1 || evt.ctrlKey) { // Ignore right/middle click or Ctrl+click
            return;
        }
        this.postEvent(evt);
        return this._super(evt);
    },

    postEvent: function(evt){
        this.get('ws').send({
            _meta: {
                spider: this.get('slyd.spider'),
                project: this.get('slyd.project'),
            },
            _command: 'interact',
            interaction: interactionEvent(evt)
        });
    },

    bindResizeEvent: function() {
        Ember.$(window).on('resize', Ember.run.bind(this, this.handleResize));
    }.on('init'),

    handleResize: function() {
        var iframe_window = this.getIframeNode().contentWindow;
        this.get('ws').send({
            _command: 'resize',
            size: iframe_window.innerWidth + 'x' + iframe_window.innerHeight
        });
    },

    saveCookies: function(cookies){
        this.cookies = cookies;
        if(window.sessionStorage){
            window.sessionStorage.portia_cookies = JSON.stringify(cookies);
        }
    },
    loadCookies: function(){
        if(window.sessionStorage && sessionStorage.portia_cookies){
            this.cookies = JSON.parse(sessionStorage.portia_cookies);
        }
    }.on('init'),

    actions: {
        reconnectWebsocket: function() {
            this.get('ws').connect();
        }
    }
});
<commit_msg>Don't bubble event when clicking on inputs (Fixes checkboxes)<commit_after>/*global $:false */
/*global TreeMirror:false */
import Ember from 'ember';

import ApplicationUtils from '../../mixins/application-utils';
import WebDocument from '../web-document';
import interactionEvent from '../../utils/interaction-event';

function paintCanvasMessage(canvas) {
    var ctx = canvas.getContext('2d');

    var pattern = document.createElement('canvas');
    pattern.width = 20;
    pattern.height = 20;
    var pctx = pattern.getContext('2d');
    pctx.fillStyle = "#ccc";
    pctx.fillRect(0,0,10,10);
    pctx.fillRect(10,10,10,10);
    pattern = ctx.createPattern(pattern, "repeat");

    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.font = '16px sans-serif';
    ctx.fillStyle = 'black';
    ctx.fillText('Displaying the content of the canvas is not supported', 10, canvas.height / 2);
}
function treeMirrorDelegate(){
    return {
        createElement: function(tagName) {
            var node = null;
            if(tagName === 'SCRIPT' || tagName === 'META' || tagName === 'BASE') {
                node = document.createElement('NOSCRIPT');
            } else if(tagName === 'FORM') {
                node = document.createElement(tagName);
                $(node).on('submit', ()=>false);
            } else if (tagName === 'IFRAME' || tagName === 'FRAME') {
                node = document.createElement(tagName);
                node.setAttribute('src', '/static/frames-not-supported.html');
            } else if (tagName === 'CANVAS') {
                node = document.createElement(tagName);
                paintCanvasMessage(node);
            }
            return node;
        },
        setAttribute: function(node, attrName, value){
            if(
                /^on/.test(attrName) ||  // Disallow JS attributes
                ((node.tagName === 'FRAME' || node.tagName === 'IFRAME') &&
                (attrName === 'src' || attrName === 'srcdoc')) // Frames not supported
            ) {
                return true;
            }

            try{
                node.setAttribute(attrName, value);
            }catch(e){
                console.log(e, attrName, value);
            }

            if(node.tagName === 'CANVAS' && (attrName === 'width' || attrName === 'height')) {
                paintCanvasMessage(node);
            }

            return true;
        }
    };
}

export default WebDocument.extend(ApplicationUtils, {
    ws_deferreds: {},
    connect: function() {
        var ws = this.get('ws');

        ws.addCommand('loadStarted', function() {
            this.showLoading(true);
        }.bind(this));

        ws.addCommand('metadata', function(data) {
            if (data.id && this.get('ws_deferreds.' + data.id)) {
                var deferred = this.get('ws_deferreds.' + data.id);
                this.set('ws_deferreds.' + data.id, undefined);
                if (data.error) {
                    deferred.reject(data);
                } else {
                    deferred.resolve(data);
                }
            }
            this[data.loading ? 'showLoading' : 'hideLoading']();

            var listener = this.get('listener');
            if(listener && listener.updateExtractedItems) {
                listener.updateExtractedItems(data.items || []);
                listener.set('followedLinks', data.links || []);
                var pageMap = listener.get('pageMap');
                // Handle page change in browser tab on the server caused by event
                if (!pageMap[data.fp] || data.fp !== listener.get('loadedPageFp')) {
                    pageMap[data.fp] = data;
                    this.installEventHandlersForBrowsing();
                    this.hideLoading();
                    listener.get('browseHistory').pushObject(data.fp);
                    Ember.run.later(this, function() {
                        var doc = this.getIframeNode().contentWindow.document;
                        doc.onscroll = this.redrawNow.bind(this);
                    }, 500);
                }
                listener.set('loadedPageFp', data.fp);
            }
            this.set('loadedPageFp', data.fp);
            this.set('followedLinks', data.links);
            this.set('currentUrl', data.url);
            Ember.run.next(this, function() {
                this.redrawNow();
            });
        }.bind(this));

        ws.addCommand('mutation', function(data){
            data = data._data;
            var action = data[0];
            var args = data.slice(1);
            if(action === 'initialize') {
                this.iframePromise = this.clearIframe().then(function(){
                    var doc = this.getIframeNode().contentWindow.document;
                    this.treeMirror = new TreeMirror(doc, treeMirrorDelegate(this));
                }.bind(this));
            }
            this.iframePromise.then(function() {
                this.treeMirror[action].apply(this.treeMirror, args);
            }.bind(this));
        }.bind(this));

        ws.addCommand('cookies', msg => this.saveCookies(msg._data));
    }.on('init'),

    connectionStatusType: 'warning',
    connectionStatusMessage: '',
    reconnectInteractions: null,
    _reconnectClock: function() {
        if (Number(this.get('connectionStatusTime')) >= 2) {
            this.decrementProperty('connectionStatusTime');
            this.set('connectionStatusMessage', 'Reconnecting to server in ' + this.get('connectionStatusTime') + ' seconds.');
            this.set('connectionAction', true);
        } else {
            this.set('connectionStatusMessage', 'Reconnecting...');
            this.set('connectionAction', null);
        }
    },

    connectionLost: function() {
        if (this.get('ws.closed')) {
            Ember.run.later(this, function() {
                this.set('showConnectionLost', true);
            }, 500);
            if (this.get('reconnectInteractions') === null) {
                var reconnect = this.get('canvas._interactionsBlocked');
                this.setInteractionsBlocked(true);
                this.set('reconnectInteractions', reconnect);
            }            this.set('connectionStatusTime', this.get('ws.reconnectTimeout') / 1000);
            this.addObserver('clock.second', this, this._reconnectClock);
        } else {
            this.removeObserver('clock.second', this, this._reconnectClock);
        }
    }.observes('ws.closed'),

    connectionReEstablished: function() {
        if (this.get('ws.opened')) {
            if (this.get('reconnectInteractions') !== null) {
                var reconnect = this.get('reconnectInteractions');
                this.set('reconnectInteractions', null);
                this.setInteractionsBlocked(reconnect);
                var listener = this.get('listener');
                if(listener && listener.reload && listener.get('loadedPageFp')) {
                    listener.reload();
                }
            }
            this.set('connectionStatusMessage', null);
            this.set('connectionAction', null);
            this.set('showConnectionLost', false);
        }
    }.observes('ws.opened'),

    connectionConnecting: function() {
        if (this.get('ws.connecting')) {
            this.set('connectionStatusMessage', 'Reconnecting...');
            this.set('connectionAction', null);
        }
    }.observes('ws.connecting'),

    fetchDocument: function(url, spider, fp, command) {
        var unique_id = this.shortGuid(),
            deferred = new Ember.RSVP.defer(),
            ifWindow = this.getIframeNode().contentWindow;
        this.set('ws_deferreds.' + unique_id, deferred);
        this.get('ws').send({
            _meta: {
                spider: spider,
                project: this.get('slyd.project'),
                id: unique_id,
                viewport: ifWindow.innerWidth + 'x' + ifWindow.innerHeight,
                user_agent: navigator.userAgent,
                cookies: this.cookies
            },
            _command: command || 'load',
            url: url
        });
        return deferred.promise;
    },

    setInteractionsBlocked: function(blocked) {
        if (this.get('reconnectInteractions') !== null) {
            this.set('reconnectInteractions', blocked);
            return;
        }
        if (this.get('canvas.interactionsBlocked') !== blocked) {
            this.set('canvas.interactionsBlocked', blocked);
        }
    },

    setIframeContent: function(doc) {
        if(typeof doc !== 'string') {
            return;
        }
        var iframe = Ember.$('#' + this.get('iframeId'));
        iframe.attr('srcdoc', doc);
        this.set('document.iframe', iframe);
    },

    clearIframe: function() {
        var defer = new Ember.RSVP.defer();
        var iframe = this.getIframeNode();
        var id = this.shortGuid();
        // Using a empty static page because using srcdoc or an data:uri gives
        // permission problems and/or broken baseURI behaviour in different browsers.
        iframe.setAttribute('src', '/static/empty-frame.html?' + id);
        iframe.removeAttribute('srcdoc');
        // Using a message to workaround onload bug on some browsers (cough IE cough).
        var $win = $(window).bind('message', function onMessage(e){
            if(e.originalEvent.data.frameReady === id){
                $win.unbind('message', onMessage);
                defer.resolve();
            }
        });
        return defer.promise;
    },

    frameEventListeners: [],
    addFrameEventListener: function(event, fn, useCapture=false){
        this.frameEventListeners.push([event, fn, useCapture]);
        this.getIframe()[0].addEventListener(event, fn, useCapture);
    },

    installEventHandlersForBrowsing: function() {
        this._super();
        var iframe = this.getIframe();
        iframe.on('keyup.portia keydown.portia keypress.portia input.portia ' +
                  'mousedown.portia mouseup.portia', this.postEvent.bind(this));
        this.addFrameEventListener("scroll", e => Ember.run.throttle(this, this.postEvent, e, 200), true);
        this.addFrameEventListener('focus', this.postEvent.bind(this), true);
        this.addFrameEventListener('blur', this.postEvent.bind(this), true);
        this.addFrameEventListener('change', this.postEvent.bind(this), true);
    },

    uninstallEventHandlers: function(){
        var frameDoc = this.getIframe()[0];
        this.frameEventListeners.forEach(([event, fn, useCapture]) => {
            frameDoc.removeEventListener(event, fn, useCapture);
        });
        this.frameEventListeners = [];
        this._super();
    },

    clickHandlerBrowse: function(evt) {
        if(evt.which > 1 || evt.ctrlKey) { // Ignore right/middle click or Ctrl+click
            return;
        }
        this.postEvent(evt);
        if(evt.target.tagName !== 'INPUT') {
            return this._super(evt);
        }
    },

    postEvent: function(evt){
        this.get('ws').send({
            _meta: {
                spider: this.get('slyd.spider'),
                project: this.get('slyd.project'),
            },
            _command: 'interact',
            interaction: interactionEvent(evt)
        });
    },

    bindResizeEvent: function() {
        Ember.$(window).on('resize', Ember.run.bind(this, this.handleResize));
    }.on('init'),

    handleResize: function() {
        var iframe_window = this.getIframeNode().contentWindow;
        this.get('ws').send({
            _command: 'resize',
            size: iframe_window.innerWidth + 'x' + iframe_window.innerHeight
        });
    },

    saveCookies: function(cookies){
        this.cookies = cookies;
        if(window.sessionStorage){
            window.sessionStorage.portia_cookies = JSON.stringify(cookies);
        }
    },
    loadCookies: function(){
        if(window.sessionStorage && sessionStorage.portia_cookies){
            this.cookies = JSON.parse(sessionStorage.portia_cookies);
        }
    }.on('init'),

    actions: {
        reconnectWebsocket: function() {
            this.get('ws').connect();
        }
    }
});
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// 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';

var format = require('util').format;
var inherits = require('util').inherits;

var errors = require('./errors');

module.exports.StateOptions = StateOptions;
module.exports.HealthyState = HealthyState;
module.exports.UnhealthyState = UnhealthyState;
module.exports.LockedHealthyState = LockedHealthyState;
module.exports.LockedUnhealthyState = LockedUnhealthyState;

function StateOptions(stateMachine, options) {
    options = options || {};
    // for setState changes
    this.stateMachine = stateMachine;
    // for downstream shouldRequest, used differently in Peer and Circuit.
    this.nextHandler = options.nextHandler;
    // for mocking tests
    this.timers = options.timers;
    this.timeHeap = options.timeHeap;
    this.random = options.random;
    // the number of miliseconds that healthy and unhealthy requests are
    // tracked between state reevaluation.
    this.period = options.period;
    // when healthy, the minimum number of requests during a period to trigger
    // state reevaluation.
    this.minimumRequests = options.minimumRequests;
    // when healthy, the failure rate for a period that will trigger a
    // transition to unhealthy.
    this.maxErrorRate = options.maxErrorRate;
    // when unhealthy, allow one request per period. this is the number of
    // consecutive periods that must have 100% healthy responses to trigger a
    // switch back to healthy.
    this.probation = options.probation;
}

/*
 * Collectively, the health states receive additional options through the peer
 * options:
 * - maxErrorRate (error rate to go from healthy to unhealthy)
 * - minResponseCount (response count to go from unhealthy to healthy)
 * - TODO
 *
 * They also inherit:
 * - channel.timers
 * - channel.random
 */

function State(options) {
    var self = this;

    self.stateMachine = options.stateMachine;
    self.nextHandler = options.nextHandler;
    self.timers = options.timers;
    self.timeHeap = options.timeHeap;
    self.random = options.random;
}

State.prototype.onDeactivate = function onDeactivate() {
};

State.prototype.onRequest = function onRequest(/* req */) {
};

State.prototype.onRequestHealthy = function onRequestHealthy() {
};

State.prototype.onRequestUnhealthy = function onRequestUnhealthy() {
};

State.prototype.onRequestError = function onRequestError(err) {
};

State.prototype.close = function close(callback) {
    callback(null);
};

State.prototype.shouldRequest = function shouldRequest(req, options) {
    var self = this;

    var now = self.timers.now();
    if (self.willCallNextHandler(now)) {
        return self.nextHandler.shouldRequest(req, options);
    } else {
        return 0;
    }
};

function PeriodicState(options) {
    var self = this;
    State.call(self, options);

    self.period = options.period || 1000; // ms
    self.start = 0;
    self.timeout = 0;
    self.periodTimer = null;

    self.startNewPeriod(self.timers.now());
}
inherits(PeriodicState, State);

PeriodicState.prototype.startNewPeriod = function startNewPeriod(now) {
    var self = this;

    self.start = now;
    self.setPeriodTimer(self.period, now);
    self.onNewPeriod();
};

PeriodicState.prototype.onDeactivate = function onDeactivate() {
    var self = this;

    if (self.periodTimer) {
        self.periodTimer.cancel();
        self.periodTimer = null;
    }
};

PeriodicState.prototype.setPeriodTimer = function setPeriodTimer(timeout, now) {
    var self = this;

    if (self.periodTimer) {
        self.periodTimer.cancel();
        self.periodTimer = null;
    }

    self.timeout = timeout;
    self.periodTimer = self.timeHeap.update(self, now);
};

PeriodicState.prototype.onTimeout = function onTimeout() {
    var self = this;

    var now = self.timers.now();
    self.checkPeriod(true, now);
};

PeriodicState.prototype.checkPeriod = function checkPeriod(inTimeout, now) {
    var self = this;

    var elapsed = now - self.start;
    var remain = self.period - elapsed;
    if (remain <= 0) {
        self.startNewPeriod(now);
    } else if (inTimeout) {
        self.setPeriodTimer(remain, now);
    }
};

PeriodicState.prototype.willCallNextHandler = function willCallNextHandler(now) {
    var self = this;

    self.checkPeriod(false, now);
};

function HealthyState(options) {
    var self = this;
    PeriodicState.call(self, options);

    self.maxErrorRate = options.maxErrorRate || 0.5;
    self.healthyCount = 0;
    self.unhealthyCount = 0;
    self.totalRequests = 0;
    self.minRequests = typeof options.minRequests === 'number' ?
        options.minRequests : 5;
}

inherits(HealthyState, PeriodicState);

HealthyState.prototype.type = 'tchannel.healthy';
HealthyState.prototype.healthy = true;
HealthyState.prototype.locked = false;

HealthyState.prototype.toString = function healthyToString() {
    var self = this;
    return format('[Healthy %s healthy %s unhealthy]', self.healthyCount, self.unhealthyCount);
};

HealthyState.prototype.willCallNextHandler = function willCallNextHandler(now) {
    var self = this;

    self.checkPeriod(false, now);

    // active unless .onNewPeriod transitioned
    return self.stateMachine.state === self;
};

HealthyState.prototype.onNewPeriod = function onNewPeriod(now) {
    var self = this;

    var totalCount = self.healthyCount + self.unhealthyCount;

    // TODO: could store on self for introspection, maybe call it
    // "lastPeriodErrorRate"?; we could even keep a fixed size sample of error
    // rates from periods and choose based on their differences (discrete
    // derivative)...
    var errorRate = self.unhealthyCount / totalCount;

    if (errorRate > self.maxErrorRate &&
        self.totalRequests > self.minRequests) {
        // Transition to unhealthy state if the healthy request rate dips below
        // the acceptable threshold.
        self.stateMachine.setState(UnhealthyState);
        // TODO: useful to mark self dead somehow? for now we're just using "am
        // I still the current state" logic coupled to the consuming
        // stateMachine in .willCallNextHandler
    } else {
        // okay last period, reset counts for the new period
        self.healthyCount = 0;
        self.unhealthyCount = 0;
    }
};

HealthyState.prototype.onRequestHealthy = function onRequestHealthy() {
    var self = this;
    self.healthyCount++;
    self.totalRequests++;
};

HealthyState.prototype.onRequestUnhealthy = function onRequestUnhealthy() {
    var self = this;
    self.totalRequests++;
    self.unhealthyCount++;
    self.checkPeriod(false, self.timers.now());
};

HealthyState.prototype.onRequestError = function onRequestError(err) {
    var self = this;

    self.totalRequests++;
    var codeString = errors.classify(err);
    if (errors.isUnhealthy(codeString)) {
        self.unhealthyCount++;
    } else {
        self.healthyCount++;
    }
    self.checkPeriod(false, self.timers.now());
};

function UnhealthyState(options) {
    var self = this;
    PeriodicState.call(self, options);

    self.minResponseCount = options.probation || 5;
    self.healthyCount = 0;
    self.triedThisPeriod = true;
}

inherits(UnhealthyState, PeriodicState);

UnhealthyState.prototype.type = 'tchannel.unhealthy';
UnhealthyState.prototype.healthy = false;
UnhealthyState.prototype.locked = false;

UnhealthyState.prototype.onNewPeriod = function onNewPeriod(now) {
    var self = this;

    self.triedThisPeriod = false;
};

UnhealthyState.prototype.toString = function healthyToString() {
    var self = this;
    return format('[Unhealthy %s consecutive healthy requests]', self.healthyCount);
};

UnhealthyState.prototype.willCallNextHandler = function willCallNextHandler(now) {
    var self = this;

    self.checkPeriod(false, now);

    // if .checkPeriod transitioned us back to healthy, we're done
    if (self.stateMachine.state !== self) {
        return false;
    }

    // Allow one trial per period
    return !self.triedThisPeriod;
};

UnhealthyState.prototype.onRequest = function onRequest(/* req */) {
    var self = this;

    self.triedThisPeriod = true;
    self.checkPeriod(false, self.timers.now());
};

UnhealthyState.prototype.onRequestHealthy = function onRequestHealthy() {
    var self = this;

    self.healthyCount++;
    if (self.healthyCount > self.minResponseCount) {
        self.stateMachine.setState(HealthyState);
    }
};

UnhealthyState.prototype.onRequestUnhealthy = function onRequestUnhealthy() {
    var self = this;

    self.healthyCount = 0;
};

UnhealthyState.prototype.onRequestError = function onRequestError(err) {
    var self = this;

    var codeString = errors.classify(err);
    if (!errors.isUnhealthy(codeString)) {
        self.onRequestHealthy();
        return;
    }

    self.healthyCount = 0;
};

function LockedHealthyState(options) {
    var self = this;

    State.call(self, options);
}

inherits(LockedHealthyState, State);

LockedHealthyState.prototype.type = 'tchannel.healthy-locked';
LockedHealthyState.prototype.healthy = true;
LockedHealthyState.prototype.locked = true;

LockedHealthyState.prototype.toString = function lockedHealthyToString() {
    return '[Healthy state (locked)]';
};

LockedHealthyState.prototype.willCallNextHandler = function willCallNextHandler() {
    return true;
};

LockedHealthyState.prototype.shouldRequest = function shouldRequest(req, options) {
    var self = this;

    return self.nextHandler.shouldRequest(req, options);
};

function LockedUnhealthyState(options) {
    var self = this;

    State.call(self, options);
}

inherits(LockedUnhealthyState, State);

LockedUnhealthyState.prototype.type = 'tchannel.unhealthy-locked';
LockedUnhealthyState.prototype.healthy = false;
LockedUnhealthyState.prototype.locked = true;

LockedUnhealthyState.prototype.toString = function lockedUnhealthyToString() {
    return '[Unhealthy state (locked)]';
};

LockedUnhealthyState.prototype.willCallNextHandler = function willCallNextHandler() {
    return false;
};

LockedUnhealthyState.prototype.shouldRequest = function shouldRequest(req, options) {
    return 0;
};
<commit_msg>State: add stateMachine score invalidation support<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// 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';

var format = require('util').format;
var inherits = require('util').inherits;

var errors = require('./errors');

module.exports.StateOptions = StateOptions;
module.exports.HealthyState = HealthyState;
module.exports.UnhealthyState = UnhealthyState;
module.exports.LockedHealthyState = LockedHealthyState;
module.exports.LockedUnhealthyState = LockedUnhealthyState;

function StateOptions(stateMachine, options) {
    options = options || {};
    // for setState changes
    this.stateMachine = stateMachine;
    // for downstream shouldRequest, used differently in Peer and Circuit.
    this.nextHandler = options.nextHandler;
    // for mocking tests
    this.timers = options.timers;
    this.timeHeap = options.timeHeap;
    this.random = options.random;
    // the number of miliseconds that healthy and unhealthy requests are
    // tracked between state reevaluation.
    this.period = options.period;
    // when healthy, the minimum number of requests during a period to trigger
    // state reevaluation.
    this.minimumRequests = options.minimumRequests;
    // when healthy, the failure rate for a period that will trigger a
    // transition to unhealthy.
    this.maxErrorRate = options.maxErrorRate;
    // when unhealthy, allow one request per period. this is the number of
    // consecutive periods that must have 100% healthy responses to trigger a
    // switch back to healthy.
    this.probation = options.probation;
}

/*
 * Collectively, the health states receive additional options through the peer
 * options:
 * - maxErrorRate (error rate to go from healthy to unhealthy)
 * - minResponseCount (response count to go from unhealthy to healthy)
 * - TODO
 *
 * They also inherit:
 * - channel.timers
 * - channel.random
 */

function State(options) {
    var self = this;

    self.stateMachine = options.stateMachine;
    self.nextHandler = options.nextHandler;
    self.timers = options.timers;
    self.timeHeap = options.timeHeap;
    self.random = options.random;
}

State.prototype.onDeactivate = function onDeactivate() {
};

State.prototype.onRequest = function onRequest(/* req */) {
};

State.prototype.onRequestHealthy = function onRequestHealthy() {
};

State.prototype.onRequestUnhealthy = function onRequestUnhealthy() {
};

State.prototype.onRequestError = function onRequestError(err) {
};

State.prototype.close = function close(callback) {
    callback(null);
};

State.prototype.invalidate = function invalidate() {
    var self = this;

    if (self.stateMachine.invalidateScore) {
        self.stateMachine.invalidateScore();
    }
};

State.prototype.shouldRequest = function shouldRequest(req, options) {
    var self = this;

    var now = self.timers.now();
    if (self.willCallNextHandler(now)) {
        return self.nextHandler.shouldRequest(req, options);
    } else {
        return 0;
    }
};

function PeriodicState(options) {
    var self = this;
    State.call(self, options);

    self.period = options.period || 1000; // ms
    self.start = 0;
    self.timeout = 0;
    self.periodTimer = null;

    self.startNewPeriod(self.timers.now());
}
inherits(PeriodicState, State);

PeriodicState.prototype.startNewPeriod = function startNewPeriod(now) {
    var self = this;

    self.start = now;
    self.setPeriodTimer(self.period, now);
    self.onNewPeriod();
};

PeriodicState.prototype.onDeactivate = function onDeactivate() {
    var self = this;

    if (self.periodTimer) {
        self.periodTimer.cancel();
        self.periodTimer = null;
    }
};

PeriodicState.prototype.setPeriodTimer = function setPeriodTimer(timeout, now) {
    var self = this;

    if (self.periodTimer) {
        self.periodTimer.cancel();
        self.periodTimer = null;
    }

    self.timeout = timeout;
    self.periodTimer = self.timeHeap.update(self, now);
};

PeriodicState.prototype.onTimeout = function onTimeout() {
    var self = this;

    var now = self.timers.now();
    self.checkPeriod(true, now);
};

PeriodicState.prototype.checkPeriod = function checkPeriod(inTimeout, now) {
    var self = this;

    var elapsed = now - self.start;
    var remain = self.period - elapsed;
    if (remain <= 0) {
        self.startNewPeriod(now);
    } else if (inTimeout) {
        self.setPeriodTimer(remain, now);
    }
};

PeriodicState.prototype.willCallNextHandler = function willCallNextHandler(now) {
    var self = this;

    self.checkPeriod(false, now);
};

function HealthyState(options) {
    var self = this;
    PeriodicState.call(self, options);

    self.maxErrorRate = options.maxErrorRate || 0.5;
    self.healthyCount = 0;
    self.unhealthyCount = 0;
    self.totalRequests = 0;
    self.minRequests = typeof options.minRequests === 'number' ?
        options.minRequests : 5;
}

inherits(HealthyState, PeriodicState);

HealthyState.prototype.type = 'tchannel.healthy';
HealthyState.prototype.healthy = true;
HealthyState.prototype.locked = false;

HealthyState.prototype.toString = function healthyToString() {
    var self = this;
    return format('[Healthy %s healthy %s unhealthy]', self.healthyCount, self.unhealthyCount);
};

HealthyState.prototype.willCallNextHandler = function willCallNextHandler(now) {
    var self = this;

    self.checkPeriod(false, now);

    // active unless .onNewPeriod transitioned
    return self.stateMachine.state === self;
};

HealthyState.prototype.onNewPeriod = function onNewPeriod(now) {
    var self = this;

    var totalCount = self.healthyCount + self.unhealthyCount;

    // TODO: could store on self for introspection, maybe call it
    // "lastPeriodErrorRate"?; we could even keep a fixed size sample of error
    // rates from periods and choose based on their differences (discrete
    // derivative)...
    var errorRate = self.unhealthyCount / totalCount;

    if (errorRate > self.maxErrorRate &&
        self.totalRequests > self.minRequests) {
        // Transition to unhealthy state if the healthy request rate dips below
        // the acceptable threshold.
        self.stateMachine.setState(UnhealthyState);
        // TODO: useful to mark self dead somehow? for now we're just using "am
        // I still the current state" logic coupled to the consuming
        // stateMachine in .willCallNextHandler
    } else {
        // okay last period, reset counts for the new period
        self.healthyCount = 0;
        self.unhealthyCount = 0;
    }
};

HealthyState.prototype.onRequestHealthy = function onRequestHealthy() {
    var self = this;
    self.healthyCount++;
    self.totalRequests++;
};

HealthyState.prototype.onRequestUnhealthy = function onRequestUnhealthy() {
    var self = this;
    self.totalRequests++;
    self.unhealthyCount++;
    self.checkPeriod(false, self.timers.now());
};

HealthyState.prototype.onRequestError = function onRequestError(err) {
    var self = this;

    self.totalRequests++;
    var codeString = errors.classify(err);
    if (errors.isUnhealthy(codeString)) {
        self.unhealthyCount++;
    } else {
        self.healthyCount++;
    }
    self.checkPeriod(false, self.timers.now());
};

function UnhealthyState(options) {
    var self = this;
    PeriodicState.call(self, options);

    self.minResponseCount = options.probation || 5;
    self.healthyCount = 0;
    self.triedThisPeriod = true;
}

inherits(UnhealthyState, PeriodicState);

UnhealthyState.prototype.type = 'tchannel.unhealthy';
UnhealthyState.prototype.healthy = false;
UnhealthyState.prototype.locked = false;

UnhealthyState.prototype.onNewPeriod = function onNewPeriod(now) {
    var self = this;

    self.triedThisPeriod = false;
};

UnhealthyState.prototype.toString = function healthyToString() {
    var self = this;
    return format('[Unhealthy %s consecutive healthy requests]', self.healthyCount);
};

UnhealthyState.prototype.willCallNextHandler = function willCallNextHandler(now) {
    var self = this;

    self.checkPeriod(false, now);

    // if .checkPeriod transitioned us back to healthy, we're done
    if (self.stateMachine.state !== self) {
        return false;
    }

    // Allow one trial per period
    return !self.triedThisPeriod;
};

UnhealthyState.prototype.onRequest = function onRequest(/* req */) {
    var self = this;

    self.triedThisPeriod = true;
    self.checkPeriod(false, self.timers.now());
};

UnhealthyState.prototype.onRequestHealthy = function onRequestHealthy() {
    var self = this;

    self.healthyCount++;
    if (self.healthyCount > self.minResponseCount) {
        self.stateMachine.setState(HealthyState);
    }
};

UnhealthyState.prototype.onRequestUnhealthy = function onRequestUnhealthy() {
    var self = this;

    self.healthyCount = 0;
};

UnhealthyState.prototype.onRequestError = function onRequestError(err) {
    var self = this;

    var codeString = errors.classify(err);
    if (!errors.isUnhealthy(codeString)) {
        self.onRequestHealthy();
        return;
    }

    self.healthyCount = 0;
};

function LockedHealthyState(options) {
    var self = this;

    State.call(self, options);
}

inherits(LockedHealthyState, State);

LockedHealthyState.prototype.type = 'tchannel.healthy-locked';
LockedHealthyState.prototype.healthy = true;
LockedHealthyState.prototype.locked = true;

LockedHealthyState.prototype.toString = function lockedHealthyToString() {
    return '[Healthy state (locked)]';
};

LockedHealthyState.prototype.willCallNextHandler = function willCallNextHandler() {
    return true;
};

LockedHealthyState.prototype.shouldRequest = function shouldRequest(req, options) {
    var self = this;

    return self.nextHandler.shouldRequest(req, options);
};

function LockedUnhealthyState(options) {
    var self = this;

    State.call(self, options);
}

inherits(LockedUnhealthyState, State);

LockedUnhealthyState.prototype.type = 'tchannel.unhealthy-locked';
LockedUnhealthyState.prototype.healthy = false;
LockedUnhealthyState.prototype.locked = true;

LockedUnhealthyState.prototype.toString = function lockedUnhealthyToString() {
    return '[Unhealthy state (locked)]';
};

LockedUnhealthyState.prototype.willCallNextHandler = function willCallNextHandler() {
    return false;
};

LockedUnhealthyState.prototype.shouldRequest = function shouldRequest(req, options) {
    return 0;
};
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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
//
//     https://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.

/* eslint-disable no-unused-vars */

let googleAuth;

const API_KEY = '';
const CLIENT_ID = '';
const SCOPE = 'profile email';
const DISCOVERY_DOCS = [];

/**
 * Creates a new element with a specified type, class and innerText.
 * @param {string} elementType The type of the element that will be created.
 * @param {string} className The class of the element that will be created.
 * @param {string} innerText The innerText of the element that will be
 * created.
 * @return {element} The element created.
 */
function createElement(elementType, className, innerText) {
  const newElement = document.createElement(elementType);
  newElement.className = className;
  newElement.innerText = innerText;

  return newElement;
}

/**
 * Initializes the GoogleAuth object and checks if the user is on the
 * right page based on their login status. If not, redirects them to
 * the appropriate page.
 */
async function initClient() {
  await initGoogleAuthObject();
  googleAuth = gapi.auth2.getAuthInstance();
}

/**
 * Initializes the JavaScript object and loads the gapi.auth2 module
 * to perform OAuth.
 */
async function initGoogleAuthObject() {
  await gapi.auth2.init({
    'apiKey': API_KEY,
    'clientId': CLIENT_ID,
    'discoveryDocs': DISCOVERY_DOCS,
    'scope': SCOPE,
    'prompt': 'select_account',
  });
}

/**
 * Loads the required Google APIs modules and initializes the client.
 */
function loadClient() {
  gapi.load('client:auth2', initClient);
}

/**
 * Loads the profile data associated with the currently logged in user.
 */
async function loadProfileData() {
  if (googleAuth.isSignedIn.get()) {
    const userProfile = googleAuth.currentUser.get().getBasicProfile();
    const menuElement = document.getElementById('menu');
    const profilePicture = createElement('img', 'profile-picture', '');
    profilePicture.src = userProfile.getImageUrl();
    menuElement.prepend(profilePicture);
  } else {
    window.location.href = 'index.html';
  }
}

/**
 * Signs in the user, updates the authorization status and redirects them
 * to the home page (only if they also provide the authorization needed).
 */
async function signIn() {
  const authResult = await googleAuth.signIn();

  if (googleAuth.isSignedIn.get()) {
    const idToken = authResult.getAuthResponse().id_token;
    document.cookie = 'id_token=' + encodeURIComponent(idToken);
    await fetch('/add-user', {method: 'POST'});
    window.location.href = 'home-page.html';
  }
}

/**
 * Signs out the user, deletes the cookie and redirects them to the start page.
 */
function signOut() {
  googleAuth.signOut();
  document.cookie = 'id_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC;';
  window.location.href = 'index.html';
}

window.signIn = signIn;
window.addEventListener('load', loadClient);

export {initClient, createElement, loadProfileData, signOut};
<commit_msg>Remove local variable<commit_after>// Copyright 2020 Google LLC
//
// 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
//
//     https://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.

/* eslint-disable no-unused-vars */

const API_KEY = '';
const CLIENT_ID = '';
const SCOPE = 'profile email';
const DISCOVERY_DOCS = [];

/**
 * Creates a new element with a specified type, class and innerText.
 * @param {string} elementType The type of the element that will be created.
 * @param {string} className The class of the element that will be created.
 * @param {string} innerText The innerText of the element that will be
 * created.
 * @return {element} The element created.
 */
function createElement(elementType, className, innerText) {
  const newElement = document.createElement(elementType);
  newElement.className = className;
  newElement.innerText = innerText;

  return newElement;
}

/**
 * Initializes the GoogleAuth object and checks if the user is on the
 * right page based on their login status. If not, redirects them to
 * the appropriate page.
 */
async function initClient() {
  await initGoogleAuthObject();
}

/**
 * Initializes the JavaScript object and loads the gapi.auth2 module
 * to perform OAuth.
 */
async function initGoogleAuthObject() {
  await gapi.auth2.init({
    'apiKey': API_KEY,
    'clientId': CLIENT_ID,
    'discoveryDocs': DISCOVERY_DOCS,
    'scope': SCOPE,
    'prompt': 'select_account',
  });
}

/**
 * Loads the required Google APIs modules and initializes the client.
 */
function loadClient() {
  gapi.load('client:auth2', initClient);
}

/**
 * Loads the profile data associated with the currently logged in user.
 */
async function loadProfileData() {
  const googleAuth = gapi.auth2.getAuthInstance();

  if (googleAuth.isSignedIn.get()) {
    const userProfile = googleAuth.currentUser.get().getBasicProfile();
    const menuElement = document.getElementById('menu');
    const profilePicture = createElement('img', 'profile-picture', '');
    profilePicture.src = userProfile.getImageUrl();
    menuElement.prepend(profilePicture);
  } else {
    window.location.href = 'index.html';
  }
}

/**
 * Signs in the user, updates the authorization status and redirects them
 * to the home page (only if they also provide the authorization needed).
 */
async function signIn() {
  const googleAuth = gapi.auth2.getAuthInstance();
  const authResult = await googleAuth.signIn();

  if (googleAuth.isSignedIn.get()) {
    const idToken = authResult.getAuthResponse().id_token;
    document.cookie = 'id_token=' + encodeURIComponent(idToken);
    await fetch('/add-user', {method: 'POST'});
    window.location.href = 'home-page.html';
  }
}

/**
 * Signs out the user, deletes the cookie and redirects them to the start page.
 */
function signOut() {
  const googleAuth = gapi.auth2.getAuthInstance();
  googleAuth.signOut();
  document.cookie = 'id_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC;';
  window.location.href = 'index.html';
}

window.signIn = signIn;
window.addEventListener('load', loadClient);

export {initClient, createElement, loadProfileData, signOut};
<|endoftext|>


<commit_before>/*jshint node:true*/

// ldp.js
// This file contains the server side JavaScript code for your application.
// This sample application uses express as web application framework (http://expressjs.com/),
// and jade as template engine (http://jade-lang.com/).

var express = require('express');
var rdfstore = require('rdfstore');

// setup middleware
var app = express();
app.set('view engine', 'jade');
app.set('views', __dirname + '/views'); //optional since express defaults to CWD/views

// setup rdfstore/mongo
var storage='mongodb';
var store;

if (storage === 'mongodb') {
   store = new rdfstore.Store({persistent:true,
                    engine:'mongodb',
                    name:'ldpjs',
                    overwrite:false,
                    mongoDomain:'localhost',
                    mongoPort:27017
                   }, function(ldpjsDB){
                   	   store = ldpjsDB;
                   });
} else {
   store = rdfstore.create();
}

app.use(function(err, req, res, next){
	console.error(err.stack);
	res.send(500, 'Something broke!');
});

app.route('/resources/*')
.all(function(req, res, next) {
	res.links({
		type: 'http://www.w3.org/ns/ldp#Resource'
	});
	next();
})
.get(function(req, res, next) {
	res.send('GET ' + req.path);
})
.put(function(req, res, next) {
	res.send('PUT ' + req.path);
})
.post(function(req, res, next) {
	res.send('POST ' + req.path);
})
.delete(function(req, res, next) {
	res.send('DELETE ' + req.path);
});

// render index page
app.get('/', function(req, res){
	res.render('index');
});

// There are many useful environment variables available in process.env.
// VCAP_APPLICATION contains useful information about a deployed application.
var appInfo = JSON.parse(process.env.VCAP_APPLICATION || "{}");
// TODO: Get application information and use it in your app.

// VCAP_SERVICES contains all the credentials of services bound to
// this application. For details of its content, please refer to
// the document or sample of each service.
var services = JSON.parse(process.env.VCAP_SERVICES || "{}");
// TODO: Get service credentials and communicate with bluemix services.

// The IP address of the Cloud Foundry DEA (Droplet Execution Agent) that hosts this application:
var host = (process.env.VCAP_APP_HOST || 'localhost');
// The port on the DEA for communication with the application:
var port = (process.env.VCAP_APP_PORT || 3000);
// Start server
app.listen(port, host);
console.log('App started on port ' + port);

<commit_msg>fix problem with static files<commit_after>/*jshint node:true*/

// ldp.js
// This file contains the server side JavaScript code for your application.
// This sample application uses express as web application framework (http://expressjs.com/),
// and jade as template engine (http://jade-lang.com/).

var express = require('express');
var rdfstore = require('rdfstore');

// setup middleware
var app = express();
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'jade');
app.set('views', __dirname + '/views'); //optional since express defaults to CWD/views

// setup rdfstore/mongo
var storage='mongodb';
var store;

if (storage === 'mongodb') {
   store = new rdfstore.Store({persistent:true,
                    engine:'mongodb',
                    name:'ldpjs',
                    overwrite:false,
                    mongoDomain:'localhost',
                    mongoPort:27017
                   }, function(ldpjsDB){
                   	   store = ldpjsDB;
                   });
} else {
   store = rdfstore.create();
}

app.use(function(err, req, res, next){
	console.error(err.stack);
	res.send(500, 'Something broke!');
});

app.route('/resources/*')
.all(function(req, res, next) {
	res.links({
		type: 'http://www.w3.org/ns/ldp#Resource'
	});
	next();
})
.get(function(req, res, next) {
	res.send('GET ' + req.path);
})
.put(function(req, res, next) {
	res.send('PUT ' + req.path);
})
.post(function(req, res, next) {
	res.send('POST ' + req.path);
})
.delete(function(req, res, next) {
	res.send('DELETE ' + req.path);
});

// render index page
app.get('/', function(req, res){
	res.render('index');
});

// There are many useful environment variables available in process.env.
// VCAP_APPLICATION contains useful information about a deployed application.
var appInfo = JSON.parse(process.env.VCAP_APPLICATION || "{}");
// TODO: Get application information and use it in your app.

// VCAP_SERVICES contains all the credentials of services bound to
// this application. For details of its content, please refer to
// the document or sample of each service.
var services = JSON.parse(process.env.VCAP_SERVICES || "{}");
// TODO: Get service credentials and communicate with bluemix services.

// The IP address of the Cloud Foundry DEA (Droplet Execution Agent) that hosts this application:
var host = (process.env.VCAP_APP_HOST || 'localhost');
// The port on the DEA for communication with the application:
var port = (process.env.VCAP_APP_PORT || 3000);
// Start server
app.listen(port, host);
console.log('App started on port ' + port);

<|endoftext|>


<commit_before><commit_msg>Implement leaveRoomGivenUserIdAndRoomId<commit_after>const db = require('../db.js');

module.exports = (userId, roomId) => {
  const findNumberActiveParticipantsQueryStr = `
    SELECT number_active_participants FROM rooms
      WHERE id = '${roomId}'
  `;
  const removeUserRoomsQueryStr = `
    DELETE FROM users_rooms
      WHERE room_id = '${roomId}'
  `;
  const removeRoomQueryStr = `
    DELETE FROM rooms
      WHERE id = '${roomId}'
  `;
  const hideUserRoomQueryStr = `
    UPDATE users_rooms
      SET \`show\` = false
      WHERE user_id = '${userId}' AND room_id = '${roomId}'
  `;
  const reduceNumberActiveParticipantsQueryStr = `
    UPDATE rooms
      SET \`number_active_participants\` = \`number_active_participants\` - 1
      WHERE id = '${roomId}'
  `;
  return db.query(findNumberActiveParticipantsQueryStr).spread(results => {
    if (results[0] == 1) {
      return db.query(removeUserRoomsQueryStr)
               .spread(() => (
                 db.query(removeRoomQueryStr)
                   .spread(room => room)
               ));
    }
    return db.query(hideUserRoomQueryStr)
             .spread(() => (
                db.query(reduceNumberActiveParticipantsQueryStr)
                  .spread(room => room)
              ));
  });
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

/*
    ___ usage ___ en_US ___
    usage: prolific <options> <program>

    options:

        -i, --inherit   <number>
            file handles to inherit

        -s, --scram     <number>
            number of seconds to wait for children to exit

        -p, --processor <path>
            path to processor

        --help
            display this message

    ___ $ ___ en_US ___

        log is required:
            the `--log` address and port is a required argument

        port is not an integer:
            the `--log` port must be an integer

    ___ . ___
*/

// Node.js API.
const assert = require('assert')
const path = require('path')
const url = require('url')
const children = require('child_process')
const crypto = require('crypto')
const fs = require('fs').promises

const once = require('prospective/once')
const callback = require('prospective/callback')

const rimraf = require('rimraf')

const fnv = require('hash.fnv')

const Isochronous = require('isochronous')

// Route messages through a process hierarchy using Node.js IPC.
const Descendent = require('descendent')

// Exceptions that you can catch by type.
const Interrupt = require('interrupt').create('prolific')

// Command line and environment interpretation utilities.
const inherit = require('prolific.inherit')

// Monitoring of streams that contain logging messages.
const Collector = require('prolific.collector')
const Watcher = require('prolific.watcher')

// Pass messages and sockets all around the process tree.
const descendent = require('foremost')('descendent')

const coalesce = require('extant')

const Tmp = require('./tmp')
const Killer = require('./killer')

// TODO Note that; we now require that anyone standing between a root Prolific
// sidecar and a leaf child process use the Descendent library. When I write
// multi-process applications I use [Olio](https://github.com/bigeasy/olio)
// where this is already implemented.
require('arguable')(module, {}, async arguable => {
    arguable.ultimate.ipc = true

    arguable.helpIf(arguable.ultimate.help)

    const processor = arguable.ultimate.processor

    process.env.PROLIFIC_SUPERVISOR_PROCESS_ID = process.pid

    const sidecars = {}
    const pids = {}
    const pipes = {} // See race condition below.

    const supervise = {
        sidecar: async (sidecar, pid) => {
            const [ exitCode, signal ] = await once(sidecar, 'exit').promise
            Interrupt.assert(exitCode == 0, 'sidecar.exit', {
                exitCode: exitCode,
                signal: signal,
                argv: arguable.argv
            })
            delete sidecars[pid]
        },
        child: async (child) => {
            const [ exitCode, signal ] = await once(child, 'exit').promise
            // Will only ever equal zero. We do not have the `null, "SIGTERM"`
            // combo because we always register a `SIGTERM` handler. The
            // `"SIGTERM"` response is only when the default hander fires. The
            // `"SIGTERM"` is determined by whether or not the child has a
            // `"SIGTERM"` handler, not by any action by the parent. (i.e.
            // whether or not the parent calles `child.kill()`. The behavior is
            // still the same if we send a kill signal from the shell.
            Interrupt.assert(exitCode == 0, 'child.exit', {
                exitCode: exitCode,
                signal: signal,
                argv: arguable.argv
            })
        }
    }

    descendent.increment()
    try {
        // TODO What do you really want to name this?
        const Destructible = require('destructible')
        const destructible = new Destructible(1500, 'prolific')

        const tmp = await Tmp(coalesce(process.env.TMPDIR, '/tmp'), async () => {
            const [ bytes ] = await callback(callback => crypto.randomBytes(16, callback))
            return bytes.toString('hex')
        }, process.pid)
        destructible.destruct(() => callback(callback => rimraf(tmp, callback)))

        await fs.mkdir(path.resolve(tmp, 'stage'))
        await fs.mkdir(path.resolve(tmp, 'publish'))

        process.env.PROLIFIC_TMPDIR = tmp

        const toucher = new Isochronous(60000, true, async () => {
            const now = Date.now()
            await fs.utimes(tmp, now, now)
            await fs.utimes(path.resolve(tmp, 'stage'), now, now)
            await fs.utimes(path.resolve(tmp, 'publish'), now, now)
        })

        destructible.durable('toucher', toucher.start())
        destructible.destruct(() => toucher.stop())

        const killer = new Killer(100, 30000)

        killer.on('killed', pid => {
            watcher.killed(pid)
        })
        killer.on('drain', () => destructible.destroy())

        destructible.durable('killer', killer.run())
        destructible.destruct(() => killer.destroy())

        const watcher = new Watcher(destructible.durable('watcher'), buffer => {
            return fnv(0, buffer, 0, buffer.length)
        }, path.join(tmp, 'publish'))

        const collector = new Collector

        // watcher.on('notice', notice => console.log(notice))

        watcher.on('data', data => collector.data(data))

        collector.on('data', data => {
            const pid = data.pid
            switch (data.body.method) {
            case 'start': {
                    const sidecar = children.spawn('node', [
                        path.join(__dirname, 'sidecar.bin.js'),
                        '--configuration', processor,
                        '--supervisor', process.pid
                    ], {
                        stdio: [ 'inherit', 'inherit', 'inherit', 'pipe', 'ipc' ]
                    })
                    sidecars[pid] = sidecar
                    pipes[pid] = sidecar.stdio[3]
                    descendent.addChild(sidecar, { path: data.path, pid: pid })
                    destructible.ephemeral([ 'sidecar', sidecar.pid ], supervise.sidecar(sidecar, pid))
                }
                break
            case 'eos': {
                    killer.exited(pid)
                    // TODO No need to `killer.purge()`, we can absolutely remove
                    // the pid from the `Killer` here.
                    const sidecar = sidecars[pid]
                    descendent.down([ sidecar.pid ], 'prolific:synchronous', null)
                }
                break
            default: {
                    killer.exit(pid)
                    const sidecar = sidecars[pid]
                    // **TODO** Wait on callback?
                    descendent.down([ sidecar.pid ], 'prolific:synchronous', data.body)
                }
                break
            }
        })

        const stdio = inherit(arguable)
        stdio.push('ipc')

        const argv = arguable.argv.slice()
        // TODO Restore inheritance.
        const child = children.spawn(argv.shift(), argv, { stdio: stdio })
        // TODO Maybe have something to call to notify of failure to finish.
        // destructible.destruct(() => child.kill())

        descendent.addChild(child, null)

        descendent.on('prolific:pipe', function (message) {
            const pid = message.cookie.path[message.cookie.path.length - 1]
            descendent.down(message.cookie.path, 'prolific:pipe', true, coalesce(pipes[pid]))
        })

        destructible.ephemeral('close', supervise.child(child))

        await arguable.destroyed
        destructible.destroy()
        await destructible.promise

        return 0
    } finally {
        descendent.decrement()
    }
})
<commit_msg>Another hanging sidecar.<commit_after>#!/usr/bin/env node

/*
    ___ usage ___ en_US ___
    usage: prolific <options> <program>

    options:

        -i, --inherit   <number>
            file handles to inherit

        -s, --scram     <number>
            number of seconds to wait for children to exit

        -p, --processor <path>
            path to processor

        --help
            display this message

    ___ $ ___ en_US ___

        log is required:
            the `--log` address and port is a required argument

        port is not an integer:
            the `--log` port must be an integer

    ___ . ___
*/

// Node.js API.
const assert = require('assert')
const path = require('path')
const url = require('url')
const children = require('child_process')
const crypto = require('crypto')
const fs = require('fs').promises

const once = require('prospective/once')
const callback = require('prospective/callback')

const rimraf = require('rimraf')

const fnv = require('hash.fnv')

const Isochronous = require('isochronous')

// Route messages through a process hierarchy using Node.js IPC.
const Descendent = require('descendent')

// Exceptions that you can catch by type.
const Interrupt = require('interrupt').create('prolific')

// Command line and environment interpretation utilities.
const inherit = require('prolific.inherit')

// Monitoring of streams that contain logging messages.
const Collector = require('prolific.collector')
const Watcher = require('prolific.watcher')

// Pass messages and sockets all around the process tree.
const descendent = require('foremost')('descendent')

const coalesce = require('extant')

const Tmp = require('./tmp')
const Killer = require('./killer')

// TODO Note that; we now require that anyone standing between a root Prolific
// sidecar and a leaf child process use the Descendent library. When I write
// multi-process applications I use [Olio](https://github.com/bigeasy/olio)
// where this is already implemented.
require('arguable')(module, {}, async arguable => {
    arguable.ultimate.ipc = true

    arguable.helpIf(arguable.ultimate.help)

    const processor = arguable.ultimate.processor

    process.env.PROLIFIC_SUPERVISOR_PROCESS_ID = process.pid

    const sidecars = {}
    const pids = {}
    const pipes = {} // See race condition below.

    const supervise = {
        sidecar: async (sidecar, pid) => {
            const [ exitCode, signal ] = await once(sidecar, 'exit').promise
            Interrupt.assert(exitCode == 0, 'sidecar.exit', {
                exitCode: exitCode,
                signal: signal,
                argv: arguable.argv
            })
            delete sidecars[pid]
        },
        child: async (child) => {
            const [ exitCode, signal ] = await once(child, 'exit').promise
            // Will only ever equal zero. We do not have the `null, "SIGTERM"`
            // combo because we always register a `SIGTERM` handler. The
            // `"SIGTERM"` response is only when the default hander fires. The
            // `"SIGTERM"` is determined by whether or not the child has a
            // `"SIGTERM"` handler, not by any action by the parent. (i.e.
            // whether or not the parent calles `child.kill()`. The behavior is
            // still the same if we send a kill signal from the shell.
            Interrupt.assert(exitCode == 0, 'child.exit', {
                exitCode: exitCode,
                signal: signal,
                argv: arguable.argv
            })
        }
    }

    descendent.increment()
    try {
        // TODO What do you really want to name this?
        const Destructible = require('destructible')
        const destructible = new Destructible(1500, 'prolific')

        const tmp = await Tmp(coalesce(process.env.TMPDIR, '/tmp'), async () => {
            const [ bytes ] = await callback(callback => crypto.randomBytes(16, callback))
            return bytes.toString('hex')
        }, process.pid)
        destructible.destruct(() => callback(callback => rimraf(tmp, callback)))

        await fs.mkdir(path.resolve(tmp, 'stage'))
        await fs.mkdir(path.resolve(tmp, 'publish'))

        process.env.PROLIFIC_TMPDIR = tmp

        const toucher = new Isochronous(60000, true, async () => {
            const now = Date.now()
            await fs.utimes(tmp, now, now)
            await fs.utimes(path.resolve(tmp, 'stage'), now, now)
            await fs.utimes(path.resolve(tmp, 'publish'), now, now)
        })

        destructible.durable('toucher', toucher.start())
        destructible.destruct(() => toucher.stop())

        const killer = new Killer(100, 30000)

        killer.on('killed', pid => {
            watcher.killed(pid)
        })
        killer.on('drain', () => destructible.destroy())

        destructible.durable('killer', killer.run())
        destructible.destruct(() => killer.destroy())

        const watcher = new Watcher(destructible.durable('watcher'), buffer => {
            return fnv(0, buffer, 0, buffer.length)
        }, path.join(tmp, 'publish'))

        const collector = new Collector

        // watcher.on('notice', notice => console.log(notice))

        watcher.on('data', data => collector.data(data))

        collector.on('data', data => {
            const pid = data.pid
            switch (data.body.method) {
            case 'start': {
                    const sidecar = children.spawn('node', [
                        path.join(__dirname, 'sidecar.bin.js'),
                        '--configuration', processor,
                        '--supervisor', process.pid
                    ], {
                        stdio: [ 'inherit', 'inherit', 'inherit', 'pipe', 'ipc' ]
                    })
                    sidecars[pid] = sidecar
                    pipes[pid] = sidecar.stdio[3]
                    descendent.addChild(sidecar, { path: data.path, pid: pid })
                    destructible.ephemeral([ 'sidecar', sidecar.pid ], supervise.sidecar(sidecar, pid))
                }
                break
            case 'eos': {
                    killer.exited(pid)
                    // TODO No need to `killer.purge()`, we can absolutely remove
                    // the pid from the `Killer` here.
                    const sidecar = sidecars[pid]
                    descendent.down([ sidecar.pid ], 'prolific:synchronous', null)
                }
                break
            default: {
                    killer.exit(pid)
                    const sidecar = sidecars[pid]
                    // **TODO** Wait on callback?
                    descendent.down([ sidecar.pid ], 'prolific:synchronous', data.body)
                }
                break
            }
        })

        const stdio = inherit(arguable)
        stdio.push('ipc')

        const argv = arguable.argv.slice()
        // TODO Restore inheritance.
        const child = children.spawn(argv.shift(), argv, { stdio: stdio })
        // TODO Maybe have something to call to notify of failure to finish.
        // destructible.destruct(() => child.kill())

        descendent.addChild(child, null)

        descendent.on('prolific:pipe', function (message) {
            const pid = message.cookie.path[message.cookie.path.length - 1]
            descendent.down(message.cookie.path, 'prolific:pipe', true, coalesce(pipes[pid]))
        })

        destructible.ephemeral('close', supervise.child(child))

        await arguable.destroyed
        destructible.destroy()
        await destructible.promise

        return 0
    } catch (error) {
        console.log(killer.destroyed, killer._pids)
        throw error
    } finally {
        descendent.decrement()
    }
})
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

var OAuth = require('oauth').OAuth,
    pjson = require('../../../package.json'),
    Evernote = require('../../../base.js').Evernote;

var Client = function(options) {
  this.consumerKey = options.consumerKey;
  this.consumerSecret = options.consumerSecret;
  this.sandbox = typeof(options.sandbox) !== 'undefined' ? options.sandbox : true;
  if (this.sandbox) {
    var defaultServiceHost = 'sandbox.evernote.com';
  } else {
    var defaultServiceHost = 'www.evernote.com';
  }
  this.serviceHost = options.serviceHost || defaultServiceHost;
  this.additionalHeaders = options.additionalHeaders || {};
  this.token = options.token;
  this.secret = options.secret;
};

Client.prototype.getRequestToken = function(callbackUrl, callback) {
  var self = this;
  var oauth = self.getOAuthClient(callbackUrl);
  oauth.getOAuthRequestToken(function(err, oauthToken, oauthTokenSecret, results) {
    callback(err, oauthToken, oauthTokenSecret, results)
  });
};

Client.prototype.getAuthorizeUrl = function(oauthToken) {
  var self = this;
  return self.getEndpoint('OAuth.action') + '?oauth_token=' + oauthToken;
};

Client.prototype.getAccessToken = function(oauthToken, oauthTokenSecret, oauthVerifier, callback) {
  var self = this;
  var oauth = self.getOAuthClient('');
  oauth.getOAuthAccessToken(oauthToken, oauthTokenSecret, oauthVerifier,
    function(err, oauthAccessToken, oauthAccessTokenSecret, results) {
      callback(err, oauthAccessToken, oauthAccessTokenSecret, results);
      self.token = oauthAccessToken;
    });
};

Client.prototype.getUserStore = function() {
  var self = this;
  return new Store(Evernote.UserStoreClient, function(callback) {
    callback(null, self.token, self.getEndpoint("/edam/user"));
  });
};

Client.prototype.getNoteStore = function(noteStoreUrl) {
  var self = this;
  if (typeof noteStoreUrl !== 'undefined') {
    self.noteStoreUrl = noteStoreUrl;
  }
  return new Store(Evernote.NoteStoreClient, function(callback) {
    if (self.noteStoreUrl) {
      callback(null, self.token, self.noteStoreUrl);
    } else {
      self.getUserStore().getNoteStoreUrl(function(err, noteStoreUrl) {
        self.noteStoreUrl = noteStoreUrl;
        callback(err, self.token, self.noteStoreUrl);
      });
    }
  });
};

Client.prototype.getSharedNoteStore = function(linkedNotebook) {
  var self = this;
  return new Store(Evernote.NoteStoreClient, function(callback) {
    var thisStore = this;
    if (thisStore.sharedToken) {
      callback(null, thisStore.sharedToken, linkedNotebook.noteStoreUrl);
    } else {
      var noteStore = new Store(Evernote.NoteStoreClient, function(cb) {
        cb(null, self.token, linkedNotebook.noteStoreUrl);
      });
      noteStore.authenticateToSharedNotebook(linkedNotebook.shareKey, function(err, sharedAuth) {
        thisStore.sharedToken = sharedAuth.authenticationToken;
        callback(err, thisStore.sharedToken, linkedNotebook.noteStoreUrl);
      });
    }
  });
};

Client.prototype.getBusinessNoteStore = function() {
  var self = this;
  return new Store(Evernote.NoteStoreClient, function(callback) {
    var thisStore = this;
    if (thisStore.bizToken && thisStore.bizNoteStoreUrl) {
      callback(null, thisStore.bizToken, thisStore.bizNoteStoreUrl);
    } else {
      self.getUserStore().authenticateToBusiness(function(err, bizAuth) {
        thisStore.bizToken = bizAuth.authenticationToken;
        thisStore.bizNoteStoreUri = bizAuth.noteStoreUrl;
        thisStore.bizUser = bizAuth.user;
        callback(err, thisStore.bizToken, store.bizNoteStoreUri);
      });
    }
  });
};

Client.prototype.getOAuthClient = function(callbackUrl) {
  var self = this;
  return new OAuth(
    self.getEndpoint('oauth'),
    self.getEndpoint('oauth'),
    self.consumerKey,
    self.consumerSecret,
    "1.0",
    callbackUrl,
    "HMAC-SHA1");
};

Client.prototype.getEndpoint = function(path) {
  var self = this;
  var url = 'https://' + self.serviceHost;
  if (path) {
    url += '/' + path;
  }
  return url
};

var Store = function(clientClass, enInfoFunc) {
  var self = this;
  self.clientClass = clientClass;
  self.enInfoFunc = enInfoFunc;

  for (var key in self.clientClass.prototype) {
    if (key.indexOf('_') != -1 || typeof(self.clientClass.prototype[key]) != 'function') continue;
    self[key] = self.createWrapperFunction(key);
  }
};

Store.prototype.createWrapperFunction = function(name) {
  var self = this;
  return function() {
    var orgArgs = arguments;
    self.getThriftClient(function(err, client, token) {
      if (err) {
        callback = orgArgs[orgArgs.length - 1];
        if (callback && typeof(callback) === "function") {
          callback(err);
        } else {
          throw "Evernote SDK for Node.js doesn't support synchronous calls";
        }
        return;
      }
      var orgFunc = client[name];
      var orgArgNames = self.getParamNames(orgFunc);
      if (orgArgNames != null && orgArgs.length + 1 == orgArgNames.length) {
        try {
          var newArgs = [];
          for (var i in orgArgNames) {
            if (orgArgNames[i] == 'authenticationToken') newArgs.push(token);
            if (i < orgArgs.length) newArgs.push(orgArgs[i]);
          }
          orgFunc.apply(client, newArgs);
        } catch (e) {
          orgFunc.apply(client, orgArgs);
        }
      } else {
        orgFunc.apply(client, orgArgs);
      }
    });
  };
};

Store.prototype.getThriftClient = function(callback) {
  var self = this;
  self.enInfoFunc(function(err, token, url) {
    var m = token.match(/:A=([^:]+):/);
    if (m) {
      self.userAgentId = m[1];
    } else {
      self.userAgentId = '';
    }
    var transport = new Evernote.Thrift.NodeBinaryHttpTransport(url);
    transport.addHeaders(
      {'User-Agent':
        self.userAgentId + ' / ' + pjson.version + '; Node.js / ' + process.version});
    var protocol = new Evernote.Thrift.BinaryProtocol(transport);
    callback(err, new self.clientClass(protocol), token);
  });
};

Store.prototype.getParamNames = function(func) {
  var funStr = func.toString();
  return funStr.slice(funStr.indexOf('(')+1, funStr.indexOf(')')).match(/([^\s,]+)/g);
};

exports.Client = Client;
<commit_msg>Fixed bug which caused an exception when calling getBusinessNoteStore.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

var OAuth = require('oauth').OAuth,
    pjson = require('../../../package.json'),
    Evernote = require('../../../base.js').Evernote;

var Client = function(options) {
  this.consumerKey = options.consumerKey;
  this.consumerSecret = options.consumerSecret;
  this.sandbox = typeof(options.sandbox) !== 'undefined' ? options.sandbox : true;
  if (this.sandbox) {
    var defaultServiceHost = 'sandbox.evernote.com';
  } else {
    var defaultServiceHost = 'www.evernote.com';
  }
  this.serviceHost = options.serviceHost || defaultServiceHost;
  this.additionalHeaders = options.additionalHeaders || {};
  this.token = options.token;
  this.secret = options.secret;
};

Client.prototype.getRequestToken = function(callbackUrl, callback) {
  var self = this;
  var oauth = self.getOAuthClient(callbackUrl);
  oauth.getOAuthRequestToken(function(err, oauthToken, oauthTokenSecret, results) {
    callback(err, oauthToken, oauthTokenSecret, results)
  });
};

Client.prototype.getAuthorizeUrl = function(oauthToken) {
  var self = this;
  return self.getEndpoint('OAuth.action') + '?oauth_token=' + oauthToken;
};

Client.prototype.getAccessToken = function(oauthToken, oauthTokenSecret, oauthVerifier, callback) {
  var self = this;
  var oauth = self.getOAuthClient('');
  oauth.getOAuthAccessToken(oauthToken, oauthTokenSecret, oauthVerifier,
    function(err, oauthAccessToken, oauthAccessTokenSecret, results) {
      callback(err, oauthAccessToken, oauthAccessTokenSecret, results);
      self.token = oauthAccessToken;
    });
};

Client.prototype.getUserStore = function() {
  var self = this;
  return new Store(Evernote.UserStoreClient, function(callback) {
    callback(null, self.token, self.getEndpoint("/edam/user"));
  });
};

Client.prototype.getNoteStore = function(noteStoreUrl) {
  var self = this;
  if (typeof noteStoreUrl !== 'undefined') {
    self.noteStoreUrl = noteStoreUrl;
  }
  return new Store(Evernote.NoteStoreClient, function(callback) {
    if (self.noteStoreUrl) {
      callback(null, self.token, self.noteStoreUrl);
    } else {
      self.getUserStore().getNoteStoreUrl(function(err, noteStoreUrl) {
        self.noteStoreUrl = noteStoreUrl;
        callback(err, self.token, self.noteStoreUrl);
      });
    }
  });
};

Client.prototype.getSharedNoteStore = function(linkedNotebook) {
  var self = this;
  return new Store(Evernote.NoteStoreClient, function(callback) {
    var thisStore = this;
    if (thisStore.sharedToken) {
      callback(null, thisStore.sharedToken, linkedNotebook.noteStoreUrl);
    } else {
      var noteStore = new Store(Evernote.NoteStoreClient, function(cb) {
        cb(null, self.token, linkedNotebook.noteStoreUrl);
      });
      noteStore.authenticateToSharedNotebook(linkedNotebook.shareKey, function(err, sharedAuth) {
        thisStore.sharedToken = sharedAuth.authenticationToken;
        callback(err, thisStore.sharedToken, linkedNotebook.noteStoreUrl);
      });
    }
  });
};

Client.prototype.getBusinessNoteStore = function() {
  var self = this;
  return new Store(Evernote.NoteStoreClient, function(callback) {
    var thisStore = this;
    if (thisStore.bizToken && thisStore.bizNoteStoreUrl) {
      callback(null, thisStore.bizToken, thisStore.bizNoteStoreUrl);
    } else {
      self.getUserStore().authenticateToBusiness(function(err, bizAuth) {
        thisStore.bizToken = bizAuth.authenticationToken;
        thisStore.bizNoteStoreUri = bizAuth.noteStoreUrl;
        thisStore.bizUser = bizAuth.user;
        callback(err, thisStore.bizToken, thisStore.bizNoteStoreUri);
      });
    }
  });
};

Client.prototype.getOAuthClient = function(callbackUrl) {
  var self = this;
  return new OAuth(
    self.getEndpoint('oauth'),
    self.getEndpoint('oauth'),
    self.consumerKey,
    self.consumerSecret,
    "1.0",
    callbackUrl,
    "HMAC-SHA1");
};

Client.prototype.getEndpoint = function(path) {
  var self = this;
  var url = 'https://' + self.serviceHost;
  if (path) {
    url += '/' + path;
  }
  return url
};

var Store = function(clientClass, enInfoFunc) {
  var self = this;
  self.clientClass = clientClass;
  self.enInfoFunc = enInfoFunc;

  for (var key in self.clientClass.prototype) {
    if (key.indexOf('_') != -1 || typeof(self.clientClass.prototype[key]) != 'function') continue;
    self[key] = self.createWrapperFunction(key);
  }
};

Store.prototype.createWrapperFunction = function(name) {
  var self = this;
  return function() {
    var orgArgs = arguments;
    self.getThriftClient(function(err, client, token) {
      if (err) {
        callback = orgArgs[orgArgs.length - 1];
        if (callback && typeof(callback) === "function") {
          callback(err);
        } else {
          throw "Evernote SDK for Node.js doesn't support synchronous calls";
        }
        return;
      }
      var orgFunc = client[name];
      var orgArgNames = self.getParamNames(orgFunc);
      if (orgArgNames != null && orgArgs.length + 1 == orgArgNames.length) {
        try {
          var newArgs = [];
          for (var i in orgArgNames) {
            if (orgArgNames[i] == 'authenticationToken') newArgs.push(token);
            if (i < orgArgs.length) newArgs.push(orgArgs[i]);
          }
          orgFunc.apply(client, newArgs);
        } catch (e) {
          orgFunc.apply(client, orgArgs);
        }
      } else {
        orgFunc.apply(client, orgArgs);
      }
    });
  };
};

Store.prototype.getThriftClient = function(callback) {
  var self = this;
  self.enInfoFunc(function(err, token, url) {
    var m = token.match(/:A=([^:]+):/);
    if (m) {
      self.userAgentId = m[1];
    } else {
      self.userAgentId = '';
    }
    var transport = new Evernote.Thrift.NodeBinaryHttpTransport(url);
    transport.addHeaders(
      {'User-Agent':
        self.userAgentId + ' / ' + pjson.version + '; Node.js / ' + process.version});
    var protocol = new Evernote.Thrift.BinaryProtocol(transport);
    callback(err, new self.clientClass(protocol), token);
  });
};

Store.prototype.getParamNames = function(func) {
  var funStr = func.toString();
  return funStr.slice(funStr.indexOf('(')+1, funStr.indexOf(')')).match(/([^\s,]+)/g);
};

exports.Client = Client;
<|endoftext|>


<commit_before>// Program to remove duplicates of array and return array of only unique elements

function uniqueElements(arr) {
	var hashmap = {};
	var unique = [];

}<commit_msg>Add return unique elements array function<commit_after>// Program to remove duplicates of array and return array of only unique elements

function uniqueElements(arr) {
	var hashmap = {}; // to be used as reference table
	var unique = []; // array to be returned

	for (var i = 0; i < arr.length; i++) {
		// if key returns undefined (unique), it is evaluated as false
		if (!hashmap.hasOwnProperty(arr[i])) { // if element does not exist in hash
			hashmap[arr[i]] = 1; // add element to hash table as key (and assign random number 1 as value)
			unique.push(arr[i]); // push element into unique array
		}
	}
	return unique;
}
<|endoftext|>


<commit_before>'use strict';

/* jshint node:true, undef:true, unused:true */
const Rollup   = require('broccoli-rollup');
const Babel    = require('broccoli-babel-transpiler');
const merge    = require('broccoli-merge-trees');
const uglify   = require('broccoli-uglify-js');
const version  = require('git-repo-version');
const watchify = require('broccoli-watchify');
const concat   = require('broccoli-concat');
const fs       = require('fs');

const stew   = require('broccoli-stew');

const find   = stew.find;
const mv     = stew.mv;
const rename = stew.rename;
const env    = stew.env;
const map    = stew.map;

const lib       = find('lib');

// test stuff
const testDir   = find('test');
const testFiles = find('test/{index.html,worker.js}');

const json3     = mv(find('node_modules/json3/lib/{json3.js}'), 'node_modules/json3/lib/', 'test/');
// mocha doesn't browserify correctly
const mocha     = mv(find('node_modules/mocha/mocha.{js,css}'), 'node_modules/mocha/',    'test/');

const testVendor = merge([ json3, mocha ]);

const es5 = new Babel(lib, {
  plugins: [
    'transform-es2015-arrow-functions',
    'transform-es2015-computed-properties',
    'transform-es2015-shorthand-properties',
    'transform-es2015-template-literals',
    'transform-es2015-parameters',
    'transform-es2015-destructuring',
    'transform-es2015-spread',
    'transform-es2015-block-scoping',
    'transform-es2015-constants',
    ['transform-es2015-classes', { loose: true }],
    'babel6-plugin-strip-class-callcheck'
  ]
});

// build RSVP itself
const rsvp = new Rollup(es5, {
  rollup: {
    entry: 'lib/rsvp.js',
    targets: [
      {
        format: 'umd',
        moduleName: 'RSVP',
        dest: 'rsvp.js',
        sourceMap: 'inline'
      },
      {
        format: 'es',
        dest: 'rsvp.es.js',
        sourceMap: 'inline'
      }
    ]
  }
});

const rsvpES6 = new Rollup(lib, {
  rollup: {
    entry: 'lib/rsvp.js',
    targets: [
      {
        format: 'es',
        dest: 'es6/rsvp.es.js',
        sourceMap: 'inline'
      }
    ]
  }
});

const testBundle = watchify(merge([
  mv(rsvp, 'test'),
  testDir
]), {
  browserify: { debug: true, entries: ['./test/index.js'] }
});

const header = stew.map(find('config/versionTemplate.txt'), content => content.replace(/VERSION_PLACEHOLDER_STRING/, version()));

function concatAs(tree, outputFile) {
  return concat(merge([tree, header]), {
    headerFiles: ['config/versionTemplate.txt'],
    inputFiles:  ['rsvp.js'],
    outputFile: outputFile
  });
}

function production(dist, header) {
  let result;
  env('production', () => {
    result = uglify(concatAs(dist, 'rsvp.min.js'), {
      compress: {
        negate_iife: false,
        sequences: false
      },
      mangle: true
    });
  })
  return result;
}

function development(dist, header) {
  return concatAs(dist, 'rsvp.js');
}

module.exports = merge([
  merge([
    production(rsvp, header),
    development(rsvp, header),
    concat(merge([rsvp, header]), {
      headerFiles: ['config/versionTemplate.txt'],
      inputFiles:  ['rsvp.es.js'],
      outputFile: 'rsvp.es.js'
    }),
    concat(merge([rsvpES6, header]), {
      headerFiles: ['config/versionTemplate.txt'],
      inputFiles:  ['es6/rsvp.es.js'],
      outputFile: 'es6/rsvp.es.js'
    })
  ].filter(Boolean)),
  // test stuff
  testFiles,
  testVendor,
  mv(testBundle, 'test')
]);
<commit_msg>Delete Brocfile.js<commit_after><|endoftext|>