<commit_before>
const RG = require('../src/rg');
RG.Component = require('../src/component');
const Ability = require('../src/abilities');

const DebugGame = function(fact, parser) {
    const _fact = fact;
    const _parser = parser;

    this.create = function(obj, game, player) {
        const sqrPerItem = obj.sqrPerItem;
        obj.cols = 100;
        obj.rows = 100;
        const level = _fact.createLastBattle(game, obj);

        const spirit = new RG.Actor.Spirit('Wolf spirit');
        spirit.get('Stats').setStrength(500);
        level.addActor(spirit, 2, 1);

        const gem = new RG.Item.SpiritGem('Lesser gem');
        level.addItem(gem);

        const pickaxe = _parser.createActualObj('items', 'Pick-axe');
        level.addItem(pickaxe, 2, 2);

        const poison = _parser.createActualObj('items',
            'Potion of frost poison');
        poison.count = 5;
        level.addItem(poison, 2, 2);
        const curePoison = _parser.createActualObj('items',
            'Potion of cure poison');
        level.addItem(curePoison, 3, 2);

        const rifle = _parser.createActualObj('items', 'Rifle');
        const ammo = _parser.createActualObj('items', 'Steel bullet');
        ammo.setCount(100);
        level.addItem(rifle, 1, 1);
        level.addItem(ammo, 1, 1);

        // Test for shops
        const keeper = _parser.createActualObj('actors', 'shopkeeper');
        const gold = new RG.Item.GoldCoin();
        gold.count = 50;
        keeper.getInvEq().addItem(gold);
        level.addActor(keeper, 2, 2);
        const shopElem = new RG.Element.Shop();
        const shopCell = level.getMap().getCell(3, 3);
        shopCell.setProp('elements', shopElem);
        const soldItem = _parser.createActualObj('items', 'Ruby glass sword');
        soldItem.add('Unpaid', new RG.Component.Unpaid());
        shopCell.setProp('items', soldItem);
        shopElem.setShopkeeper(keeper);

        const numFree = level.getMap().getFree().length;
        const itemsPerLevel = Math.round(numFree / sqrPerItem);

        const itemConf = {
            itemsPerLevel,
            func: (item) => (item.value <= 2500),
            maxValue: 2500,
            food: () => true,
            gold: () => false
        };
        _fact.addNRandItems(level, _parser, itemConf);

        const cols = level.getMap().cols;
        const rows = level.getMap().rows;

        const boss = _parser.createActor('Thabba, Son of Ice');
        level.addActor(boss, cols - 2, rows - 2);

        const cryomancer = _parser.createActor('Cryomancer');
        level.addActor(cryomancer, 1, rows - 2);

        const spiritPot = _parser.createActualObj(
            'items', 'Potion of spirit form');
        player.getInvEq().addItem(spiritPot);

        const potStr = _parser.createItem('Potion of strength');
        player.getInvEq().addItem(potStr);

        // BladeMaster components
        player.add('Attacker', new RG.Component.Attacker());
        player.add('Defender', new RG.Component.Defender());
        player.add('MasterEquipper', new RG.Component.MasterEquipper());
        player.add('BiDirStrike', new RG.Component.BiDirStrike());
        player.add('CounterAttack', new RG.Component.BiDirStrike());

        // Marksman components
        player.add(new RG.Component.ThroughShot());

        const winCond = new RG.Game.WinCondition('Kill a keeper');
        winCond.addActorKilled(keeper);

        game.addPlayer(player);

        const eq = player.getInvEq().getEquipment();
        eq.addSlot('spiritgem', new RG.Inv.EquipSlot(eq, 'spiritgem'));
        const gem1 = _parser.createItem('Lesser spirit gem');
        const gem2 = _parser.createItem('Greater spirit gem');
        player.getInvEq().addItem(gem1);
        player.getInvEq().addItem(gem2);
        player.add(new RG.Component.SpiritItemCrafter());

        const exploreElem = new RG.Element.Exploration();
        exploreElem.setExp(100);
        level.addElement(exploreElem, 1, 20);

        const trainer = this.createTrainer();
        level.addActor(trainer, 1, 2);

        const coins = new RG.Item.GoldCoin();
        coins.count = 600;
        player.getInvEq().addItem(coins);

        // if (!player.getBook()) {
            const spellbook = new RG.Spell.SpellBook(player);
            player.setBook(spellbook);
            RG.Spell.addAllSpells(spellbook);
            player.add(new RG.Component.SpellPower());
            player.get('SpellPower').setPP(100);
        // }

        const vActor = new RG.Actor.Virtual('spawner');
        const spawnBrain = new RG.Brain.Spawner(vActor);
        spawnBrain.setConstraint({op: 'lt', prop: 'danger', value: 10});
        vActor.setBrain(spawnBrain);
        level.addVirtualProp(RG.TYPE_ACTOR, vActor);

        const fire = _parser.createActor('Fire');
        const fadingComp = new RG.Component.Fading();
        fadingComp.setDuration(20);
        fire.add(fadingComp);
        level.addActor(fire, 7, 1);

        const thunderbird = _parser.createActor('thunderbird');
        level.addActor(thunderbird, 20, 1);

        const firekit = parser.createEntity('firemaking kit');
        player.getInvEq().addItem(firekit);

        player.add(new RG.Component.Coldness());
        player.get('SpellPower').setPP(100);
        player.get('SpellPower').setMaxPP(100);

        const itemRand = new RG.Factory.ItemRandomizer();
        const runeProt = parser.createItem('rune of protection');
        itemRand.adjustItem(runeProt, 100);
        player.getInvEq().addItem(runeProt);

        const runeDig = parser.createItem('rune of tunneling');
        itemRand.adjustItem(runeDig, 100);
        player.getInvEq().addItem(runeDig);

        const runeForce = parser.createItem('rune of force');
        itemRand.adjustItem(runeForce, 100);
        player.getInvEq().addItem(runeForce);

        const lever = new RG.Element.Lever();
        level.addElement(lever, 2, 1);
        for (let i = 0; i < 3; i++) {
            const leverDoor = new RG.Element.LeverDoor();
            lever.addTarget(leverDoor);
            level.addElement(leverDoor, 3 + i, 1);
        }

        // For testing actor abilities and camouflage
        const abilities = player.get('Abilities');
        const camouflage = new Ability.Camouflage();
        abilities.addAbility(camouflage);
        const sharpener = new Ability.Sharpener();
        abilities.addAbility(sharpener);

        return game;
    };

    this.createTrainer = function() {
        const human = _parser.createActor('fighter');
        human.setName('Old trainer');
        const trainComp = new RG.Component.Trainer();
        trainComp.getChatObj().setTrainer(human);
        human.add(trainComp);
        return human;
    };
};

module.exports = DebugGame;
<commit_msg>Moved functions into prototype.<commit_after>/* For debugging various features of the game. Basically everything
 * requiring
 * manual testing is added to the end of create() function.
 */

const RG = require('../src/rg');
RG.Component = require('../src/component');
const Ability = require('../src/abilities');

const DebugGame = function(fact, parser) {
    this._fact = fact;
    this._parser = parser;
};

DebugGame.prototype.create = function(obj, game, player) {
    const parser = this._parser;
    const sqrPerItem = obj.sqrPerItem;
    obj.cols = 100;
    obj.rows = 100;
    const level = this._fact.createLastBattle(game, obj);

    const spirit = this._parser.createActor('Wolf spirit');
    spirit.get('Stats').setStrength(500);
    level.addActor(spirit, 2, 1);

    const gem = new RG.Item.SpiritGem('Lesser gem');
    level.addItem(gem);

    const pickaxe = this._parser.createActualObj('items', 'Pick-axe');
    level.addItem(pickaxe, 2, 2);

    const poison = this._parser.createActualObj('items',
        'Potion of frost poison');
    poison.count = 5;
    level.addItem(poison, 2, 2);
    const curePoison = this._parser.createActualObj('items',
        'Potion of cure poison');
    level.addItem(curePoison, 3, 2);

    const rifle = this._parser.createActualObj('items', 'Rifle');
    const ammo = this._parser.createActualObj('items', 'Steel bullet');
    ammo.setCount(100);
    level.addItem(rifle, 1, 1);
    level.addItem(ammo, 1, 1);

    // Test for shops
    const keeper = this._parser.createActualObj('actors', 'shopkeeper');
    const gold = new RG.Item.GoldCoin();
    gold.count = 50;
    keeper.getInvEq().addItem(gold);
    level.addActor(keeper, 2, 2);
    const shopElem = new RG.Element.Shop();
    const shopCell = level.getMap().getCell(3, 3);
    shopCell.setProp('elements', shopElem);
    const soldItem = this._parser.createActualObj('items',
        'Ruby glass sword');
    soldItem.add('Unpaid', new RG.Component.Unpaid());
    shopCell.setProp('items', soldItem);
    shopElem.setShopkeeper(keeper);

    const numFree = level.getMap().getFree().length;
    const itemsPerLevel = Math.round(numFree / sqrPerItem);

    const itemConf = {
        itemsPerLevel,
        func: (item) => (item.value <= 2500),
        maxValue: 2500,
        food: () => true,
        gold: () => false
    };
    this._fact.addNRandItems(level, this._parser, itemConf);

    const cols = level.getMap().cols;
    const rows = level.getMap().rows;

    const boss = this._parser.createActor('Thabba, Son of Ice');
    level.addActor(boss, cols - 2, rows - 2);

    const cryomancer = this._parser.createActor('Cryomancer');
    level.addActor(cryomancer, 1, rows - 2);

    const spiritPot = this._parser.createActualObj(
        'items', 'Potion of spirit form');
    player.getInvEq().addItem(spiritPot);

    const potStr = this._parser.createItem('Potion of strength');
    player.getInvEq().addItem(potStr);

    // BladeMaster components
    player.add('Attacker', new RG.Component.Attacker());
    player.add('Defender', new RG.Component.Defender());
    player.add('MasterEquipper', new RG.Component.MasterEquipper());
    player.add('BiDirStrike', new RG.Component.BiDirStrike());
    player.add('CounterAttack', new RG.Component.BiDirStrike());

    // Marksman components
    player.add(new RG.Component.ThroughShot());

    const winCond = new RG.Game.WinCondition('Kill a keeper');
    winCond.addActorKilled(keeper);

    game.addPlayer(player);

    const eq = player.getInvEq().getEquipment();
    eq.addSlot('spiritgem', new RG.Inv.EquipSlot(eq, 'spiritgem'));
    const gem1 = this._parser.createItem('Lesser spirit gem');
    const gem2 = this._parser.createItem('Greater spirit gem');
    player.getInvEq().addItem(gem1);
    player.getInvEq().addItem(gem2);
    player.add(new RG.Component.SpiritItemCrafter());

    const exploreElem = new RG.Element.Exploration();
    exploreElem.setExp(100);
    level.addElement(exploreElem, 1, 20);

    const trainer = this.createTrainer();
    level.addActor(trainer, 1, 2);

    const coins = new RG.Item.GoldCoin();
    coins.count = 600;
    player.getInvEq().addItem(coins);

    // if (!player.getBook()) {
        const spellbook = new RG.Spell.SpellBook(player);
        player.setBook(spellbook);
        RG.Spell.addAllSpells(spellbook);
        player.add(new RG.Component.SpellPower());
        player.get('SpellPower').setPP(100);
    // }

    const vActor = new RG.Actor.Virtual('spawner');
    const spawnBrain = new RG.Brain.Spawner(vActor);
    spawnBrain.setConstraint({op: 'lt', prop: 'danger', value: 10});
    vActor.setBrain(spawnBrain);
    level.addVirtualProp(RG.TYPE_ACTOR, vActor);

    const fire = this._parser.createActor('Fire');
    const fadingComp = new RG.Component.Fading();
    fadingComp.setDuration(20);
    fire.add(fadingComp);
    level.addActor(fire, 7, 1);

    const thunderbird = this._parser.createActor('thunderbird');
    level.addActor(thunderbird, 20, 1);

    const firekit = parser.createEntity('firemaking kit');
    player.getInvEq().addItem(firekit);

    player.add(new RG.Component.Coldness());
    player.get('SpellPower').setPP(100);
    player.get('SpellPower').setMaxPP(100);

    const itemRand = new RG.Factory.ItemRandomizer();
    const runeProt = parser.createItem('rune of protection');
    itemRand.adjustItem(runeProt, 100);
    player.getInvEq().addItem(runeProt);

    const runeDig = parser.createItem('rune of tunneling');
    itemRand.adjustItem(runeDig, 100);
    player.getInvEq().addItem(runeDig);

    const runeForce = parser.createItem('rune of force');
    itemRand.adjustItem(runeForce, 100);
    player.getInvEq().addItem(runeForce);

    const lever = new RG.Element.Lever();
    level.addElement(lever, 2, 1);
    for (let i = 0; i < 3; i++) {
        const leverDoor = new RG.Element.LeverDoor();
        lever.addTarget(leverDoor);
        level.addElement(leverDoor, 3 + i, 1);
    }

    // For testing actor abilities and camouflage
    const abilities = player.get('Abilities');
    const camouflage = new Ability.Camouflage();
    abilities.addAbility(camouflage);
    const sharpener = new Ability.Sharpener();
    abilities.addAbility(sharpener);

    return game;
};

DebugGame.prototype.createTrainer = function() {
    const human = this._parser.createActor('fighter');
    human.setName('Old trainer');
    const trainComp = new RG.Component.Trainer();
    trainComp.getChatObj().setTrainer(human);
    human.add(trainComp);
    return human;
};

module.exports = DebugGame;
<|endoftext|>


<commit_before>var fs = require('fs');
var environment = process.env.NODE_ENV || 'development';

fs.stat('../tmp', function(err, stats) {
  if (stats && !stats.isDirectory()) {
    console.log("Creating tmp directory...");
    fs.mkdirSync("../tmp");
  }
});
<commit_msg>Removed unused code<commit_after>var fs = require('fs');

fs.stat('../tmp', function(err, stats) {
  if (stats && !stats.isDirectory()) {
    console.log("Creating tmp directory...");
    fs.mkdirSync("../tmp");
  }
});
<|endoftext|>


<commit_before>// component-factory.js

var _ = require('underscore');
var noflo = require('noflo');

/**
 * Creates a noflo Component factory function from a component definitation,
 * registers any event handlers on definitation. Triggers ondata events for outPorts.
 *
 * Usage:
 *  componentFactory({
 *      description: "text",
 *      icon: 'fontawesome-icon-name',
 *      inPorts: {
 *          portName: {
 *              ondata: function(payload)
 *          }
 *      },
 *      outPorts: {
 *          portName: {
 *              ondata: function(payload)
 *          }
 *      },
 *      onicon: function(icon)
 *   });
 */
module.exports = function(def){
    if (!def) throw Error("No parameter");
    return function() {
        // noflo requires each port and nodeInstance to have its own options object
        var nodeInstance = new noflo.Component({
            outPorts: _.mapObject(def.outPorts, _.clone),
            inPorts: _.mapObject(def.inPorts, _.clone)
        });
        triggerPortDataEvents(nodeInstance.outPorts);
        registerPorts(nodeInstance.outPorts, def.outPorts);
        registerPorts(nodeInstance.inPorts, def.inPorts);
        registerListeners(nodeInstance, def);
        nodeInstance.description = def.description;
        nodeInstance.setIcon(def.icon);
        return nodeInstance;
    };
};

/**
 * Fires data event when OutPort send function is called on port.
 */
function triggerPortDataEvents(ports) {
    _.each(ports, function(port) {
        port.send = _.partial(thenTrigger, port.send, 'data');
    });
}

/**
 * Emit event after function is called
 * @this Port object sending the payload
 * @param fn function to call
 * @param event event to emit
 */
function thenTrigger(fn, event /* arguments to fn and event */) {
    var args = _.toArray(arguments).slice(2);
    var ret = fn.apply(this, args);
    this.emit.apply(this, [].concat(event, args));
    return ret;
}

/**
 * Register listeners of the set of port definitions with this actual noflo Ports set.
 *
 * Usage:
 *  registerPorts({portName: new Port()}, {
 *      portName: {
 *          ondata: function(payload)
 *      }
 *  });
 */
function registerPorts(ports, portDefs) {
    _.each(portDefs, function(port, name) {
        _.each(_.pick(port, isListener), register.bind(ports[name]));
    });
}

/**
 * Registers listers by their event type to this EventEmitter.
 * Usage:
 *  registerListeners(new EventEmitter(), {
 *      ondata: function(payload)
 *  });
 */
function registerListeners(eventEmitter, listeners) {
    _.each(_.pick(listeners, isListener), register.bind(eventEmitter));
}

/**
 * Checks the given function and that name starts with 'on'.
 * @param fn is a function if result is true
 * @param name starts with 'on' if result is true
 */
function isListener(fn, name) {
    return _.isFunction(fn) && name.indexOf('on') === 0;
}

/**
 * Registers the function handler with the event type for this EventEmitter.
 * @this EventEmitter (Component or Port) that this fn should be registered with
 * @param fn function handler
 * @param name event type prefixed with 'on'
 */
function register(fn, name) {
    this.on(name.substring(2), fn);
}
<commit_msg>Extra check to ensure send fn exists<commit_after>// component-factory.js

var _ = require('underscore');
var noflo = require('noflo');

/**
 * Creates a noflo Component factory function from a component definitation,
 * registers any event handlers on definitation. Triggers ondata events for outPorts.
 *
 * Usage:
 *  componentFactory({
 *      description: "text",
 *      icon: 'fontawesome-icon-name',
 *      inPorts: {
 *          portName: {
 *              ondata: function(payload)
 *          }
 *      },
 *      outPorts: {
 *          portName: {
 *              ondata: function(payload)
 *          }
 *      },
 *      onicon: function(icon)
 *   });
 */
module.exports = function(def){
    if (!def) throw Error("No parameter");
    return function() {
        // noflo requires each port and nodeInstance to have its own options object
        var nodeInstance = new noflo.Component({
            outPorts: _.mapObject(def.outPorts, _.clone),
            inPorts: _.mapObject(def.inPorts, _.clone)
        });
        triggerPortDataEvents(nodeInstance.outPorts);
        registerPorts(nodeInstance.outPorts, def.outPorts);
        registerPorts(nodeInstance.inPorts, def.inPorts);
        registerListeners(nodeInstance, def);
        nodeInstance.description = def.description;
        nodeInstance.setIcon(def.icon);
        return nodeInstance;
    };
};

/**
 * Fires data event when OutPort send function is called on port.
 */
function triggerPortDataEvents(ports) {
    _.each(ports, function(port) {
        if (_.isFunction(port.send)) {
            port.send = _.partial(thenTrigger, port.send, 'data');
        }
    });
}

/**
 * Emit event after function is called
 * @this Port object sending the payload
 * @param fn function to call
 * @param event event to emit
 */
function thenTrigger(fn, event /* arguments to fn and event */) {
    var args = _.toArray(arguments).slice(2);
    var ret = fn.apply(this, args);
    this.emit.apply(this, [].concat(event, args));
    return ret;
}

/**
 * Register listeners of the set of port definitions with this actual noflo Ports set.
 *
 * Usage:
 *  registerPorts({portName: new Port()}, {
 *      portName: {
 *          ondata: function(payload)
 *      }
 *  });
 */
function registerPorts(ports, portDefs) {
    _.each(portDefs, function(port, name) {
        _.each(_.pick(port, isListener), register.bind(ports[name]));
    });
}

/**
 * Registers listers by their event type to this EventEmitter.
 * Usage:
 *  registerListeners(new EventEmitter(), {
 *      ondata: function(payload)
 *  });
 */
function registerListeners(eventEmitter, listeners) {
    _.each(_.pick(listeners, isListener), register.bind(eventEmitter));
}

/**
 * Checks the given function and that name starts with 'on'.
 * @param fn is a function if result is true
 * @param name starts with 'on' if result is true
 */
function isListener(fn, name) {
    return _.isFunction(fn) && name.indexOf('on') === 0;
}

/**
 * Registers the function handler with the event type for this EventEmitter.
 * @this EventEmitter (Component or Port) that this fn should be registered with
 * @param fn function handler
 * @param name event type prefixed with 'on'
 */
function register(fn, name) {
    this.on(name.substring(2), fn);
}
<|endoftext|>


<commit_before>/**
* 背景页面入口引导,用来管理WebView,为应用页面间通讯做代理
*/
(function(window){
    //辅助类和方法
    Array.prototype.indexOf = function(val) {
        for (var i = 0; i < this.length; i++) {
            if (this[i] == val) return i;
        }
        return -1;
    };

    Array.prototype.remove = function(val) {
        var index = this.indexOf(val);
        if (index > -1) {
            this.splice(index, 1);
        }
    };

    //系统管理器
    var Boost = function(config){
        return new Boost.prototype.init(config);
    };

    var webviews = {
        ids: [],
        table: {},
        register: function(id,state){
            state = state || {};
            if (!this.table[id]){
                this.ids.push(id);
            }
            this.table[id] = state;
        },
        unregister: function(id){
            delete this.table[id];
            this.ids.remove(id);
        }
    };

    var __event = {};

    Boost.prototype = {
        init: function(config){
            this.config = config;
            return this;
        }
    }

    Boost.prototype.init.prototype = Boost.prototype;

    Boost.prototype.load = function(id, data){
        lc_bridge.exeJsOnContentWebview(id, data);
    }

    //WebView管理机制
    Boost.prototype.register = function(){

        document.addEventListener("webview_register", function(event){
            var webview = JSON.parse(event.data);
            webviews.register(webview.id);
        });

        document.addEventListener("webview_unregister", function(event){
            var webview = JSON.parse(event.data);
            webviews.unregister(webview.id);
        });
    }

    //消息机制
    Boost.prototype.delegate = function(){
        var that = this;
        document.addEventListener("delegate", function(event){
            //业务页面通过背景页自我调用

            lc_bridge.postMessage(event.origin, "message", event.origin);

            var data = JSON.parse(event.data);

            if (__event[data.type]){
                for (var i in __event[data.type]){
                    console.log("页面消息转发:"+data.type);
                    lc_bridge.postMessage(__event[data.type][i], data.type, "页面ID"+event.origin+"|"+"内容:"+event.data);
                }
            }

        }, false);

        document.addEventListener("event_register", function(event){
            if (__event[event.data]){
                __event[event.data].push(event.origin);
            } else {
                __event[event.data] = [event.origin];
            }
            console.log(event.origin);
        }, false);

        document.addEventListener("event_unregister", function(event){
            __event[event.data] && __event[event.data].remove(event.origin);
        }, false);
    }

    //用来测试的用例
    Boost.prototype.test = function(){
        document.addEventListener("test", function(event){
            var data = JSON.parse(event.data);
            console.log("test----");
            if (data.type == "not-exist&post"){
                console.log("not-exist&post");
                lc_bridge.postMessage(event.data, "testCallback", data.data);
                lc_bridge.postMessage(event.origin, "testCallback", data.data);
            }

            if (data.type == "not-exist&exec"){
                console.log("not-exist&exec");
                lc_bridge.exeJsRemote(event.data, "alert('exec')");
                lc_bridge.exeJsRemote(event.origin, "alert('不存在的页面执行exec')");
            }

        });
    }

    Boost.prototype.run = function(){

        //必需模块
        this.register();

        //消息通讯
        this.delegate();

        //测试用例
        this.test();

    }

    Boost.webviews = webviews;

    window.$app = window.Boost = Boost;

    //模块化支持
    if(typeof define !== undefined && typeof define.amd !== undefined){
        define(function(){
            return Boost;
        });
    }

    //启动服务
    var config = {
        version: 1.0
    };

    $app(config).run();

})(this);<commit_msg>背景管理页新版更新<commit_after>/**
 * @file entrance.js
 * @desc uix native背景页环境,维护应用上下文环境,
 */
(function () {
    // 系统原生事件,新activity创建的时候触发webview_register事件, activity销毁的时候触发webview_unregister事件
    // var _events = ['webview_register', 'webview_unregister'];
    var bridge = lc_bridge;
    var webViews = {};
    var status = {
        ACTIVE: 1,
        PAUSED: 2,
        STOPPED: 3,
        KILLED: 0
    };
    // 监听activity webView创建事件
    document.addEventListener('webview_register', function (event) {
        (event.origin in webViews) || (webViews[event.origin] = {
            origin: event.origin,
            status: status.ACTIVE,
            data: event.data
        });
    });
    // 监听activity webView销毁事件
    document.addEventListener('webview_unregister', function (event) {
        (event.origin in webViews) && (delete webViews[event.origin]);
    });
    // 注册页面要监听的事件
    var events = {};
    document.addEventListener('event_register', function (event) {
        var data = JSON.parse(event.data);
        var type = data.type;
        var origin = event.origin;
        if (events[type]) {
            events[type].indexOf(origin) < 0 || events[type].push(origin);
        }
        else {
            events[type] = [origin];
        }
    }, false);
    // 移除要监听的事件
    document.addEventListener('event_unregister', function (event) {
        var data = JSON.parse(event.data);
        var type = data.type;
        var origin = event.origin;
        if (events[type]) {
            var index = events[type].indexOf(origin);
            events[type].splice(index, 1);
        }
    }, false);
    // 触发事件
    document.addEventListener('delegate', function (event) {
        var data = JSON.parse(event.data);
        var type = data.type;
        var message = JSON.stringify(data.message);
        if (events[type]) {
            for (var i = 0, len = events[type].length; i < len; i++) {
                bridge.postMessage(events[type][i], type, message);
            }
        }
    }, false);
})();
<|endoftext|>


<commit_before>import React from 'react';
import Backbone from 'backbone';
import modals from 'modals';
import MaintenanceMessageBanner from './MaintenanceMessageBanner.react';
import context from 'context';
import globals from 'globals';
import Router from 'react-router';

import NotificationController from 'controllers/NotificationController';
import { trackAction } from 'utilities/userActivity';
import { hasLoggedInUser } from 'utilities/profilePredicate';

let Link = Router.Link;

let links = [
    {
      name: "Dashboard",
      linksTo: "dashboard",
      href: "/application/dashboard",
      icon: "stats",
      requiresLogin: true,
      isEnabled: true
    },
    {
      name: "Projects",
      linksTo: "projects",
      href: "/application/projects",
      icon: "folder-open",
      requiresLogin: true,
      isEnabled: true
    },
    {
      name: "Images",
      linksTo: "images",
      href: "/application/images",
      icon: "floppy-disk",
      requiresLogin: false,
      isEnabled: true
    },
    {
      name: "Providers",
      linksTo: "providers",
      href: "/application/providers",
      icon: "cloud",
      requiresLogin: true,
      isEnabled: true
    },
    {
      name: "Help",
      linksTo: "help",
      href: "/application/help",
      icon: "question-sign",
      requiresLogin: false,
      isEnabled: true
    },
    {
      name: "Admin",
      linksTo: "admin",
      href: "/application/admin",
      icon: "cog",
      requiresLogin: true,
      requiresStaff: true,
      isEnabled: true
    },
    {
      name: "Badges",
      linksTo: "my-badges",
      href: "/application/badges",
      icon: "star",
      requiresLogin: true,
      requiresStaff: false,
      isEnabled: globals.BADGES_ENABLED
    }
];

// Don't show Providers link if globals.USE_ALLOCATION_SOURCES
if ( globals.USE_ALLOCATION_SOURCES ) {
    links = links.filter( link => link.name !== "Providers");
};

let LoginLink = React.createClass({
    render: function () {
      let redirect_path = window.location.pathname+"?beta=true";

      return (

          Login

      );
    }
});

let LogoutLink = React.createClass({

    propTypes: {
      username: React.PropTypes.string.isRequired
    },

    onShowVersion: function (e) {
      e.preventDefault();
      modals.VersionModals.showVersion();
    },

    render: function () {
      var statusPageEl,
        username = this.props.username;

      if (!username && window.show_public_site) {
          username = "AnonymousUser"
      }
      if (globals.STATUS_PAGE_LINK) {
        statusPageEl =(

              Status

        );
      }

      return (

            {username}

              <Link to="settings">Settings</Link>

              <Link to="my-requests-resources">My requests</Link>

              Version

            {statusPageEl}

              Sign out

      );
    }
  });

let Header = React.createClass({
    displayName: "Header",

    propTypes: {
      profile: React.PropTypes.instanceOf(Backbone.Model),
      currentRoute: React.PropTypes.array.isRequired
    },

    // We need the screen size for handling the opening and closing of our menu on small screens
    //See navLinks below for implementation.

    getInitialState: function() {
        return {windowWidth: window.innerWidth};
    },

    handleResize: function(e) {
        this.setState({windowWidth: window.innerWidth});
    },

    handleNotice: function() {
        if (context.hasMaintenanceNotice()) {
            var notice = context.getMaintenanceNotice();
            var options = {
                "toastClass": "toast toast-mod-info-darken",
                "positionClass": "toast-top-full-width",
                "closeButton": true,
                "timeOut": 0,
                "extendedTimeOut": 0,
                "tapToDismiss": false,
                "closeOnHover": false
            };

            NotificationController.warning(
                notice['title'],
                notice['message'],
                options
            );
        }
    },

    componentDidMount: function() {
        window.addEventListener('resize', this.handleResize);
        this.handleNotice();
    },

    componentWillUnmount: function() {
        window.removeEventListener('resize', this.handleResize);
    },

    renderBetaToggle: function () {
      if (!window.show_troposphere_only) {
        let trackEvent = (e) => {
            trackAction('switch-ui', {
                    user_interface: 'troposphere-to-airport'
            });
            trackAction('switch-to-airport');
        };

        var toggleBackground = {
            backgroundColor: "rgba(17, 5, 1, .3)",
            width: 103,
            height: 20,
            borderRadius: 14,
            fontSize: 13
        };

        return (

View Old UI

        )
      }
    },

    render: function () {
      var profile = this.props.profile,
        hasUser = hasLoggedInUser(profile);

      var loginLogoutDropdown = (hasUser ?
            <LogoutLink username={profile.get('username')}/> :
            <LoginLink/>);

      if (!profile.get('selected_identity')) {
        links = links.filter(function (link) {
          return !link.requiresLogin && link.isEnabled;
        })
      } else {
        links = links.filter(function (link) {
          if (link.requiresStaff) return profile.get('is_staff');
          return link.isEnabled;
        })
      }

      var navLinks = links.map(function (link) {
        var isCurrentRoute = (link.name.toLowerCase() === this.props.currentRoute[0]);
        var className = isCurrentRoute ? "active" : null;

        //We need to only trigger the toggle menu on small screen sizes to avoid buggy behavior when selecting menu items on larger screens
        var smScreen = (this.state.windowWidth < 768);
        var toggleMenu = smScreen ? {toggle: 'collapse',target:'.navbar-collapse'} : {toggle: null, target: null};

        return (

            <Link to={link.linksTo}>

              {link.name}
            </Link>

        );
      }.bind(this));

      var brandLink = (hasUser ?
        <Link to="dashboard" className="navbar-brand"/> :
        <Link to="images" className="navbar-brand"/>);

      return (

          <MaintenanceMessageBanner maintenanceMessages={this.props.maintenanceMessages}/>

              <button type="button" className="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                Toggle navigation

              </button>
              {brandLink}

                {navLinks}

                {loginLogoutDropdown}

            {hasUser ? this.renderBetaToggle() : null}

      );

    }
});

export default Header;
<commit_msg>Corrected notice referencing in modal<commit_after>import React from 'react';
import Backbone from 'backbone';
import modals from 'modals';
import MaintenanceMessageBanner from './MaintenanceMessageBanner.react';
import context from 'context';
import globals from 'globals';
import Router from 'react-router';

import NotificationController from 'controllers/NotificationController';
import { trackAction } from 'utilities/userActivity';
import { hasLoggedInUser } from 'utilities/profilePredicate';

let Link = Router.Link;

let links = [
    {
      name: "Dashboard",
      linksTo: "dashboard",
      href: "/application/dashboard",
      icon: "stats",
      requiresLogin: true,
      isEnabled: true
    },
    {
      name: "Projects",
      linksTo: "projects",
      href: "/application/projects",
      icon: "folder-open",
      requiresLogin: true,
      isEnabled: true
    },
    {
      name: "Images",
      linksTo: "images",
      href: "/application/images",
      icon: "floppy-disk",
      requiresLogin: false,
      isEnabled: true
    },
    {
      name: "Providers",
      linksTo: "providers",
      href: "/application/providers",
      icon: "cloud",
      requiresLogin: true,
      isEnabled: true
    },
    {
      name: "Help",
      linksTo: "help",
      href: "/application/help",
      icon: "question-sign",
      requiresLogin: false,
      isEnabled: true
    },
    {
      name: "Admin",
      linksTo: "admin",
      href: "/application/admin",
      icon: "cog",
      requiresLogin: true,
      requiresStaff: true,
      isEnabled: true
    },
    {
      name: "Badges",
      linksTo: "my-badges",
      href: "/application/badges",
      icon: "star",
      requiresLogin: true,
      requiresStaff: false,
      isEnabled: globals.BADGES_ENABLED
    }
];

// Don't show Providers link if globals.USE_ALLOCATION_SOURCES
if ( globals.USE_ALLOCATION_SOURCES ) {
    links = links.filter( link => link.name !== "Providers");
};

let LoginLink = React.createClass({
    render: function () {
      let redirect_path = window.location.pathname+"?beta=true";

      return (

          Login

      );
    }
});

let LogoutLink = React.createClass({

    propTypes: {
      username: React.PropTypes.string.isRequired
    },

    onShowVersion: function (e) {
      e.preventDefault();
      modals.VersionModals.showVersion();
    },

    render: function () {
      var statusPageEl,
        username = this.props.username;

      if (!username && window.show_public_site) {
          username = "AnonymousUser"
      }
      if (globals.STATUS_PAGE_LINK) {
        statusPageEl =(

              Status

        );
      }

      return (

            {username}

              <Link to="settings">Settings</Link>

              <Link to="my-requests-resources">My requests</Link>

              Version

            {statusPageEl}

              Sign out

      );
    }
  });

let Header = React.createClass({
    displayName: "Header",

    propTypes: {
      profile: React.PropTypes.instanceOf(Backbone.Model),
      currentRoute: React.PropTypes.array.isRequired
    },

    // We need the screen size for handling the opening and closing of our menu on small screens
    //See navLinks below for implementation.

    getInitialState: function() {
        return {windowWidth: window.innerWidth};
    },

    handleResize: function(e) {
        this.setState({windowWidth: window.innerWidth});
    },

    handleNotice: function() {
        if (context.hasMaintenanceNotice()) {
            var notice = context.getMaintenanceNotice();
            var options = {
                "toastClass": "toast toast-mod-info-darken",
                "positionClass": "toast-top-full-width",
                "closeButton": true,
                "timeOut": 0,
                "extendedTimeOut": 0,
                "tapToDismiss": false,
                "closeOnHover": false
            };

            NotificationController.warning(
                "Maintenance Information",
                notice,
                options
            );
        }
    },

    componentDidMount: function() {
        window.addEventListener('resize', this.handleResize);
        this.handleNotice();
    },

    componentWillUnmount: function() {
        window.removeEventListener('resize', this.handleResize);
    },

    renderBetaToggle: function () {
      if (!window.show_troposphere_only) {
        let trackEvent = (e) => {
            trackAction('switch-ui', {
                    user_interface: 'troposphere-to-airport'
            });
            trackAction('switch-to-airport');
        };

        var toggleBackground = {
            backgroundColor: "rgba(17, 5, 1, .3)",
            width: 103,
            height: 20,
            borderRadius: 14,
            fontSize: 13
        };

        return (

View Old UI

        )
      }
    },

    render: function () {
      var profile = this.props.profile,
        hasUser = hasLoggedInUser(profile);

      var loginLogoutDropdown = (hasUser ?
            <LogoutLink username={profile.get('username')}/> :
            <LoginLink/>);

      if (!profile.get('selected_identity')) {
        links = links.filter(function (link) {
          return !link.requiresLogin && link.isEnabled;
        })
      } else {
        links = links.filter(function (link) {
          if (link.requiresStaff) return profile.get('is_staff');
          return link.isEnabled;
        })
      }

      var navLinks = links.map(function (link) {
        var isCurrentRoute = (link.name.toLowerCase() === this.props.currentRoute[0]);
        var className = isCurrentRoute ? "active" : null;

        //We need to only trigger the toggle menu on small screen sizes to avoid buggy behavior when selecting menu items on larger screens
        var smScreen = (this.state.windowWidth < 768);
        var toggleMenu = smScreen ? {toggle: 'collapse',target:'.navbar-collapse'} : {toggle: null, target: null};

        return (

            <Link to={link.linksTo}>

              {link.name}
            </Link>

        );
      }.bind(this));

      var brandLink = (hasUser ?
        <Link to="dashboard" className="navbar-brand"/> :
        <Link to="images" className="navbar-brand"/>);

      return (

          <MaintenanceMessageBanner maintenanceMessages={this.props.maintenanceMessages}/>

              <button type="button" className="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                Toggle navigation

              </button>
              {brandLink}

                {navLinks}

                {loginLogoutDropdown}

            {hasUser ? this.renderBetaToggle() : null}

      );

    }
});

export default Header;
<|endoftext|>


<commit_before>/* broadcast.js - all published messages are relayed to all connected clients */

var mqtt = require('../..')
  , util = require('util');

mqtt.createServer(function(client) {
  var self = this;

  if (!self.clients) self.clients = {};

  client.on('connect', function(packet) {
    client.connack({returnCode: 0});
    client.id = packet.clientId;
    console.log("CONNECT: client id: " + client.id);
    self.clients[client.id] = client;
  });

  client.on('publish', function(packet) {
    for (var k in self.clients) {
      self.clients[k].publish({topic: packet.topic, payload: packet.payload});
    }
  });

  client.on('subscribe', function(packet) {
    var granted = [];
    for (var i = 0; i < packet.subscriptions.length; i++) {
      granted.push(packet.subscriptions[i].qos);
    }

    client.suback({granted: granted, messageId: packet.messageId});
  });

  client.on('pingreq', function(packet) {
    client.pingresp();
  });

  client.on('disconnect', function(packet) {
    client.stream.end();
  });

  client.on('close', function(err) {
    delete self.clients[client.id];
  });

  client.on('error', function(err) {
    client.stream.end();
    util.log('error!');
  });
}).listen(process.argv[2] || 1883);
<commit_msg>Expand logging<commit_after>/* broadcast.js - all published messages are relayed to all connected clients */

var mqtt = require('../..')
  , util = require('util');

mqtt.createServer(function(client) {
  var self = this;

  if (!self.clients) self.clients = {};

  client.on('connect', function(packet) {
    client.connack({returnCode: 0});
    client.id = packet.clientId;
    console.log("CONNECT(%s): %j", client.id, packet);
    self.clients[client.id] = client;
  });

  client.on('publish', function(packet) {
    console.log("PUBLISH(%s): %j", client.id, packet);
    for (var k in self.clients) {
      self.clients[k].publish({topic: packet.topic, payload: packet.payload});
    }
  });

  client.on('subscribe', function(packet) {
    console.log("SUBSCRIBE(%s): %j", client.id, packet);
    var granted = [];
    for (var i = 0; i < packet.subscriptions.length; i++) {
      granted.push(packet.subscriptions[i].qos);
    }

    client.suback({granted: granted, messageId: packet.messageId});
  });

  client.on('pingreq', function(packet) {
    console.log('PINGREQ(%s)', client.id);
    client.pingresp();
  });

  client.on('disconnect', function(packet) {
    client.stream.end();
  });

  client.on('close', function(err) {
    delete self.clients[client.id];
  });

  client.on('error', function(err) {
    client.stream.end();
    console.dir(err);
  });
}).listen(process.argv[2] || 1883);
<|endoftext|>


<commit_before>import { Class } from '../core/Core';
import Obj from '../foundation/Object';
import '../foundation/EventTarget';  // For Function#on
import { isApple } from '../ua/UA';
import { lookupKey } from '../dom/DOMEvent';
import RichTextView from '../views/controls/RichTextView';
import ViewEventsController from '../views/ViewEventsController';
import { DEFAULT_IN_INPUT, ACTIVE_IN_INPUT, DISABLE_IN_INPUT } from './keyboardShortcuts.js';
import toPlatformKey from './toPlatformKey';

const allowedInputs = {
    checkbox: 1,
    radio: 1,
    file: 1,
    submit: 1,
};

const handleOnDown = {};

/**
    Class: O.GlobalKeyboardShortcuts

    Extends: O.Object

    This class facilitates adding keyboard shortcuts to your application.
*/
const GlobalKeyboardShortcuts = Class({

    Extends: Obj,

    /**
        Property: O.GlobalKeyboardShortcuts#isEnabled
        Type: Boolean
        Default: true

        Callbacks will only fire if this property is true when the instance
        handles the event.
    */

    /**
        Property (private): O.GlobalKeyboardShortcuts#_shortcuts
        Type: Object

        The map of shortcut key to an array of `[object, method]` tuples.
    */

    /**
        Constructor: O.GlobalKeyboardShortcuts
    */
    init: function (/* ...mixins */) {
        this.isEnabled = true;
        this._shortcuts = {};

        GlobalKeyboardShortcuts.parent.constructor.apply( this, arguments );

        ViewEventsController.kbShortcuts = this;
        ViewEventsController.addEventTarget( this, -10 );
    },

    /**
        Method: O.GlobalKeyboardShortcuts#destroy

        Destructor.
    */
    destroy () {
        if ( ViewEventsController.kbShortcuts === this ) {
            delete ViewEventsController.kbShortcuts;
        }
        ViewEventsController.removeEventTarget( this );
        GlobalKeyboardShortcuts.parent.destroy.call( this );
    },

    /**
        Method: O.GlobalKeyboardShortcuts#register

        Add a global keyboard shortcut. If a shortcut has already been
        registered for this key, it will be replaced, but will be restored when
        the new handler is removed.

        Parameters:
            key     - {String} The key to trigger the callback on. Modifier keys
                      (Alt, Ctrl, Meta, Shift) should be prefixed in
                      alphabetical order and with a hypen after each one.
                      Letters should be lower case. e.g. `Ctrl-f`.

                      The special modifier "Cmd-" may be used, which will map
                      to "Meta-" on a Mac (the command key) and "Ctrl-"
                      elsewhere.
            object  - {Object} The object to trigger the callback on.
            method  - {String} The name of the method to trigger.
            ifInput - {Number} Determines whether the shortcut is active when
                      focused inside an <input> or equivalent. Defaults to
                      active if and only if Meta or Ctrl are part of the
                      shortcut. The value must be one of:

                      * DEFAULT_IN_INPUT (Use the default)
                      * ACTIVE_IN_INPUT (Active when input is focused)
                      * DISABLE_IN_INPUT (Not active when input is focused)

        Returns:
            {O.GlobalKeyboardShortcuts} Returns self.
    */
    register ( key, object, method, ifInput ) {
        key = toPlatformKey( key );
        const shortcuts = this._shortcuts;
        ( shortcuts[ key ] || ( shortcuts[ key ] = [] ) )
            .push([ object, method, ifInput || DEFAULT_IN_INPUT ]);
        return this;
    },

    /**
        Method: O.GlobalKeyboardShortcuts#deregister

        Remove a global keyboard shortcut. Must use identical arguments to those
        which were used in the call to <O.GlobalKeyboardShortcuts#register>.

        Parameters:
            key    - {String} The key on which the callback was triggered.
            object - {Object} The object on which the callback was triggered.
            method - {String} The name of the method that was being triggered.

        Returns:
            {O.GlobalKeyboardShortcuts} Returns self.
    */
    deregister ( key, object, method ) {
        key = toPlatformKey( key );
        const current = this._shortcuts[ key ];
        const length = current ? current.length : 0;
        let l = length;
        while ( l-- ) {
            const item = current[l];
            if ( item[0] === object && item[1] === method ) {
                if ( length === 1 ) {
                    delete this._shortcuts[ key ];
                } else {
                    current.splice( l, 1 );
                }
            }
        }
        return this;
    },

    /**
        Method: O.GlobalKeyboardShortcuts#getHandlerForKey

        Get the keyboard shortcut to be triggered by a key combo, represented as
        a string, as output by <O.DOMEvent#lookupKey>.

        Parameters:
            key - {String} The key combo to get the handler for.

        Returns:
            {Array|null} Returns the [ object, method ] tuple to be triggered by
            the event, or null if nothing is registered for this key press.
    */
    getHandlerForKey ( key ) {
        const shortcuts = this._shortcuts[ key ];
        if ( shortcuts && this.get( 'isEnabled' ) ) {
            return shortcuts[ shortcuts.length - 1 ];
        }
        return null;
    },

    /**
        Method: O.GlobalKeyboardShortcuts#trigger

        Keypress event handler. Triggers any registered callback.

        Parameters:
            event - {DOMEvent} The keydown/keypress event.
    */
    trigger: function ( event ) {
        const target = event.target;
        const nodeName = target.nodeName;
        const key = lookupKey( event );
        const allowedInInput =
            ( isApple ? event.metaKey : event.ctrlKey ) &&
            ( event.altKey || /-(?:.|Enter)$/.test( key ) );
        const inputIsFocused = (
            nodeName === 'TEXTAREA' ||
            nodeName === 'SELECT' ||
            ( nodeName === 'INPUT' && !allowedInputs[ target.type ] ) ||
            ( event.targetView instanceof RichTextView )
        );
        if ( event.type === 'keydown' ) {
            handleOnDown[ key ] = true;
        } else if ( handleOnDown[ key ] ) {
            return;
        }
        const handler = this.getHandlerForKey( key );
        if ( handler ) {
            const ifInput = handler[2];
            if ( inputIsFocused && ifInput !== ACTIVE_IN_INPUT &&
                    ( !allowedInInput || ifInput === DISABLE_IN_INPUT ) ) {
                return;
            }
            handler[0][ handler[1] ]( event );
            if ( !event.doDefault ) {
                event.preventDefault();
            }
        }
    }.on( 'keydown', 'keypress' ),
});

GlobalKeyboardShortcuts.DEFAULT_IN_INPUT = DEFAULT_IN_INPUT;
GlobalKeyboardShortcuts.ACTIVE_IN_INPUT = ACTIVE_IN_INPUT;
GlobalKeyboardShortcuts.DISABLE_IN_INPUT = DISABLE_IN_INPUT;

export default GlobalKeyboardShortcuts;
<commit_msg>Fix lint error with excessively long line<commit_after>import { Class } from '../core/Core';
import Obj from '../foundation/Object';
import '../foundation/EventTarget';  // For Function#on
import { isApple } from '../ua/UA';
import { lookupKey } from '../dom/DOMEvent';
import RichTextView from '../views/controls/RichTextView';
import ViewEventsController from '../views/ViewEventsController';
import {
    DEFAULT_IN_INPUT,
    ACTIVE_IN_INPUT,
    DISABLE_IN_INPUT,
} from './keyboardShortcuts.js';
import toPlatformKey from './toPlatformKey';

const allowedInputs = {
    checkbox: 1,
    radio: 1,
    file: 1,
    submit: 1,
};

const handleOnDown = {};

/**
    Class: O.GlobalKeyboardShortcuts

    Extends: O.Object

    This class facilitates adding keyboard shortcuts to your application.
*/
const GlobalKeyboardShortcuts = Class({

    Extends: Obj,

    /**
        Property: O.GlobalKeyboardShortcuts#isEnabled
        Type: Boolean
        Default: true

        Callbacks will only fire if this property is true when the instance
        handles the event.
    */

    /**
        Property (private): O.GlobalKeyboardShortcuts#_shortcuts
        Type: Object

        The map of shortcut key to an array of `[object, method]` tuples.
    */

    /**
        Constructor: O.GlobalKeyboardShortcuts
    */
    init: function (/* ...mixins */) {
        this.isEnabled = true;
        this._shortcuts = {};

        GlobalKeyboardShortcuts.parent.constructor.apply( this, arguments );

        ViewEventsController.kbShortcuts = this;
        ViewEventsController.addEventTarget( this, -10 );
    },

    /**
        Method: O.GlobalKeyboardShortcuts#destroy

        Destructor.
    */
    destroy () {
        if ( ViewEventsController.kbShortcuts === this ) {
            delete ViewEventsController.kbShortcuts;
        }
        ViewEventsController.removeEventTarget( this );
        GlobalKeyboardShortcuts.parent.destroy.call( this );
    },

    /**
        Method: O.GlobalKeyboardShortcuts#register

        Add a global keyboard shortcut. If a shortcut has already been
        registered for this key, it will be replaced, but will be restored when
        the new handler is removed.

        Parameters:
            key     - {String} The key to trigger the callback on. Modifier keys
                      (Alt, Ctrl, Meta, Shift) should be prefixed in
                      alphabetical order and with a hypen after each one.
                      Letters should be lower case. e.g. `Ctrl-f`.

                      The special modifier "Cmd-" may be used, which will map
                      to "Meta-" on a Mac (the command key) and "Ctrl-"
                      elsewhere.
            object  - {Object} The object to trigger the callback on.
            method  - {String} The name of the method to trigger.
            ifInput - {Number} Determines whether the shortcut is active when
                      focused inside an <input> or equivalent. Defaults to
                      active if and only if Meta or Ctrl are part of the
                      shortcut. The value must be one of:

                      * DEFAULT_IN_INPUT (Use the default)
                      * ACTIVE_IN_INPUT (Active when input is focused)
                      * DISABLE_IN_INPUT (Not active when input is focused)

        Returns:
            {O.GlobalKeyboardShortcuts} Returns self.
    */
    register ( key, object, method, ifInput ) {
        key = toPlatformKey( key );
        const shortcuts = this._shortcuts;
        ( shortcuts[ key ] || ( shortcuts[ key ] = [] ) )
            .push([ object, method, ifInput || DEFAULT_IN_INPUT ]);
        return this;
    },

    /**
        Method: O.GlobalKeyboardShortcuts#deregister

        Remove a global keyboard shortcut. Must use identical arguments to those
        which were used in the call to <O.GlobalKeyboardShortcuts#register>.

        Parameters:
            key    - {String} The key on which the callback was triggered.
            object - {Object} The object on which the callback was triggered.
            method - {String} The name of the method that was being triggered.

        Returns:
            {O.GlobalKeyboardShortcuts} Returns self.
    */
    deregister ( key, object, method ) {
        key = toPlatformKey( key );
        const current = this._shortcuts[ key ];
        const length = current ? current.length : 0;
        let l = length;
        while ( l-- ) {
            const item = current[l];
            if ( item[0] === object && item[1] === method ) {
                if ( length === 1 ) {
                    delete this._shortcuts[ key ];
                } else {
                    current.splice( l, 1 );
                }
            }
        }
        return this;
    },

    /**
        Method: O.GlobalKeyboardShortcuts#getHandlerForKey

        Get the keyboard shortcut to be triggered by a key combo, represented as
        a string, as output by <O.DOMEvent#lookupKey>.

        Parameters:
            key - {String} The key combo to get the handler for.

        Returns:
            {Array|null} Returns the [ object, method ] tuple to be triggered by
            the event, or null if nothing is registered for this key press.
    */
    getHandlerForKey ( key ) {
        const shortcuts = this._shortcuts[ key ];
        if ( shortcuts && this.get( 'isEnabled' ) ) {
            return shortcuts[ shortcuts.length - 1 ];
        }
        return null;
    },

    /**
        Method: O.GlobalKeyboardShortcuts#trigger

        Keypress event handler. Triggers any registered callback.

        Parameters:
            event - {DOMEvent} The keydown/keypress event.
    */
    trigger: function ( event ) {
        const target = event.target;
        const nodeName = target.nodeName;
        const key = lookupKey( event );
        const allowedInInput =
            ( isApple ? event.metaKey : event.ctrlKey ) &&
            ( event.altKey || /-(?:.|Enter)$/.test( key ) );
        const inputIsFocused = (
            nodeName === 'TEXTAREA' ||
            nodeName === 'SELECT' ||
            ( nodeName === 'INPUT' && !allowedInputs[ target.type ] ) ||
            ( event.targetView instanceof RichTextView )
        );
        if ( event.type === 'keydown' ) {
            handleOnDown[ key ] = true;
        } else if ( handleOnDown[ key ] ) {
            return;
        }
        const handler = this.getHandlerForKey( key );
        if ( handler ) {
            const ifInput = handler[2];
            if ( inputIsFocused && ifInput !== ACTIVE_IN_INPUT &&
                    ( !allowedInInput || ifInput === DISABLE_IN_INPUT ) ) {
                return;
            }
            handler[0][ handler[1] ]( event );
            if ( !event.doDefault ) {
                event.preventDefault();
            }
        }
    }.on( 'keydown', 'keypress' ),
});

GlobalKeyboardShortcuts.DEFAULT_IN_INPUT = DEFAULT_IN_INPUT;
GlobalKeyboardShortcuts.ACTIVE_IN_INPUT = ACTIVE_IN_INPUT;
GlobalKeyboardShortcuts.DISABLE_IN_INPUT = DISABLE_IN_INPUT;

export default GlobalKeyboardShortcuts;
<|endoftext|>


<commit_before>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
      timeout = 20;
    } else {
      timeout = randomIntFromInterval(0, max_sleep) * 1000
    }
    setTimeout(commit, timeout, t_offset, outFD, outFile, data, counter, length);
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [max_time_in_seconds] [step]")
}

function sleep(seconds) {
  var endTime = new Date().getTime() + (seconds * 1000);
  while (new Date().getTime() <= endTime) {;}
}

function randomIntFromInterval(min,max)
{
   return Math.floor(Math.random()*(max-min+1)+min);
}

function commit(t_offset, outFD, outFile, data, counter, length)
{
  if (counter < length)
  {
    var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
    var args = ['commit', outFile, '-m', message]
    if (counter + step >= length) step = length - counter
    process.stdout.write("\033[2K Writing " + counter + "/" + length + " bytes: " + message + "\r");
    fs.writeSync(outFD, data.slice(counter, counter+step), 0, step)
    child_process.execFileSync('/Applications/Xcode.app/Contents/Developer/usr/bin/git', args)
    var timeout = randomIntFromInterval(0, max_sleep)
    counter = counter + step
    if (process.env.FAKETIME) {
      t_offset = t_offset + time<commit_msg>Fixing punctuation<commit_after>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit_time_hour, commit_time_min, commit_time_sec)
    var t_offset = 0
    if (process.env.FAKETIME) {
      // 20% buffer, to reduce the likelihood of going in to the future
      t_offset = commit_time * -1.2;
      faketime(t_offset);
    }
    try {
      child_process.execFileSync('git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var counter = 0
    process.stdout.write("Queueing up the first commit...\r");
    var timeout;
    if (process.env.FAKETIME) {
      faketime(t_offset);
      timeout = 20;
    } else {
      timeout = randomIntFromInterval(0, max_sleep) * 1000
    }
    setTimeout(commit, timeout, t_offset, outFD, outFile, data, counter, length);
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [max_time_in_seconds] [step]")
}

function sleep(seconds) {
  var endTime = new Date().getTime() + (seconds * 1000);
  while (new Date().getTime() <= endTime) {;}
}

function randomIntFromInterval(min,max)
{
   return Math.floor(Math.random()*(max-min+1)+min);
}

function commit(t_offset, outFD, outFile, data, counter, length)
{
  if (counter < length)
  {
    var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
    var args = ['commit', outFile, '-m', message]
    if (counter + step >= length) step = length - counter
    process.stdout.write("\033[2K Writing " + counter + "/" + length + " bytes: " + message + "\r");
    fs.writeSync(outFD, data.slice(counter, counter+step), 0, step)
    child_process.execFileSync('/Applications/Xcode.app/Contents/Developer/usr/bin/git', args)
    var timeout = randomIntFromInterval(0, max_sleep)
    counter = counter + step
    if (process.env.FAKETIME) {
      t_offset = t_offset + timeo<|endoftext|>


<commit_before>import request from 'request';
import constantStrings from '../utils/constantStrings.json';
import testimonials from '../resources/testimonials.json';

const githubClient = process.env.GITHUB_ID;
const githubSecret = process.env.GITHUB_SECRET;

module.exports = function(app) {
  const router = app.loopback.Router();
  const User = app.models.User;

  router.get('/api/github', githubCalls);
  router.get('/chat', chat);
  router.get('/twitch', twitch);
  router.get('/u/:email', unsubscribe);
  router.get('/unsubscribe/:email', unsubscribe);
  router.get('/submit-cat-photo', submitCatPhoto);
  router.get(
    '/the-fastest-web-page-on-the-internet',
    theFastestWebPageOnTheInternet
  );
  router.get('/unsubscribed', unsubscribed);
  router.get('/nonprofits', nonprofits);
  router.get('/nonprofits-form', nonprofitsForm);
  router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
  router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
  router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
  router.get('/stories', showTestimonials);
  router.get('/all-stories', showAllTestimonials);
  router.get('/how-nonprofit-projects-work', howNonprofitProjectsWork);
  router.get(
      '/software-resources-for-nonprofits',
      softwareResourcesForNonprofits
  );
  router.get('/academic-honesty', academicHonesty);

  app.use(router);

  function chat(req, res) {
    res.redirect('https://gitter.im/FreeCodeCamp/FreeCodeCamp');
  }

  function howNonprofitProjectsWork(req, res) {
      res.redirect(301,
        'https://medium.freecodecamp.com/open-source-for-good-1a0ea9f32d5a');

  }

  function softwareResourcesForNonprofits(req, res) {
    res.render('resources/software-resources-for-nonprofits', {
      title: 'Software Resources for Nonprofits'
    });
  }

  function academicHonesty(req, res) {
      res.render('resources/academic-honesty', {
          title: 'Academic Honesty policy'
      });
  }

  function theFastestWebPageOnTheInternet(req, res) {
    res.render('resources/the-fastest-web-page-on-the-internet', {
      title: 'This is the fastest web page on the internet'
    });
  }

  function showTestimonials(req, res) {
    res.render('resources/stories', {
      title: 'Testimonials from Happy freeCodeCamp Students ' +
        'who got Software Engineer Jobs',
      stories: testimonials.slice(0, 72),
      moreStories: true
    });
  }

  function showAllTestimonials(req, res) {
    res.render('resources/stories', {
      title: 'Testimonials from Happy freeCodeCamp Students ' +
        'who got Software Engineer Jobs',
      stories: testimonials,
      moreStories: false
    });
  }

  function submitCatPhoto(req, res) {
    res.send('Submitted!');
  }

  function bootcampCalculator(req, res) {
    res.render('resources/calculator', {
      title: 'Coding Bootcamp Cost Calculator'
    });
  }

  function nonprofits(req, res) {
    res.render('resources/nonprofits', {
      title: 'Your Nonprofit Can Get Pro Bono Code'
    });
  }

  function nonprofitsForm(req, res) {
    res.render('resources/nonprofits-form', {
      title: 'Nonprofit Projects Proposal Form'
    });
  }

  function agileProjectManagers(req, res) {
    res.render('resources/pmi-acp-agile-project-managers', {
      title: 'Get Agile Project Management Experience for the PMI-ACP'
    });
  }

  function agileProjectManagersForm(req, res) {
    res.render('resources/pmi-acp-agile-project-managers-form', {
      title: 'Agile Project Management Program Application Form'
    });
  }

  function twitch(req, res) {
    res.redirect('https://twitch.tv/freecodecamp');
  }

  function unsubscribe(req, res, next) {
    req.checkParams(
      'email',
      `"${req.params.email}" isn't a valid email address.`
    ).isEmail();
    const errors = req.validationErrors(true);
    if (errors) {
      req.flash('error', { msg: errors.email.msg });
      return res.redirect('/');
    }
    return User.find({
      where: {
        email: req.params.email
      }
    }, (err, users) => {
      if (err) { return next(err); }
      if (!users.length) {
        req.flash('info', {
          msg: 'Email address not found. Please update your Email ' +
            'preferences from your settings.'
        });
        return res.redirect('/');
      }

      const updates = users.map(user => {
        return new Promise((resolve, reject) =>
          user.updateAttributes({
            sendQuincyEmail: false
          }, (err) => {
            if (err) {
              reject(err);
            } else {
              resolve();
            }
          })
        );
      });
      return Promise.all(updates)
        .then(() => {
          req.flash('info', {
            msg: 'We\'ve successfully updated your Email preferences.'
          });
          return res.redirect('/unsubscribed/' + req.params.email);
        })
        .catch(next);
    });
  }

  function unsubscribed(req, res) {
    res.render('resources/unsubscribed', {
      title: 'You have been unsubscribed'
    });
  }

  function githubCalls(req, res, next) {
    var githubHeaders = {
      headers: {
        'User-Agent': constantStrings.gitHubUserAgent
      },
      port: 80
    };
    request(
      [
        'https://api.github.com/repos/freecodecamp/',
        'freecodecamp/pulls?client_id=',
        githubClient,
        '&client_secret=',
        githubSecret
      ].join(''),
      githubHeaders,
      function(err, status1, pulls) {
        if (err) { return next(err); }
        pulls = pulls ?
          Object.keys(JSON.parse(pulls)).length :
          'Can\'t connect to github';

        return request(
          [
            'https://api.github.com/repos/freecodecamp/',
            'freecodecamp/issues?client_id=',
            githubClient,
            '&client_secret=',
            githubSecret
          ].join(''),
          githubHeaders,
          function(err, status2, issues) {
            if (err) { return next(err); }
            issues = ((pulls === parseInt(pulls, 10)) && issues) ?
            Object.keys(JSON.parse(issues)).length - pulls :
              "Can't connect to GitHub";
            return res.send({
              issues: issues,
              pulls: pulls
            });
          }
        );
      }
    );
  }
};
<commit_msg>feat(unsub-email): Add an annon unsubscribe route<commit_after>import request from 'request';
import { isMongoId } from 'validator';
import dedent from 'dedent';

import constantStrings from '../utils/constantStrings.json';
import testimonials from '../resources/testimonials.json';
import { wrapHandledError } from '../utils/create-handled-error';

const githubClient = process.env.GITHUB_ID;
const githubSecret = process.env.GITHUB_SECRET;

module.exports = function(app) {
  const router = app.loopback.Router();
  const User = app.models.User;

  router.get('/api/github', githubCalls);
  router.get('/chat', chat);
  router.get('/twitch', twitch);
  router.get('/u/:email', unsubscribe);
  router.get('/unsubscribe/:email', unsubscribe);
  router.get('/z', unsubscribeAnon);
  router.get(
    '/the-fastest-web-page-on-the-internet',
    theFastestWebPageOnTheInternet
  );
  router.get('/unsubscribed', unsubscribed);
  router.get('/nonprofits', nonprofits);
  router.get('/nonprofits-form', nonprofitsForm);
  router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
  router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
  router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
  router.get('/stories', showTestimonials);
  router.get('/all-stories', showAllTestimonials);
  router.get('/how-nonprofit-projects-work', howNonprofitProjectsWork);
  router.get(
      '/software-resources-for-nonprofits',
      softwareResourcesForNonprofits
  );
  router.get('/academic-honesty', academicHonesty);

  app.use(router);

  function chat(req, res) {
    res.redirect('https://gitter.im/FreeCodeCamp/FreeCodeCamp');
  }

  function howNonprofitProjectsWork(req, res) {
      res.redirect(301,
        'https://medium.freecodecamp.com/open-source-for-good-1a0ea9f32d5a');

  }

  function softwareResourcesForNonprofits(req, res) {
    res.render('resources/software-resources-for-nonprofits', {
      title: 'Software Resources for Nonprofits'
    });
  }

  function academicHonesty(req, res) {
      res.render('resources/academic-honesty', {
          title: 'Academic Honesty policy'
      });
  }

  function theFastestWebPageOnTheInternet(req, res) {
    res.render('resources/the-fastest-web-page-on-the-internet', {
      title: 'This is the fastest web page on the internet'
    });
  }

  function showTestimonials(req, res) {
    res.render('resources/stories', {
      title: 'Testimonials from Happy freeCodeCamp Students ' +
        'who got Software Engineer Jobs',
      stories: testimonials.slice(0, 72),
      moreStories: true
    });
  }

  function showAllTestimonials(req, res) {
    res.render('resources/stories', {
      title: 'Testimonials from Happy freeCodeCamp Students ' +
        'who got Software Engineer Jobs',
      stories: testimonials,
      moreStories: false
    });
  }

  function bootcampCalculator(req, res) {
    res.render('resources/calculator', {
      title: 'Coding Bootcamp Cost Calculator'
    });
  }

  function nonprofits(req, res) {
    res.render('resources/nonprofits', {
      title: 'Your Nonprofit Can Get Pro Bono Code'
    });
  }

  function nonprofitsForm(req, res) {
    res.render('resources/nonprofits-form', {
      title: 'Nonprofit Projects Proposal Form'
    });
  }

  function agileProjectManagers(req, res) {
    res.render('resources/pmi-acp-agile-project-managers', {
      title: 'Get Agile Project Management Experience for the PMI-ACP'
    });
  }

  function agileProjectManagersForm(req, res) {
    res.render('resources/pmi-acp-agile-project-managers-form', {
      title: 'Agile Project Management Program Application Form'
    });
  }

  function twitch(req, res) {
    res.redirect('https://twitch.tv/freecodecamp');
  }

  function unsubscribeAnon(req, res, next) {
    const { query: { unsubscribeId } } = req;
    const isValid = unsubscribeId && isMongoId(unsubscribeId);
    if (!isValid) {
      throw wrapHandledError(
        new Error('unsubscribeId is not a mongo ObjectId'),
        {
          message: dedent`
            Oops... something is not right. We could not unsubscribe that email
            address
          `,
          type: 'danger',
          redirectTo: '/'
        }
      );
    }
    User.findOne({
      where: { unsubscribeId }
    }, (err, user) => {
      if (err) { return next(err); }
      if (!user || !user.email) {
        throw wrapHandledError(
          new Error('No user or user email to unsubscribe'),
          {
            message: dedent`
              We couldn't find a user account to unsubscribe, are you clicking
              a link from an email we sent?
            `,
            type: 'info',
            redirectTo: '/'
          }
        );
      }
      return user.update$({
        sendQuincyEmail: false
      }).subscribe(() => {
        req.flash(
          'info',
          'We\'ve successfully updated your Email preferences.'
        );
        return res.redirect('/unsubscribed');
      },
        next,
        () => {
          return user.manualReload();
        }
      );
    });
  }

  function unsubscribe(req, res, next) {
    req.checkParams(
      'email',
      `"${req.params.email}" isn't a valid email address.`
    ).isEmail();
    const errors = req.validationErrors(true);
    if (errors) {
      req.flash('error', { msg: errors.email.msg });
      return res.redirect('/');
    }
    return User.find({
      where: {
        email: req.params.email
      }
    }, (err, users) => {
      if (err) { return next(err); }
      if (!users.length) {
        req.flash('info', {
          msg: 'Email address not found. Please update your Email ' +
            'preferences from your settings.'
        });
        return res.redirect('/');
      }

      const updates = users.map(user => {
        return new Promise((resolve, reject) =>
          user.updateAttributes({
            sendQuincyEmail: false
          }, (err) => {
            if (err) {
              reject(err);
            } else {
              resolve();
            }
          })
        );
      });
      return Promise.all(updates)
        .then(() => {
          req.flash('info', {
            msg: 'We\'ve successfully updated your Email preferences.'
          });
          return res.redirect('/unsubscribed');
        })
        .catch(next);
    });
  }

  function unsubscribed(req, res) {
    res.render('resources/unsubscribed', {
      title: 'You have been unsubscribed'
    });
  }

  function githubCalls(req, res, next) {
    var githubHeaders = {
      headers: {
        'User-Agent': constantStrings.gitHubUserAgent
      },
      port: 80
    };
    request(
      [
        'https://api.github.com/repos/freecodecamp/',
        'freecodecamp/pulls?client_id=',
        githubClient,
        '&client_secret=',
        githubSecret
      ].join(''),
      githubHeaders,
      function(err, status1, pulls) {
        if (err) { return next(err); }
        pulls = pulls ?
          Object.keys(JSON.parse(pulls)).length :
          'Can\'t connect to github';

        return request(
          [
            'https://api.github.com/repos/freecodecamp/',
            'freecodecamp/issues?client_id=',
            githubClient,
            '&client_secret=',
            githubSecret
          ].join(''),
          githubHeaders,
          function(err, status2, issues) {
            if (err) { return next(err); }
            issues = ((pulls === parseInt(pulls, 10)) && issues) ?
            Object.keys(JSON.parse(issues)).length - pulls :
              "Can't connect to GitHub";
            return res.send({
              issues: issues,
              pulls: pulls
            });
          }
        );
      }
    );
  }
};
<|endoftext|>