<commit_before>// ==UserScript==
// @name         OPR China Map Helper
// @version      0.4.3
// @category     Info
// @namespace    https://github.com/Ingrass/OPR-Tools/
// @updateURL    http://ethern.me/ingress/OPR_China_Map_Helper.user.js
// @downloadURL  http://ethern.me/ingress/OPR_China_Map_Helper.user.js
// @description  Add some buttons for China map in OPR
// @author       Ethern Triomphe346 19John 记忆的残骸 stdssr
// @match        https://opr.ingress.com/recon
// @grant        none
// @require      https://cdn.jsdelivr.net/clipboard.js/1.6.1/clipboard.min.js
// ==/UserScript==

(function() {
    //constants
    var x_PI = 3.14159265358979324 * 3000.0 / 180.0;
    var PI = 3.1415926535897932384626;
    var a = 6378245.0;
    var ee = 0.00669342162296594323;

    //global var
    var name;
    var postion;
    var description;

    function transform_lat(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        var ret = -100.0 + 2.0 * lng1 + 3.0 * lat1 + 0.2 * lat1 * lat1 + 0.1 * lng1 * lat1 + 0.2 * Math.sqrt(Math.abs(lng1));
        ret += (20.0 * Math.sin(6.0 * lng1 * PI) + 20.0 * Math.sin(2.0 * lng1 * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lat1 * PI) + 40.0 * Math.sin(lat1 / 3.0 * PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(lat1 / 12.0 * PI) + 320 * Math.sin(lat1 * PI / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    function transform_lng(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        var ret = 300.0 + lng1 + 2.0 * lat1 + 0.1 * lng1 * lng1 + 0.1 * lng1 * lat1 + 0.1 * Math.sqrt(Math.abs(lng1));
        ret += (20.0 * Math.sin(6.0 * lng1 * PI) + 20.0 * Math.sin(2.0 * lng1 * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lng1 * PI) + 40.0 * Math.sin(lng1 / 3.0 * PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(lng1 / 12.0 * PI) + 300.0 * Math.sin(lng1 / 30.0 * PI)) * 2.0 / 3.0;
        return ret;
    }

    function out_of_china(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        // 纬度3.86~53.55,经度73.66~135.05
        return !(lng1 > 73.66 && lng1 < 135.05 && lat1 > 3.86 && lat1 < 53.55);
    }

    /**
     * WGS-84 to 火星坐标系(GCJ-02)
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    function wgs84togcj02(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        if (out_of_china(lng1, lat1)) {
            return [lng1, lat1];
        } else {
            var dlat = transform_lat(lng1 - 105.0, lat1 - 35.0);
            var dlng = transform_lng(lng1 - 105.0, lat1 - 35.0);
            var radlat = lat1 / 180.0 * PI;
            var magic = Math.sin(radlat);
            magic = 1 - ee * magic * magic;
            var sqrtmagic = Math.sqrt(magic);
            dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
            dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
            var mglat = lat1 + dlat;
            var mglng = lng1 + dlng;
            return [mglng, mglat];
        }
    }

    /**
     * 火星坐标系(GCJ-02) to 百度坐标系(BD-09) 的转换
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    function gcj02tobd09(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        var z = Math.sqrt(lng1 * lng1 + lat1 * lat1) + 0.00002 * Math.sin(lat1 * x_PI);
        var theta = Math.atan2(lat1, lng1) + 0.000003 * Math.cos(lng1 * x_PI);
        var bd_lng = z * Math.cos(theta) + 0.0065;
        var bd_lat = z * Math.sin(theta) + 0.006;
        return [bd_lng, bd_lat];
    }

    function get_portal_info() {
        var name = target.getElementsByTagName('a')[0].innerHTML;
        var position = target.getElementsByTagName('a')[1].getAttribute("href");
        // var description = target.getElementsByTagName('a')[2].get
        name = /^\s*(.*)\s*$/.exec(name)[1];
        position = /@(.*)$/.exec(position)[1];
        //TODO: to be verified
        // if (position.indexOf("www.ingress.com/intel?ll=") >= 0) {
        //     position = /=(.*)$/.exec(position)[1];
        //     var end = position.indexOf("&z=20");
        //     if (end >= 0) {
        //         position = position.substring(0, end);
        //     }
        // } else {
        //     position = /@(.*)$/.exec(position)[1];
        // }
        return [name, position];
    }

    function get_image_link() {
        var img = document.getElementsByClassName("center-cropped-img")[0];
        return img.getAttribute("src");
    }

    function get_tencent_link() {
        var portal_info = get_portal_info();
        var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        var gcj = wgs84togcj02(wgs_lng, wgs_lat);
        return "http://map.qq.com/?type=marker&isopeninfowin=1&markertype=1&name=" + name + "&addr=" + position + "&pointy=" + gcj[1] + "&pointx=" + gcj[0];
    }

    function goto_tencent_map() {
        var href = get_tencent_link();
        window.open(href, "map");
    }

    function get_baidu_link() {
        var portal_info = get_portal_info();
        var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "http://api.map.baidu.com/marker?location=" + wgs_lat + "," + wgs_lng + "&title=" + name + "&content=Application&output=html&coord_type=wgs84";
    }

    function goto_baidu_map() {
        var href = get_baidu_link();
        window.open(href, "map");
    }

    function get_OSM_link() {
        var portal_info = get_portal_info();
        // var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "http://www.openstreetmap.org/?mlat=" + wgs_lat + "&mlon=" + wgs_lng + "#map=16/" + wgs_lat + "/" + wgs_lng;
    }

    function goto_OSM() {
        var href = get_OSM_link();
        window.open(href, "map");
    }

    //AutoNavi (高德地图)
    function get_autonavi_link() {
        var portal_info = get_portal_info();
        var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "http://uri.amap.com/marker?position=" + wgs_lng + "," + wgs_lat + "&name=" + name + "&coordinate=wgs84&callnative=0";
    }

    function goto_autonavi_map() {
        href = get_autonavi_link();
        window.open(href, "map");
    }

    function get_intel_link() {
        var portal_info = get_portal_info();
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "https://ingress.com/intel?z=14&ll=" + wgs_lat + "," + wgs_lng;
    }

    function goto_intel_map() {
        var href = get_intel_link();
        window.open(href, "map");
    }

    function get_multimap_link() {
        var portal_info = get_portal_info();
        // var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "http://kitten-114.getforge.io/index.html#" + wgs_lng + "," + wgs_lat;
    }

    function goto_multimap() {
        var href = get_multimap_link();
        window.open(href, "map");
    }

    function get_clipboard() {
        var portal_info = get_portal_info();
        var result = "";
        result += "OPR Portal Candidate\n";
        result += "Title: " + portal_info[0] + "\n";
        result += "Image: " + get_image_link() + "=s0\n";
        result += "Baidu Map link: " + get_baidu_link() + "\n";
        return result;
    }

    var target = document.getElementById("descriptionDiv");
    target.appendChild(document.createElement("br"));
    target.appendChild(document.createElement("br"));

    //Ingress Intel Button
    var intel_map_button = document.createElement("button");
    var textnode = document.createTextNode("Intel");
    intel_map_button.className += "button";
    intel_map_button.onclick = goto_intel_map;
    intel_map_button.appendChild(textnode);
    target.appendChild(intel_map_button);

    //Tencent Map Button
    var tencent_map_button = document.createElement("button");
    var textnode_Tencent = document.createTextNode("Tencent");
    tencent_map_button.className += "button";
    tencent_map_button.onclick = goto_tencent_map;
    tencent_map_button.appendChild(textnode_Tencent);
    target.appendChild(tencent_map_button);

    //Baidu Map Button
    var baidu_map_button = document.createElement("button");
    var textnode_Baidu = document.createTextNode("Baidu");
    baidu_map_button.className += "button";
    baidu_map_button.onclick = goto_baidu_map;
    baidu_map_button.appendChild(textnode_Baidu);
    target.appendChild(baidu_map_button);

    //AutoNavi Button
    var autonavi_button = document.createElement("button");
    var textnode_AutoNavi = document.createTextNode("AutoNavi");
    autonavi_button.className += "button";
    autonavi_button.onclick = goto_autonavi_map;
    autonavi_button.appendChild(textnode_AutoNavi);
    target.appendChild(autonavi_button);

    //OSM Button
    var OSM_button = document.createElement("button");
    var textnode_OSM = document.createTextNode("OSM");
    OSM_button.className += "button";
    OSM_button.onclick = goto_OSM;
    OSM_button.appendChild(textnode_OSM);
    target.appendChild(OSM_button);

    //Testing_multimaps Button
    var Multimap_button = document.createElement("button");
    var textnode_Tencent_Baidu = document.createTextNode("Tencent+Baidu");
    Multimap_button.className += "button";
    Multimap_button.onclick = goto_multimap;
    Multimap_button.appendChild(textnode_Tencent_Baidu);
    target.appendChild(Multimap_button);

    //margin
    target.appendChild(document.createElement("br"));
    target.appendChild(document.createElement("br"));

    //Copy portal info to clipboard Button
    var clipboard_button = document.createElement("button");
    var textnode_copy = document.createTextNode("Copy to Clipboard");
    clipboard_button.className += "button";
    clipboard_button.className += " clipbtn";
    clipboard_button.appendChild(textnode_copy);
    target.appendChild(clipboard_button);

    new Clipboard('.clipbtn', {
        text: function(trigger) {
            return get_clipboard();
        }
    });

})();<commit_msg>small modification<commit_after>// ==UserScript==
// @name         OPR China Map Helper
// @version      0.4.4
// @category     Info
// @namespace    https://github.com/Ingrass/OPR-Tools/
// @updateURL    http://ethern.me/ingress/OPR_China_Map_Helper.user.js
// @downloadURL  http://ethern.me/ingress/OPR_China_Map_Helper.user.js
// @description  Add some buttons for China map in OPR
// @author       Ethern Triomphe346 19John 记忆的残骸 stdssr
// @match        https://opr.ingress.com/recon
// @grant        none
// @require      https://cdn.jsdelivr.net/clipboard.js/1.6.1/clipboard.min.js
// ==/UserScript==

(function() {
    //constants
    var x_PI = 3.14159265358979324 * 3000.0 / 180.0;
    var PI = 3.1415926535897932384626;
    var a = 6378245.0;
    var ee = 0.00669342162296594323;

    //global var
    var name;
    var postion;
    var description;

    function transform_lat(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        var ret = -100.0 + 2.0 * lng1 + 3.0 * lat1 + 0.2 * lat1 * lat1 + 0.1 * lng1 * lat1 + 0.2 * Math.sqrt(Math.abs(lng1));
        ret += (20.0 * Math.sin(6.0 * lng1 * PI) + 20.0 * Math.sin(2.0 * lng1 * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lat1 * PI) + 40.0 * Math.sin(lat1 / 3.0 * PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(lat1 / 12.0 * PI) + 320 * Math.sin(lat1 * PI / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    function transform_lng(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        var ret = 300.0 + lng1 + 2.0 * lat1 + 0.1 * lng1 * lng1 + 0.1 * lng1 * lat1 + 0.1 * Math.sqrt(Math.abs(lng1));
        ret += (20.0 * Math.sin(6.0 * lng1 * PI) + 20.0 * Math.sin(2.0 * lng1 * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lng1 * PI) + 40.0 * Math.sin(lng1 / 3.0 * PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(lng1 / 12.0 * PI) + 300.0 * Math.sin(lng1 / 30.0 * PI)) * 2.0 / 3.0;
        return ret;
    }

    function out_of_china(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        // 纬度3.86~53.55,经度73.66~135.05
        return !(lng1 > 73.66 && lng1 < 135.05 && lat1 > 3.86 && lat1 < 53.55);
    }

    /**
     * WGS-84 to 火星坐标系(GCJ-02)
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    function wgs84togcj02(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        if (out_of_china(lng1, lat1)) {
            return [lng1, lat1];
        } else {
            var dlat = transform_lat(lng1 - 105.0, lat1 - 35.0);
            var dlng = transform_lng(lng1 - 105.0, lat1 - 35.0);
            var radlat = lat1 / 180.0 * PI;
            var magic = Math.sin(radlat);
            magic = 1 - ee * magic * magic;
            var sqrtmagic = Math.sqrt(magic);
            dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
            dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
            var mglat = lat1 + dlat;
            var mglng = lng1 + dlng;
            return [mglng, mglat];
        }
    }

    /**
     * 火星坐标系(GCJ-02) to 百度坐标系(BD-09) 的转换
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    function gcj02tobd09(lng, lat) {
        var lat1 = +lat;
        var lng1 = +lng;
        var z = Math.sqrt(lng1 * lng1 + lat1 * lat1) + 0.00002 * Math.sin(lat1 * x_PI);
        var theta = Math.atan2(lat1, lng1) + 0.000003 * Math.cos(lng1 * x_PI);
        var bd_lng = z * Math.cos(theta) + 0.0065;
        var bd_lat = z * Math.sin(theta) + 0.006;
        return [bd_lng, bd_lat];
    }

    function get_portal_info() {
        var name = target.getElementsByTagName('a')[0].innerHTML;
        var position = target.getElementsByTagName('a')[1].getAttribute("href");
        // var description = target.getElementsByTagName('a')[2].get
        name = /^\s*(.*)\s*$/.exec(name)[1];
        position = /@(.*)$/.exec(position)[1];
        //TODO: to be verified
        // if (position.indexOf("www.ingress.com/intel?ll=") >= 0) {
        //     position = /=(.*)$/.exec(position)[1];
        //     var end = position.indexOf("&z=20");
        //     if (end >= 0) {
        //         position = position.substring(0, end);
        //     }
        // } else {
        //     position = /@(.*)$/.exec(position)[1];
        // }
        return [name, position];
    }

    function get_image_link() {
        var img = document.getElementsByClassName("center-cropped-img")[0];
        return img.getAttribute("src");
    }

    function get_tencent_link() {
        var portal_info = get_portal_info();
        var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        var gcj = wgs84togcj02(wgs_lng, wgs_lat);
        return "http://map.qq.com/?type=marker&isopeninfowin=1&markertype=1&name=" + name + "&addr=" + position + "&pointy=" + gcj[1] + "&pointx=" + gcj[0];
    }

    function goto_tencent_map() {
        var href = get_tencent_link();
        window.open(href, "map");
    }

    function get_baidu_link() {
        var portal_info = get_portal_info();
        var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "http://api.map.baidu.com/marker?location=" + wgs_lat + "," + wgs_lng + "&title=" + name + "&content=Application&output=html&coord_type=wgs84";
    }

    function goto_baidu_map() {
        var href = get_baidu_link();
        window.open(href, "map");
    }

    function get_OSM_link() {
        var portal_info = get_portal_info();
        // var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "http://www.openstreetmap.org/?mlat=" + wgs_lat + "&mlon=" + wgs_lng + "#map=16/" + wgs_lat + "/" + wgs_lng;
    }

    function goto_OSM() {
        var href = get_OSM_link();
        window.open(href, "map");
    }

    //AutoNavi (高德地图)
    function get_autonavi_link() {
        var portal_info = get_portal_info();
        var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "http://uri.amap.com/marker?position=" + wgs_lng + "," + wgs_lat + "&name=" + name + "&coordinate=wgs84&callnative=0";
    }

    function goto_autonavi_map() {
        href = get_autonavi_link();
        window.open(href, "map");
    }

    function get_intel_link() {
        var portal_info = get_portal_info();
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "https://ingress.com/intel?z=16&ll=" + wgs_lat + "," + wgs_lng;
    }

    function goto_intel_map() {
        var href = get_intel_link();
        window.open(href, "map");
    }

    function get_multimap_link() {
        var portal_info = get_portal_info();
        // var name = portal_info[0];
        var position = portal_info[1];
        var wgs_lat = position.split(",")[0];
        var wgs_lng = position.split(",")[1];
        return "http://kitten-114.getforge.io/index.html#" + wgs_lng + "," + wgs_lat;
    }

    function goto_multimap() {
        var href = get_multimap_link();
        window.open(href, "map");
    }

    function get_clipboard() {
        var portal_info = get_portal_info();
        var result = "";
        result += "OPR Portal Candidate\n";
        result += "Title: " + portal_info[0] + "\n";
        result += "Image: " + get_image_link() + "=s0\n";
        result += "Baidu Map link: " + get_baidu_link() + "\n";
        return result;
    }

    var target = document.getElementById("descriptionDiv");
    target.appendChild(document.createElement("br"));
    target.appendChild(document.createElement("br"));

    //Ingress Intel Button
    var intel_map_button = document.createElement("button");
    var textnode = document.createTextNode("Intel");
    intel_map_button.className += "button";
    intel_map_button.onclick = goto_intel_map;
    intel_map_button.appendChild(textnode);
    target.appendChild(intel_map_button);

    //Tencent Map Button
    var tencent_map_button = document.createElement("button");
    var textnode_Tencent = document.createTextNode("Tencent");
    tencent_map_button.className += "button";
    tencent_map_button.onclick = goto_tencent_map;
    tencent_map_button.appendChild(textnode_Tencent);
    target.appendChild(tencent_map_button);

    //Baidu Map Button
    var baidu_map_button = document.createElement("button");
    var textnode_Baidu = document.createTextNode("Baidu");
    baidu_map_button.className += "button";
    baidu_map_button.onclick = goto_baidu_map;
    baidu_map_button.appendChild(textnode_Baidu);
    target.appendChild(baidu_map_button);

    //AutoNavi Button
    var autonavi_button = document.createElement("button");
    var textnode_AutoNavi = document.createTextNode("AutoNavi");
    autonavi_button.className += "button";
    autonavi_button.onclick = goto_autonavi_map;
    autonavi_button.appendChild(textnode_AutoNavi);
    target.appendChild(autonavi_button);

    //OSM Button
    var OSM_button = document.createElement("button");
    var textnode_OSM = document.createTextNode("OSM");
    OSM_button.className += "button";
    OSM_button.onclick = goto_OSM;
    OSM_button.appendChild(textnode_OSM);
    target.appendChild(OSM_button);

    //Testing_multimaps Button
    var Multimap_button = document.createElement("button");
    var textnode_Tencent_Baidu = document.createTextNode("Tencent+Baidu");
    Multimap_button.className += "button";
    Multimap_button.onclick = goto_multimap;
    Multimap_button.appendChild(textnode_Tencent_Baidu);
    target.appendChild(Multimap_button);

    //margin
    target.appendChild(document.createElement("br"));
    target.appendChild(document.createElement("br"));

    //Copy portal info to clipboard Button
    var clipboard_button = document.createElement("button");
    var textnode_copy = document.createTextNode("Copy to Clipboard");
    clipboard_button.className += "button";
    clipboard_button.className += " clipbtn";
    clipboard_button.appendChild(textnode_copy);
    target.appendChild(clipboard_button);

    new Clipboard('.clipbtn', {
        text: function(trigger) {
            return get_clipboard();
        }
    });

})();<|endoftext|>


<commit_before>'use strict';

const PIXI = require("pixi.js");
const Renderer = require('incheon').render.Renderer;
let Missile = require('../common/Missile');
let Ship = require('../common/Ship');

const ASSETPATH = {
    ship: "assets/ship1.png",
    missile: "assets/shot.png"
};

/**
 * Renderer for the Spaaace client - based on Pixi.js
 */
class SpaaaceRenderer extends Renderer {

    constructor(gameEngine) {
        super(gameEngine);
        this.sprites = {};
        this.isReady = false;
    }

    init() {
        this.stage = new PIXI.Container();
        this.renderer = PIXI.autoDetectRenderer(this.gameEngine.worldSettings.width, this.gameEngine.worldSettings.height);
        document.addEventListener("DOMContentLoaded", ()=>{ document.body.appendChild(this.renderer.view); });

        PIXI.loader.add([
                ASSETPATH.ship,
                ASSETPATH.missile
            ])
            .load(() => {this.isReady = true;});
    }

    draw() {
        super.draw();
        if (!this.isReady) return; //assets might not have been loaded yet

        for (let objId of Object.keys(this.sprites)) {
            if (this.gameEngine.world.objects[objId]) {
                this.sprites[objId].x = this.gameEngine.world.objects[objId].x;
                this.sprites[objId].y = this.gameEngine.world.objects[objId].y;
                this.sprites[objId].rotation = this.gameEngine.world.objects[objId].angle * Math.PI/180;
            } else {
                this.removeObject(this.sprites[objId]);
            }
        }

        //Render the stage
        this.renderer.render(this.stage);
    }

    addObject(objData, options) {
        let sprite;

        if (objData.class == Ship) {
            sprite = new PIXI.Sprite(PIXI.loader.resources[ASSETPATH.ship].texture);
            this.sprites[objData.id] = sprite;

            // if own player's ship - color it
            if (objData.isPlayerControlled) {
                sprite.tint = 0XFF00FF;
            }

            sprite.anchor.set(0.5, 0.5);
            sprite.width = 50;
            sprite.height = 45;

        } else if (objData.class == Missile) {
            sprite = new PIXI.Sprite(PIXI.loader.resources[ASSETPATH.missile].texture);
            this.sprites[objData.id] = sprite;

            sprite.width = 81 * 0.5;
            sprite.height = 46 * 0.5;

            sprite.anchor.set(0.5, 0.5);
        }

        sprite.position.set(objData.x, objData.y);
        this.stage.addChild(sprite);

        Object.assign(sprite, options);

        return sprite;
    }

    removeObject(obj) {
        obj.destroy();
        delete this.sprites[obj.id];
    }

}

module.exports = SpaaaceRenderer;
<commit_msg>renderer: init now returns a promise that resolves on asset load<commit_after>'use strict';

const PIXI = require("pixi.js");
const Renderer = require('incheon').render.Renderer;
let Missile = require('../common/Missile');
let Ship = require('../common/Ship');

const ASSETPATH = {
    ship: "assets/ship1.png",
    missile: "assets/shot.png"
};

/**
 * Renderer for the Spaaace client - based on Pixi.js
 */
class SpaaaceRenderer extends Renderer {

    constructor(gameEngine) {
        super(gameEngine);
        this.sprites = {};
        this.isReady = false;
    }

    init() {
        this.stage = new PIXI.Container();
        this.renderer = PIXI.autoDetectRenderer(this.gameEngine.worldSettings.width, this.gameEngine.worldSettings.height);
        document.addEventListener("DOMContentLoaded", ()=>{ document.body.appendChild(this.renderer.view); });

        return new Promise((resolve,reject)=>{
            PIXI.loader.add([
                ASSETPATH.ship,
                ASSETPATH.missile
            ])
            .load(() => {
                this.isReady = true;
                resolve();
            });
        });
    }

    draw() {
        super.draw();
        if (!this.isReady) return; //assets might not have been loaded yet

        for (let objId of Object.keys(this.sprites)) {
            if (this.gameEngine.world.objects[objId]) {
                this.sprites[objId].x = this.gameEngine.world.objects[objId].x;
                this.sprites[objId].y = this.gameEngine.world.objects[objId].y;
                this.sprites[objId].rotation = this.gameEngine.world.objects[objId].angle * Math.PI/180;
            } else {
                this.removeObject(this.sprites[objId]);
            }
        }

        //Render the stage
        this.renderer.render(this.stage);
    }

    addObject(objData, options) {
        let sprite;

        if (objData.class == Ship) {
            sprite = new PIXI.Sprite(PIXI.loader.resources[ASSETPATH.ship].texture);
            this.sprites[objData.id] = sprite;

            // if own player's ship - color it
            if (objData.isPlayerControlled) {
                sprite.tint = 0XFF00FF;
            }

            sprite.anchor.set(0.5, 0.5);
            sprite.width = 50;
            sprite.height = 45;

        } else if (objData.class == Missile) {
            sprite = new PIXI.Sprite(PIXI.loader.resources[ASSETPATH.missile].texture);
            this.sprites[objData.id] = sprite;

            sprite.width = 81 * 0.5;
            sprite.height = 46 * 0.5;

            sprite.anchor.set(0.5, 0.5);
        }

        sprite.position.set(objData.x, objData.y);
        this.stage.addChild(sprite);

        Object.assign(sprite, options);

        return sprite;
    }

    removeObject(obj) {
        obj.destroy();
        delete this.sprites[obj.id];
    }

}

module.exports = SpaaaceRenderer;
<|endoftext|>


<commit_before>'use strict';

import Parse   from 'parse';
import encode  from 'parse/lib/browser/encode.js';

/**
 * Provides overrides necessary to integrate Parse serialization / deserialization with Backbone.
 */
export default class BackboneParseObject extends Parse.Object
{
   /**
    * Provides a constructor for clone.
    *
    * @param {string | object}   className - class name of object that has className field.
    * @param {object}            attributes - attributes hash.
    * @param {object}            options - Optional parameters
    */
   constructor(className, attributes, options)
   {
      super(className, attributes, options);
   }

   /**
    * Creates a new BackboneParseObject with identical attributes to this one.
    * @method clone
    * @return {BackboneParseObject}
    */
   clone()
   {
      const clone = new this.constructor();

      if (!clone.className) { clone.className = this.className; }
      if (clone.set) { clone.set(this.attributes); }

      return clone;
   }

   /**
    * Defers to Parse.Object _getSaveJSON for serializing and then filters all results assigning associated
    * `parseObject` entries which indicate a serialized Backbone.Model (ParseModel) to the entry itself which is
    * what Parse expects to receive.
    *
    * @returns {*}
    * @private
    */
   _getSaveJSON()
   {
      const json = super._getSaveJSON();

      for (const attr in json)
      {
         if (typeof json[attr].parseObject === 'object')
         {
            json[attr] = json[attr].parseObject;
         }
      }

      return json;
   }

   /**
    * Returns a JSON version of the object suitable for saving to Parse.
    *
    * There is a subtle difference in this version which is if `attrs[attr].parseObject === 'object'` then the
    * associated `parseObject` is encoded directly.
    *
    * @param {*}  seen - Provides an array of previously seen entries.
    * @return {Object}
    * @override
    * */
   toJSON(seen)
   {
      const seenEntry = this.id ? `${this.className}:${this.id}` : this;
      seen = seen || [seenEntry];

      const json = {};
      const attrs = this.attributes;

      for (const attr in attrs)
      {
         if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON)
         {
            json[attr] = attrs[attr].toJSON();
         }
         else if (typeof attrs[attr].parseObject === 'object')
         {
            json[attr] = encode(attrs[attr].parseObject, false, false, seen);
         }
         else
         {
            json[attr] = encode(attrs[attr], false, false, seen);
         }
      }
      const pending = this._getPendingOps();
      for (const attr in pending[0])
      {
         json[attr] = pending[0][attr].toJSON();
      }

      if (this.id)
      {
         json.objectId = this.id;
      }
      return json;
   }
}<commit_msg>cosmetic docs fix<commit_after>'use strict';

import Parse   from 'parse';
import encode  from 'parse/lib/browser/encode.js';

/**
 * Provides overrides necessary to integrate Parse serialization / deserialization with Backbone.
 */
export default class BackboneParseObject extends Parse.Object
{
   /**
    * Provides a constructor for clone.
    *
    * @param {string | object}   className - class name of object that has className field.
    * @param {object}            attributes - attributes hash.
    * @param {object}            options - Optional parameters
    */
   constructor(className, attributes, options)
   {
      super(className, attributes, options);
   }

   /**
    * Creates a new BackboneParseObject with identical attributes to this one.
    * @method clone
    * @return {BackboneParseObject}
    */
   clone()
   {
      const clone = new this.constructor();

      if (!clone.className) { clone.className = this.className; }
      if (clone.set) { clone.set(this.attributes); }

      return clone;
   }

   /**
    * Defers to Parse.Object _getSaveJSON for serializing and then filters all results assigning associated
    * `parseObject` entries which indicate a serialized Backbone.Model (ParseModel) to the entry itself which is
    * what Parse expects to receive.
    *
    * @returns {*}
    * @private
    */
   _getSaveJSON()
   {
      const json = super._getSaveJSON();

      for (const attr in json)
      {
         if (typeof json[attr].parseObject === 'object')
         {
            json[attr] = json[attr].parseObject;
         }
      }

      return json;
   }

   /**
    * Returns a JSON version of the object suitable for saving to Parse.
    *
    * There is a subtle difference in this version which is if `attrs[attr].parseObject === 'object'` then the
    * associated `parseObject` is encoded directly.
    *
    * @param {*}  seen - Provides an array of previously seen entries.
    * @return {Object}
    * @override
    */
   toJSON(seen)
   {
      const seenEntry = this.id ? `${this.className}:${this.id}` : this;
      seen = seen || [seenEntry];

      const json = {};
      const attrs = this.attributes;

      for (const attr in attrs)
      {
         if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON)
         {
            json[attr] = attrs[attr].toJSON();
         }
         else if (typeof attrs[attr].parseObject === 'object')
         {
            json[attr] = encode(attrs[attr].parseObject, false, false, seen);
         }
         else
         {
            json[attr] = encode(attrs[attr], false, false, seen);
         }
      }
      const pending = this._getPendingOps();
      for (const attr in pending[0])
      {
         json[attr] = pending[0][attr].toJSON();
      }

      if (this.id)
      {
         json.objectId = this.id;
      }
      return json;
   }
}<|endoftext|>


<commit_before>/*global require*/
/*jslint white:true*/
require(['require-config'], function() {
    'use strict';
    require(['app/main'], function (main) {
        main.start()
            .catch(function (err) {
                document.getElementById('root').innerHTML = 'Error starting KBase UI. Please consult the browser error log.';
                console.error('app is unhappy :(');
                console.errro(err);
            });
    });
});<commit_msg>fix typo<commit_after>/*global require*/
/*jslint white:true*/
require(['require-config'], function() {
    'use strict';
    require(['app/main'], function (main) {
        main.start()
            .catch(function (err) {
                document.getElementById('root').innerHTML = 'Error starting KBase UI. Please consult the browser error log.';
                console.error('app is unhappy :(');
                console.error(err);
            });
    });
});<|endoftext|>


<commit_before>'use strict';

function Followers(nodecg) {
  this.nodecg = nodecg;
  this.twitch = nodecg.extensions['lfg-twitchapi'];
  this.request = require('request');
  this.latestFollower = nodecg.Replicant('latestFollower', { defaultValue: null, persistent: true });
  this.username = nodecg.bundleConfig.username;
  this.pollInterval = nodecg.bundleConfig.pollInterval * 1000;

  this._scheduleFollowers();
}

Followers.prototype._scheduleFollowers = function() {
  this.nodecg.log.debug('Polling for TwitchTV Followers.');
  this.request('https://api.twitch.tv/kraken/channels/' + this.username + '/follows?limit=5',
    (err, response, body) => {
      if (err) {
        this.nodecg.log.error(err);
        setTimeout(() => { this._scheduleFollowers(); }, this.pollInterval);
        return;
      }
      if (response.statusCode != 200) {
        this.nodecg.log.error('Unknown response code: ' + response.statusCode);
        setTimeout(() => { this._scheduleFollowers(); }, this.pollInterval);
        return;
      }

      var lastFollowerTs = 0;
      if (this.latestFollower.value) {
        lastFollowerTs = Date.parse(this.latestFollower.value.created_at);
      }

      body = JSON.parse(body);
      if (body.follows.length > 0) {
        this.nodecg.log.debug('Discovered ' + body.follows.length + ' followers.');
        this.latestFollower.value = body.follows[0];

        body.follows.reverse().map((follower) => {
          var parsedTs = Date.parse(follower.created_at);
          if (parsedTs > lastFollowerTs) {
            this.nodecg.sendMessage('follower', follower);
          }
        });
      }

      setTimeout(() => { this._scheduleFollowers(); }, this.pollInterval);
    }
  );
};

module.exports = function(api) {
  return new Followers(api);
};
<commit_msg>Add some resilience and cleanup<commit_after>'use strict';

function Followers(nodecg) {
  this.nodecg = nodecg;
  this.request = require('request');
  this.latestFollower = nodecg.Replicant('latestFollower', { defaultValue: null, persistent: true });
  this.username = nodecg.bundleConfig.username;
  this.pollInterval = nodecg.bundleConfig.pollInterval * 1000;

  this._scheduleFollowers();
}

Followers.prototype._scheduleFollowers = function() {
  this.nodecg.log.debug('Polling for TwitchTV Followers.');
  this.request('https://api.twitch.tv/kraken/channels/' + this.username + '/follows?limit=50',
    (err, response, body) => {
      if (err) {
        this.nodecg.log.error(err);
        setTimeout(() => { this._scheduleFollowers(); }, this.pollInterval);
        return;
      }
      if (response.statusCode != 200) {
        this.nodecg.log.error('Unknown response code: ' + response.statusCode);
        setTimeout(() => { this._scheduleFollowers(); }, this.pollInterval);
        return;
      }

      var lastFollowerTs = 0;
      if (this.latestFollower.value) {
        lastFollowerTs = Date.parse(this.latestFollower.value.created_at);
      }

      try {
        body = JSON.parse(body);
      } catch (error) {
        this.nodecg.log.error(error);
        return;
      }

      if (body.follows.length > 0) {
        this.nodecg.log.debug('Discovered ' + body.follows.length + ' followers.');
        this.latestFollower.value = body.follows[0];

        body.follows.reverse().map((follower) => {
          var parsedTs = Date.parse(follower.created_at);
          if (parsedTs > lastFollowerTs) {
            this.nodecg.sendMessage('follower', follower);
          }
        });
      }

      setTimeout(() => { this._scheduleFollowers(); }, this.pollInterval);
    }
  );
};

module.exports = function(api) {
  return new Followers(api);
};
<|endoftext|>


<commit_before>/*
Name:           fibonacci
Description:    This function calculates fibonacci numbers for one or endless iterations.
Author:         Franklin van de Meent (https://frankl.in)
Source & docs:  https://github.com/fvdm/nodejs-fibonacci
Contact:        https://github.com/fvdm/nodejs-fibonacci/issues
License:        Unlicense / Public Domain (see UNLICENSE FILE)
                <https://github.com/fvdm/nodejs-fibonacci/raw/master/UNLICENSE>
*/

var bignum = require ('bn.js');
var EventEmitter = require ('events') .EventEmitter;

module.exports = new EventEmitter ();
module.exports.doWhile = false;

/**
 * Start iteration
 *
 * @param limit {number=0} - Run fibonacci iterations
 * @returns {object} - result or limitResult
 */

function startIteration (limit) {
  var next = new bignum (1);
  var cur = new bignum (-1);
  var last = new bignum (0);
  var loop = new bignum (0);
  var start = new Date () .getTime ();
  var result = {};
  var limitResult = {};

  limit = new bignum (limit);
  module.exports.doWhile = true;

  while (module.exports.doWhile) {
    // prev cur -> now last
    // prev next -> now cur
    last = cur;
    cur = next;
    next = cur.add (last);

    result.number = next.toString ();
    result.length = next.toString () .length;
    result.iterations = loop.toString ();
    result.ms = new Date () .getTime () - start;

    module.exports.emit ('result', result);

    // found the one
    if (loop.eq (limit)) {
      module.exports.doWhile = false;
      module.exports.emit ('done', result);
      return result;
    }

    // catch infinity
    if (next === 'Infinity') {
      limitResult = {
        reason: 'infinity',
        max_limit: Number.MAX_LIMIT.toString (),
        last_result: result,
        iterations: loop.toString (),
        intended: limit ? limit : null
      };

      module.exports.doWhile = false;
      module.exports.emit ('stop', limitResult);
      return limitResult;
    }

    // count
    loop = loop.add (new bignum (1));
  }

  return result;
}

/**
 * Stop iteration
 *
 * @returns {void}
 */

function killIteration () {
  module.exports.doWhile = false;
}

// ready
module.exports.iterate = startIteration;
module.exports.kill = killIteration;
<commit_msg>Fixed broken endless loop<commit_after>/*
Name:           fibonacci
Description:    This function calculates fibonacci numbers for one or endless iterations.
Author:         Franklin van de Meent (https://frankl.in)
Source & docs:  https://github.com/fvdm/nodejs-fibonacci
Contact:        https://github.com/fvdm/nodejs-fibonacci/issues
License:        Unlicense / Public Domain (see UNLICENSE FILE)
                <https://github.com/fvdm/nodejs-fibonacci/raw/master/UNLICENSE>
*/

var bignum = require ('bn.js');
var EventEmitter = require ('events') .EventEmitter;

module.exports = new EventEmitter ();
module.exports.doWhile = false;

/**
 * Start iteration
 *
 * @param limit {number=0} - Run fibonacci iterations
 * @returns {object} - result or limitResult
 */

function startIteration (limit) {
  var next = new bignum (1);
  var cur = new bignum (-1);
  var last = new bignum (0);
  var loop = new bignum (0);
  var start = new Date () .getTime ();
  var result = {};
  var limitResult = {};

  limit = limit && new bignum (limit);
  module.exports.doWhile = true;

  while (module.exports.doWhile) {
    // prev cur -> now last
    // prev next -> now cur
    last = cur;
    cur = next;
    next = cur.add (last);

    result.number = next.toString ();
    result.length = next.toString () .length;
    result.iterations = loop.toString ();
    result.ms = new Date () .getTime () - start;

    module.exports.emit ('result', result);

    // found the one
    if (limit && loop.eq (limit)) {
      module.exports.doWhile = false;
      module.exports.emit ('done', result);
      return result;
    }

    // catch infinity
    if (next === 'Infinity') {
      limitResult = {
        reason: 'infinity',
        max_limit: Number.MAX_LIMIT.toString (),
        last_result: result,
        iterations: loop.toString (),
        intended: limit ? limit : null
      };

      module.exports.doWhile = false;
      module.exports.emit ('stop', limitResult);
      return limitResult;
    }

    // count
    loop = loop.add (new bignum (1));
  }

  return result;
}

/**
 * Stop iteration
 *
 * @returns {void}
 */

function killIteration () {
  module.exports.doWhile = false;
}

// ready
module.exports.iterate = startIteration;
module.exports.kill = killIteration;
<|endoftext|>


<commit_before>const Main = imports.ui.main;
const GLib = imports.gi.GLib;

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;

var settings;

function init() {
    settings = Convenience.getSettings();
}

var fromCodePoint = function() {
    var MAX_SIZE = 0x4000;
    var codeUnits = [];
    var highSurrogate;
    var lowSurrogate;
    var index = -1;
    var length = arguments.length;
    if (!length) { return ''; }
    var result = '';
    while (++index < length) {
        var codePoint = Number(arguments[index]);
        if (
            !isFinite(codePoint) ||       // `NaN`, `+Infinity`, or `-Infinity`
            codePoint < 0 ||              // not a valid Unicode code point
            codePoint > 0x10FFFF ||       // not a valid Unicode code point
            Math.floor(codePoint) != codePoint // not an integer
        ) {
            throw RangeError('Invalid code point: ' + codePoint);
        }
        if (codePoint <= 0xFFFF) { // BMP code point
            codeUnits.push(codePoint);
        } else { // Astral code point; split in surrogate halves
            // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
            codePoint -= 0x10000;
            highSurrogate = (codePoint >> 10) + 0xD800;
            lowSurrogate = (codePoint % 0x400) + 0xDC00;
            codeUnits.push(highSurrogate, lowSurrogate);
        }
        if (index + 1 == length || codeUnits.length > MAX_SIZE) {
            result += String.fromCharCode.apply(null, codeUnits);
            codeUnits.length = 0;
        }
    }
    return result;
};

function overrider(lbl) {
    var t = lbl.get_text();
    var FORMAT = settings.get_string("override-string");
    var desired = FORMAT;
    if (FORMAT.indexOf("%") > -1) {
        var now = GLib.DateTime.new_now_local();
        var utcnow = GLib.DateTime.new_now_utc();

        if (FORMAT.indexOf("%f") > -1) {
            var hour = now.get_hour();
            // convert from 0-23 to 1-12
            if (hour > 12) {
                hour -= 12;
            }
            if (hour == 0) {
                hour = 12;
            }
            var clockFaceCodePoint = 0x1f550 + (hour - 1);
            var minute = now.get_minute();
            if (minute >= 30) {
                clockFaceCodePoint += 12;
            }
            var repl;
            if (String.fromCodePoint) {
                repl = String.fromCodePoint(clockFaceCodePoint)
            } else {
                repl = fromCodePoint(clockFaceCodePoint);
            }
            desired = desired.replace(/%f/g, repl);
        }
        if (FORMAT.indexOf("%@") > -1) {
            var beat_time = 0 | (((utcnow.get_hour() + 1) % 24) + utcnow.get_minute() / 60 + utcnow.get_second() / 3600) * 1000 / 24;
            beat_time = ('000' + beat_time).slice(-3);
            desired = desired.replace("%@", beat_time);
        }

        desired = now.format(desired);
    }
    if (t != desired) {
        last = t;
        lbl.set_text(desired);
    }
}

var lbl, signalHandlerID, last = "";

function enable() {
    var sA = Main.panel._statusArea;
    if (!sA) { sA = Main.panel.statusArea; }

    if (!sA || !sA.dateMenu || !sA.dateMenu.actor) {
        print("Looks like Shell has changed where things live again; aborting.");
        return;
    }

    sA.dateMenu.actor.first_child.get_children().forEach(function(w) {
        // assume that the text label is the first StLabel we find.
        // This is dodgy behaviour but there's no reliable way to
        // work out which it is.
        if (w.get_text && !lbl) { lbl = w; }
    });
    if (!lbl) {
        print("Looks like Shell has changed where things live again; aborting.");
        return;
    }
    signalHandlerID = lbl.connect("notify::text", overrider);
    last = lbl.get_text();
    overrider(lbl);
}

function disable() {
    if (lbl && signalHandlerID) {
        lbl.disconnect(signalHandlerID);
        lbl.set_text(last);
    }
}
<commit_msg>Add format: quarters as vulgar fractions<commit_after>const Main = imports.ui.main;
const GLib = imports.gi.GLib;

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;

var settings;

function init() {
    settings = Convenience.getSettings();
}

var fromCodePoint = function() {
    var MAX_SIZE = 0x4000;
    var codeUnits = [];
    var highSurrogate;
    var lowSurrogate;
    var index = -1;
    var length = arguments.length;
    if (!length) { return ''; }
    var result = '';
    while (++index < length) {
        var codePoint = Number(arguments[index]);
        if (
            !isFinite(codePoint) ||       // `NaN`, `+Infinity`, or `-Infinity`
            codePoint < 0 ||              // not a valid Unicode code point
            codePoint > 0x10FFFF ||       // not a valid Unicode code point
            Math.floor(codePoint) != codePoint // not an integer
        ) {
            throw RangeError('Invalid code point: ' + codePoint);
        }
        if (codePoint <= 0xFFFF) { // BMP code point
            codeUnits.push(codePoint);
        } else { // Astral code point; split in surrogate halves
            // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
            codePoint -= 0x10000;
            highSurrogate = (codePoint >> 10) + 0xD800;
            lowSurrogate = (codePoint % 0x400) + 0xDC00;
            codeUnits.push(highSurrogate, lowSurrogate);
        }
        if (index + 1 == length || codeUnits.length > MAX_SIZE) {
            result += String.fromCharCode.apply(null, codeUnits);
            codeUnits.length = 0;
        }
    }
    return result;
};

function overrider(lbl) {
    var t = lbl.get_text();
    var FORMAT = settings.get_string("override-string");
    var desired = FORMAT;
    if (FORMAT.indexOf("%") > -1) {
        var now = GLib.DateTime.new_now_local();
        var utcnow = GLib.DateTime.new_now_utc();

        if (FORMAT.indexOf("%vf") > -1) {
            var quarters = Math.round(now.get_minute() / 15);
            var vulgar_fraction = ["\u00B9/\u2081", "\u00B9/\u2084", "\u00B9/\u2082", "\u00B3/\u2084"][quarters];
            desired = desired.replace("%vf", vulgar_fraction);
        }
        if (FORMAT.indexOf("%f") > -1) {
            var hour = now.get_hour();
            // convert from 0-23 to 1-12
            if (hour > 12) {
                hour -= 12;
            }
            if (hour == 0) {
                hour = 12;
            }
            var clockFaceCodePoint = 0x1f550 + (hour - 1);
            var minute = now.get_minute();
            if (minute >= 30) {
                clockFaceCodePoint += 12;
            }
            var repl;
            if (String.fromCodePoint) {
                repl = String.fromCodePoint(clockFaceCodePoint)
            } else {
                repl = fromCodePoint(clockFaceCodePoint);
            }
            desired = desired.replace(/%f/g, repl);
        }
        if (FORMAT.indexOf("%@") > -1) {
            var beat_time = 0 | (((utcnow.get_hour() + 1) % 24) + utcnow.get_minute() / 60 + utcnow.get_second() / 3600) * 1000 / 24;
            beat_time = ('000' + beat_time).slice(-3);
            desired = desired.replace("%@", beat_time);
        }

        desired = now.format(desired);
    }
    if (t != desired) {
        last = t;
        lbl.set_text(desired);
    }
}

var lbl, signalHandlerID, last = "";

function enable() {
    var sA = Main.panel._statusArea;
    if (!sA) { sA = Main.panel.statusArea; }

    if (!sA || !sA.dateMenu || !sA.dateMenu.actor) {
        print("Looks like Shell has changed where things live again; aborting.");
        return;
    }

    sA.dateMenu.actor.first_child.get_children().forEach(function(w) {
        // assume that the text label is the first StLabel we find.
        // This is dodgy behaviour but there's no reliable way to
        // work out which it is.
        if (w.get_text && !lbl) { lbl = w; }
    });
    if (!lbl) {
        print("Looks like Shell has changed where things live again; aborting.");
        return;
    }
    signalHandlerID = lbl.connect("notify::text", overrider);
    last = lbl.get_text();
    overrider(lbl);
}

function disable() {
    if (lbl && signalHandlerID) {
        lbl.disconnect(signalHandlerID);
        lbl.set_text(last);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright © 2012–2013 Will Thompson <will@willthompson.co.uk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

const Lang = imports.lang;
const Main = imports.ui.main;

const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Tp = imports.gi.TelepathyGLib;
const St = imports.gi.St;

const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ExtensionUtils = imports.misc.extensionUtils;

const debugEnabled = true;

function debug() {
    if (debugEnabled) {
        global.log(Array.prototype.join.call(arguments, ' '));
    }
}

function AccountItem() {
    this._init.apply(this, arguments);
}

AccountItem.prototype = {
    __proto__: PopupMenu.PopupMenuItem.prototype,

    _init: function(account) {
        PopupMenu.PopupMenuItem.prototype._init.call(this,
            account.get_display_name());

        this.label.add_style_class_name('popup-inactive-menu-item');
        this.actor.reactive = false;
        this.actor.can_focus = false;

        this._presenceBin = new St.Bin({ x_align: St.Align.END });
        this.addActor(this._presenceBin,
                      { expand: true, span: -1, align: St.Align.END });

        this._presenceLabel = new St.Label({ text: '\u26f7',
                                             style_class: 'popup-inactive-menu-item'
                                           });
        this._presenceBin.child = this._presenceLabel;

        account.connect('presence-changed', Lang.bind(this,
            this._onPresenceChanged));
        let [presence, status, message] = account.get_current_presence();
        this._onPresenceChanged(account, presence, status, message);
    },

    _onPresenceChanged: function(account, presence, status, message) {
        debug("_onPresenceChanged for", account.get_path_suffix(), presence, status);

        if (status == "") {
            switch (presence) {
                case Tp.ConnectionPresenceType.UNSET:
                    /* Idle doesn't support SimplePresence. When accounts are
                     * online but don't support SimplePresence, this property comes
                     * out as Unset. This is documented in the spec (but not in
                     * the tp-glib docs).
                     */
                    status = "online";
                    break;

                /* In all other cases, status should be a non-empty string. But
                 * just in case…
                 */
                case Tp.ConnectionPresenceType.OFFLINE:
                    status = "offline";
                    break;

                default:
                    status = "something strange (" + presence + ")";
                    break;
            }
        }

        this._presenceLabel.text = status;
    },
};

function AccountGroupSection() {
    this._init.apply(this, arguments);
}

AccountGroupSection.prototype = {
    __proto__: PopupMenu.PopupMenuSection.prototype,

    _init: function(am, groupName, accountIDs) {
        PopupMenu.PopupMenuSection.prototype._init.call(this);

        this._am = am;

        this._switch = new PopupMenu.PopupSwitchMenuItem(groupName, true);
        this._switch.setStatus("loading\u2026");
        this.addMenuItem(this._switch);

        this._accountIDs = accountIDs;
        this._accounts = [];
    },

    helloThere: function(accounts) {
        let state = false;

        for (let i = 0; i < accounts.length; i++) {
            let account = accounts[i];
            if (this._accountIDs.indexOf(account.get_path_suffix()) >= 0) {
                this._accounts.push(account);
                /* If any account in this group is enabled, show the whole
                 * group as enabled.
                 */
                state = state || account.is_enabled();

                /* Only list accounts if there's more than one */
                if (this._accountIDs.length > 1) {
                    let nameItem = new AccountItem(account);
                    this.addMenuItem(nameItem);
                }
            }
        }

        if (this._accounts.length > 0) {
            this._switch.setToggleState(state);
            this._switch.setStatus(null);

            this._switch.connect('toggled', Lang.bind(this,
                function(item) { this._onToggled(item); }));
        } else {
            this._switch.setStatus("\u2639");
        }
    },

    _onToggled: function(item) {
        let state = item.state;

        for (let i = 0; i < this._accounts.length; i++) {
            let account = this._accounts[i];
            account.set_enabled_async(state, Lang.bind(this, function() {
                if (state) {
                    let [presence, status, msg] = this._am.get_most_available_presence();

                    account.request_presence_async(presence, status, msg,
                        function() {});
                }
            }));
        }

    },
}

function CAGMenu() {
    this._init();
}

CAGMenu.prototype = {
    __proto__: PanelMenu.SystemStatusButton.prototype,

    _init: function() {
        PanelMenu.SystemStatusButton.prototype._init.call(this, 'avatar-default-symbolic', null);

        this._sections = [];

        this._accountFilePath = GLib.build_filenamev([
            GLib.get_user_config_dir(),
            "shell-chat-account-groups",
            "groups.2.json"]);
        this._accountFile = Gio.File.new_for_path(this._accountFilePath);
        this._accountFileMonitor = this._accountFile.monitor_file(
            Gio.FileMonitorFlags.NONE, null);
        this._accountFileMonitorChangedId = this._accountFileMonitor.connect(
            'changed', Lang.bind(this, this._groupsEdited));

        this._am = Tp.AccountManager.dup();
        this._amReady = false;

        this._loadConfig();
        this._prepare();
    },

    destroy: function() {
        this._accountFileMonitor.disconnect(this._accountFileMonitorChangedId);
        PanelMenu.SystemStatusButton.prototype.destroy.call(this);
    },

    _loadConfig: function() {
        this.menu.removeAll();
        this._sections = [];

        try {
            /* Stupid. The first returned value is true. */
            let ret = GLib.file_get_contents(this._accountFilePath);
            let groups = JSON.parse(ret[1]);
            this._createSections(groups);
        } catch (error) {
            global.log("no configured groups. falling back to one per account");
        }
    },

    _createSections: function(groups) {
        for (let i = 0; i < groups.length; i++) {
            let group = groups[i];
            let groupName = group.name;
            let accounts = group.accounts;
            let section = new AccountGroupSection(this._am, groupName, accounts);
            this.menu.addMenuItem(section);
            this._sections.push(section);
        }
    },

    _prepare: function() {
        this._am.prepare_async(null, Lang.bind(this,
            function(am) {
                this._amReady = true;
                this._pushAccountsIntoSections();
            }));
    },

    _pushAccountsIntoSections: function() {
        let accounts = this._am.get_valid_accounts();

        if (this._sections.length > 0) {
            for (let i = 0; i < this._sections.length; i++) {
                this._sections[i].helloThere(accounts);
            }
        } else {
            const NUM_ACCOUNTS_BEFORE_OVERFLOWING = 10;
            let overflowItem = null;

            for (let i = 0; i < accounts.length; i++) {
                let account = accounts[i];
                let section = new AccountGroupSection(this._am, account.get_display_name(), [account.get_path_suffix()]);
                section.helloThere(accounts);

                if (i < NUM_ACCOUNTS_BEFORE_OVERFLOWING) {
                    this.menu.addMenuItem(section);
                } else {
                    if (!overflowItem) {
                        overflowItem = new PopupMenu.PopupSubMenuMenuItem("More...");
                        this.menu.addMenuItem(overflowItem);
                    }

                    overflowItem.menu.addMenuItem(section);
                }
            }
        }

        let separator = new PopupMenu.PopupSeparatorMenuItem();
        this.menu.addMenuItem(separator);

        this.menu.addAction("Chat Account Group Settings",
            Lang.bind(this, this._launchSettings));
    },

    _launchSettings: function(event) {
        let meta = ExtensionUtils.getCurrentExtension();
        let path = meta.path + "/edit-groups";

        GLib.spawn_async(null, ["python", path], null, GLib.SpawnFlags.SEARCH_PATH, null, null, null, null);
    },

    _groupsEdited: function(monitor, file, other_file, event_type) {
        if (event_type == Gio.FileMonitorEvent.CHANGES_DONE_HINT) {
            global.log("groups edited!");
            this._loadConfig();
            if (this._amReady) {
                this._pushAccountsIntoSections();
            }
        }
    },
};

function init(metadata) {
}

let groupsMenu;

function enable() {
    groupsMenu = new CAGMenu;
    Main.panel.addToStatusArea('chat-account-groups-menu', groupsMenu);
}

function disable() {
    groupsMenu.destroy();
}

/* vim: set fileencoding=utf-8 sts=4 sw=4 et : */
<commit_msg>extension: use debug() throughout<commit_after>/*
 * Copyright © 2012–2013 Will Thompson <will@willthompson.co.uk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

const Lang = imports.lang;
const Main = imports.ui.main;

const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Tp = imports.gi.TelepathyGLib;
const St = imports.gi.St;

const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ExtensionUtils = imports.misc.extensionUtils;

const debugEnabled = true;

function debug() {
    if (debugEnabled) {
        global.log(Array.prototype.join.call(arguments, ' '));
    }
}

function AccountItem() {
    this._init.apply(this, arguments);
}

AccountItem.prototype = {
    __proto__: PopupMenu.PopupMenuItem.prototype,

    _init: function(account) {
        PopupMenu.PopupMenuItem.prototype._init.call(this,
            account.get_display_name());

        this.label.add_style_class_name('popup-inactive-menu-item');
        this.actor.reactive = false;
        this.actor.can_focus = false;

        this._presenceBin = new St.Bin({ x_align: St.Align.END });
        this.addActor(this._presenceBin,
                      { expand: true, span: -1, align: St.Align.END });

        this._presenceLabel = new St.Label({ text: '\u26f7',
                                             style_class: 'popup-inactive-menu-item'
                                           });
        this._presenceBin.child = this._presenceLabel;

        account.connect('presence-changed', Lang.bind(this,
            this._onPresenceChanged));
        let [presence, status, message] = account.get_current_presence();
        this._onPresenceChanged(account, presence, status, message);
    },

    _onPresenceChanged: function(account, presence, status, message) {
        debug("_onPresenceChanged for", account.get_path_suffix(), presence, status);

        if (status == "") {
            switch (presence) {
                case Tp.ConnectionPresenceType.UNSET:
                    /* Idle doesn't support SimplePresence. When accounts are
                     * online but don't support SimplePresence, this property comes
                     * out as Unset. This is documented in the spec (but not in
                     * the tp-glib docs).
                     */
                    status = "online";
                    break;

                /* In all other cases, status should be a non-empty string. But
                 * just in case…
                 */
                case Tp.ConnectionPresenceType.OFFLINE:
                    status = "offline";
                    break;

                default:
                    status = "something strange (" + presence + ")";
                    break;
            }
        }

        this._presenceLabel.text = status;
    },
};

function AccountGroupSection() {
    this._init.apply(this, arguments);
}

AccountGroupSection.prototype = {
    __proto__: PopupMenu.PopupMenuSection.prototype,

    _init: function(am, groupName, accountIDs) {
        PopupMenu.PopupMenuSection.prototype._init.call(this);

        this._am = am;

        this._switch = new PopupMenu.PopupSwitchMenuItem(groupName, true);
        this._switch.setStatus("loading\u2026");
        this.addMenuItem(this._switch);

        this._accountIDs = accountIDs;
        this._accounts = [];
    },

    helloThere: function(accounts) {
        let state = false;

        for (let i = 0; i < accounts.length; i++) {
            let account = accounts[i];
            if (this._accountIDs.indexOf(account.get_path_suffix()) >= 0) {
                this._accounts.push(account);
                /* If any account in this group is enabled, show the whole
                 * group as enabled.
                 */
                state = state || account.is_enabled();

                /* Only list accounts if there's more than one */
                if (this._accountIDs.length > 1) {
                    let nameItem = new AccountItem(account);
                    this.addMenuItem(nameItem);
                }
            }
        }

        if (this._accounts.length > 0) {
            this._switch.setToggleState(state);
            this._switch.setStatus(null);

            this._switch.connect('toggled', Lang.bind(this,
                function(item) { this._onToggled(item); }));
        } else {
            this._switch.setStatus("\u2639");
        }
    },

    _onToggled: function(item) {
        let state = item.state;

        for (let i = 0; i < this._accounts.length; i++) {
            let account = this._accounts[i];
            account.set_enabled_async(state, Lang.bind(this, function() {
                if (state) {
                    let [presence, status, msg] = this._am.get_most_available_presence();

                    account.request_presence_async(presence, status, msg,
                        function() {});
                }
            }));
        }

    },
}

function CAGMenu() {
    this._init();
}

CAGMenu.prototype = {
    __proto__: PanelMenu.SystemStatusButton.prototype,

    _init: function() {
        PanelMenu.SystemStatusButton.prototype._init.call(this, 'avatar-default-symbolic', null);

        this._sections = [];

        this._accountFilePath = GLib.build_filenamev([
            GLib.get_user_config_dir(),
            "shell-chat-account-groups",
            "groups.2.json"]);
        this._accountFile = Gio.File.new_for_path(this._accountFilePath);
        this._accountFileMonitor = this._accountFile.monitor_file(
            Gio.FileMonitorFlags.NONE, null);
        this._accountFileMonitorChangedId = this._accountFileMonitor.connect(
            'changed', Lang.bind(this, this._groupsEdited));

        this._am = Tp.AccountManager.dup();
        this._amReady = false;

        this._loadConfig();
        this._prepare();
    },

    destroy: function() {
        this._accountFileMonitor.disconnect(this._accountFileMonitorChangedId);
        PanelMenu.SystemStatusButton.prototype.destroy.call(this);
    },

    _loadConfig: function() {
        this.menu.removeAll();
        this._sections = [];

        try {
            /* Stupid. The first returned value is true. */
            let ret = GLib.file_get_contents(this._accountFilePath);
            let groups = JSON.parse(ret[1]);
            this._createSections(groups);
        } catch (error) {
            debug("no configured groups. falling back to one per account");
        }
    },

    _createSections: function(groups) {
        for (let i = 0; i < groups.length; i++) {
            let group = groups[i];
            let groupName = group.name;
            let accounts = group.accounts;
            let section = new AccountGroupSection(this._am, groupName, accounts);
            this.menu.addMenuItem(section);
            this._sections.push(section);
        }
    },

    _prepare: function() {
        this._am.prepare_async(null, Lang.bind(this,
            function(am) {
                this._amReady = true;
                this._pushAccountsIntoSections();
            }));
    },

    _pushAccountsIntoSections: function() {
        let accounts = this._am.get_valid_accounts();

        if (this._sections.length > 0) {
            for (let i = 0; i < this._sections.length; i++) {
                this._sections[i].helloThere(accounts);
            }
        } else {
            const NUM_ACCOUNTS_BEFORE_OVERFLOWING = 10;
            let overflowItem = null;

            for (let i = 0; i < accounts.length; i++) {
                let account = accounts[i];
                let section = new AccountGroupSection(this._am, account.get_display_name(), [account.get_path_suffix()]);
                section.helloThere(accounts);

                if (i < NUM_ACCOUNTS_BEFORE_OVERFLOWING) {
                    this.menu.addMenuItem(section);
                } else {
                    if (!overflowItem) {
                        overflowItem = new PopupMenu.PopupSubMenuMenuItem("More...");
                        this.menu.addMenuItem(overflowItem);
                    }

                    overflowItem.menu.addMenuItem(section);
                }
            }
        }

        let separator = new PopupMenu.PopupSeparatorMenuItem();
        this.menu.addMenuItem(separator);

        this.menu.addAction("Chat Account Group Settings",
            Lang.bind(this, this._launchSettings));
    },

    _launchSettings: function(event) {
        let meta = ExtensionUtils.getCurrentExtension();
        let path = meta.path + "/edit-groups";

        GLib.spawn_async(null, ["python", path], null, GLib.SpawnFlags.SEARCH_PATH, null, null, null, null);
    },

    _groupsEdited: function(monitor, file, other_file, event_type) {
        if (event_type == Gio.FileMonitorEvent.CHANGES_DONE_HINT) {
            debug("groups edited!");
            this._loadConfig();
            if (this._amReady) {
                this._pushAccountsIntoSections();
            }
        }
    },
};

function init(metadata) {
}

let groupsMenu;

function enable() {
    groupsMenu = new CAGMenu;
    Main.panel.addToStatusArea('chat-account-groups-menu', groupsMenu);
}

function disable() {
    groupsMenu.destroy();
}

/* vim: set fileencoding=utf-8 sts=4 sw=4 et : */
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');

var vscode = require('vscode');
var axios = require('axios');
var errorEx = require('error-ex');

function activate(context) {
    console.log('Extension "gi" is now active!');

    var disposable = vscode.commands.registerCommand('extension.gi', function () {
        var giURL = 'https://www.gitignore.io/api/';
        var giError = new errorEx('giError');

        axios.get(giURL + 'list')
            .then(function (response) {
                var rawList = response.data;
                rawList = rawList.replace(/(\r\n|\n|\r)/gm, ",");
                var formattedList = rawList.split(',');

                const options = {
                    ignoreFocusOut: false,
                    placeHolder: 'Search Operating Systems, IDEs, or Programming Languages',
                };

                vscode.window.showQuickPick(formattedList, options)
                    .then(function (val) {
                        if (val === undefined) {
                            vscode.window.setStatusBarMessage('gi escaped', 3000);
                            var err = new giError('EscapeException');
                            throw err;
                        }
                        vscode.window.setStatusBarMessage('You picked ' + val, 3000);
                        axios.get(giURL + val)
                            .then(function (response) {
                                makeFile(response.data);
                            })
                            .catch(function (err) {
                                console.log(err);
                            });
                    });

                function makeFile(content) {
                    const choices = [{
                        label: 'Overwrite',
                        description: `Overwrite current .gitignore`
                    }, {
                        label: 'Append',
                        description: 'Append to current .gitignore'
                    }];

                    const options = {
                        matchOnDescription: true,
                        placeHolder: "A .gitignore file already exists in your current working directory. What would you like to do?"
                    };

                    var giFile = path.join(vscode.workspace.rootPath, '.gitignore');

                    fs.access(giFile, fs.F_OK, function (err) {
                        if (!err) {
                            console.log('.gitinore already exits');
                            vscode.window.showQuickPick(choices, options)
                                .then(function (val) {
                                    if (!val || val === undefined) {
                                        var err = new giError('EscapeException');
                                        vscode.window.setStatusBarMessage('gi escaped', 3000);
                                        throw err;
                                    }
                                    if (val.label === 'Overwrite') {
                                        writeToFile(content, true);
                                        vscode.window.showInformationMessage('.gitignore overwritten');
                                        return;
                                    }
                                    if (val.label === 'Append') {
                                        writeToFile(content, false);
                                        vscode.window.showInformationMessage('.gitignore appended');
                                        return;
                                    }
                                });
                        } else {
                            console.log('.gitinore does not exit');
                            writeToFile(content, true);
                            vscode.window.showInformationMessage('.gitignore created');
                            return;
                        }
                    });

                    function writeToFile(content, flag) {
                        if (flag === true) {
                            fs.writeFileSync(giFile, content, 'utf-8', function (err) {
                                if (err) {
                                    console.log('Failed to write to .gitignore');
                                } else {
                                    console.log('.gitignore created');
                                }
                            });
                        } else {
                            var lines = content.split('\n');
                            lines.splice(0,2);
                            content = lines.join('\n');
                            fs.appendFileSync(giFile, content, 'utf-8', function (err) {
                                if (err) {
                                    console.log('Failed to append to .gitignore');
                                } else {
                                    console.log('.gitignore appended');
                                }
                            });
                        }
                    }
                }
            })
            .catch(function (err) {
                console.log(err);
            });
    });

    context.subscriptions.push(disposable);
}
exports.activate = activate;

function deactivate() { }
exports.deactivate = deactivate;<commit_msg>Fix typo<commit_after>var fs = require('fs');
var path = require('path');

var vscode = require('vscode');
var axios = require('axios');
var errorEx = require('error-ex');

function activate(context) {
    console.log('Extension "gi" is now active!');

    var disposable = vscode.commands.registerCommand('extension.gi', function () {
        var giURL = 'https://www.gitignore.io/api/';
        var giError = new errorEx('giError');

        axios.get(giURL + 'list')
            .then(function (response) {
                var rawList = response.data;
                rawList = rawList.replace(/(\r\n|\n|\r)/gm, ",");
                var formattedList = rawList.split(',');

                const options = {
                    ignoreFocusOut: false,
                    placeHolder: 'Search Operating Systems, IDEs, or Programming Languages',
                };

                vscode.window.showQuickPick(formattedList, options)
                    .then(function (val) {
                        if (val === undefined) {
                            vscode.window.setStatusBarMessage('gi escaped', 3000);
                            var err = new giError('EscapeException');
                            throw err;
                        }
                        vscode.window.setStatusBarMessage('You picked ' + val, 3000);
                        axios.get(giURL + val)
                            .then(function (response) {
                                makeFile(response.data);
                            })
                            .catch(function (err) {
                                console.log(err);
                            });
                    });

                function makeFile(content) {
                    const choices = [{
                        label: 'Overwrite',
                        description: `Overwrite current .gitignore`
                    }, {
                        label: 'Append',
                        description: 'Append to current .gitignore'
                    }];

                    const options = {
                        matchOnDescription: true,
                        placeHolder: "A .gitignore file already exists in your current working directory. What would you like to do?"
                    };

                    var giFile = path.join(vscode.workspace.rootPath, '.gitignore');

                    fs.access(giFile, fs.F_OK, function (err) {
                        if (!err) {
                            console.log('.gitignore already exits');
                            vscode.window.showQuickPick(choices, options)
                                .then(function (val) {
                                    if (!val || val === undefined) {
                                        var err = new giError('EscapeException');
                                        vscode.window.setStatusBarMessage('gi escaped', 3000);
                                        throw err;
                                    }
                                    if (val.label === 'Overwrite') {
                                        writeToFile(content, true);
                                        vscode.window.showInformationMessage('.gitignore overwritten');
                                        return;
                                    }
                                    if (val.label === 'Append') {
                                        writeToFile(content, false);
                                        vscode.window.showInformationMessage('.gitignore appended');
                                        return;
                                    }
                                });
                        } else {
                            console.log('.gitignore does not exit');
                            writeToFile(content, true);
                            vscode.window.showInformationMessage('.gitignore created');
                            return;
                        }
                    });

                    function writeToFile(content, flag) {
                        if (flag === true) {
                            fs.writeFileSync(giFile, content, 'utf-8', function (err) {
                                if (err) {
                                    console.log('Failed to write to .gitignore');
                                } else {
                                    console.log('.gitignore created');
                                }
                            });
                        } else {
                            var lines = content.split('\n');
                            lines.splice(0,2);
                            content = lines.join('\n');
                            fs.appendFileSync(giFile, content, 'utf-8', function (err) {
                                if (err) {
                                    console.log('Failed to append to .gitignore');
                                } else {
                                    console.log('.gitignore appended');
                                }
                            });
                        }
                    }
                }
            })
            .catch(function (err) {
                console.log(err);
            });
    });

    context.subscriptions.push(disposable);
}
exports.activate = activate;

function deactivate() { }
exports.deactivate = deactivate;<|endoftext|>


<commit_before>'use strict';

var nodecg, accessToken, _session;
var request = require('request');
var querystring = require('querystring');
var app = require('express')();
var loginLib = require('../../lib/login');

module.exports = function (extensionApi) {
    nodecg = extensionApi;

    if (!nodecg.config.login.enabled) {
        throw new Error('Login security is not enabled, lfg-twitchapi will not load.');
    }

    if (!nodecg.config.login.twitch.enabled) {
        throw new Error('Twitch authentication is not enabled, lfg-twitchapi will not load.');
    }

    if (!Object.keys(nodecg.bundleConfig).length) {
        throw new Error('No config found in cfg/lfg-twitch.json, aborting!');
    }

    if (!nodecg.bundleConfig.username) {
        throw new Error('"username" key not present in cfg/lfg-twitch.json, aborting!');
    }

    // Non-confidential session details are made available to dashboard & view
    var sessionReplicant = nodecg.Replicant('session', { defaultValue: null, persistent: false });

    // Capture the relevant session data the moment our user logs in
    loginLib.on('login', function(session) {
        var user = session.passport.user;
        _session = session;
        if (user.provider === 'twitch' && user.username === nodecg.bundleConfig.username) {
            // Update the 'session' syncedVar with only the non-confidential information
            sessionReplicant.value = {
                provider: user.provider, // should ALWAYS be 'twitch'
                username: user.username,
                displayName: user.displayName,
                logo: user._json.logo,
                url: user._json._links.self
            };
            accessToken = user.accessToken;
        }
    });

    // If our target user logs out, we can't do anything else until they log back in
    loginLib.on('logout', function(session) {
        var user = session.passport.user;
        if (user.provider === 'twitch' && user.username === nodecg.bundleConfig.username) {
            sessionReplicant.value = null;
            accessToken = null;
            _session = null;
        }
    });

    app.get('/lfg-twitchapi/checkuser', function(req, res){
        if (req.session.passport && req.session.passport.user) {
            var user = req.session.passport.user;
            if (user.username === nodecg.bundleConfig.username) {
                // Update the 'session' syncedVar with only the non-confidential information
                sessionReplicant.value = {
                    provider: user.provider, // should ALWAYS be 'twitch'
                    username: user.username,
                    displayName: user.displayName,
                    logo: user._json.logo,
                    url: user._json._links.self
                };
                accessToken = user.accessToken;
                _session = req.session;
            }
        }
        res.sendStatus(200);
    });

    nodecg.mount(app);

    // Return the function used to make API calls, so other bundles can use it
    return {
        get: function(path, options, callback) {
            apiCall('GET', path, options, callback);
        },
        post: function(path, options, callback) {
            apiCall('POST', path, options, callback);
        },
        put: function(path, options, callback) {
            apiCall('PUT', path, options, callback);
        },
        delete: function(path, options, callback) {
            apiCall('DELETE', path, options, callback);
        },
        destroySession: function() {
            if (_session) {
                _session.destroy(function(err) {
                    if (err) {
                        nodecg.log.error(err.stack);
                    } else {
                        var username = _session.passport.user.displayName;
                        sessionReplicant.value = null;
                        accessToken = null;
                        _session = null;
                        nodecg.sendMessage('destroyed', username);
                    }
                });
            }
        }
    };
};

function apiCall(method, path, options, callback) {
    method = typeof method !== 'undefined' ? method : 'GET';
    path = typeof path === 'string' ? path : '';
    options = typeof options !== 'undefined' ? options : {};
    callback = typeof callback === 'function' ? callback : function () {};

    options = querystring.stringify(options);

    var requestOptions = {
        url: 'https://api.twitch.tv/kraken' + path + (options ? '?' + options : ''),
        headers: {
            'Accept': 'application/vnd.twitchtv.v3+json',
            'Client-ID': nodecg.config.login.twitch.clientID
        },
        method: method
    };

    // If {{username}} is present in the url string, replace it with the username from bundleConfig
    if (requestOptions.url.indexOf('{{username}}') >= 0) {
        // If we don't have an active session, error
        if (!accessToken) {
            return callback(new Error('Access token for "' + nodecg.bundleConfig.username + '" has not been captured yet.' +
                ' Once they sign in, this error will stop.'));
        }
        requestOptions.url = requestOptions.url.replace('{{username}}', nodecg.bundleConfig.username);
    }

    if (accessToken) {
        requestOptions.headers.Authorization = 'OAuth ' + accessToken;
    }

    request(requestOptions, function (error, response, body) {
        if (error) { return callback(error); }

        try { body = JSON.parse(body); }
        catch (error) { return callback(error); }

        body.channel = nodecg.bundleConfig.username;

        return callback(null, response.statusCode, body);
    });
}
<commit_msg>calm down<commit_after>'use strict';

var nodecg, accessToken, _session;
var request = require('request');
var querystring = require('querystring');
var app = require('express')();
var loginLib = require('../../lib/login');

module.exports = function (extensionApi) {
    nodecg = extensionApi;

    if (!nodecg.config.login.enabled) {
        throw new Error('Login security is not enabled, lfg-twitchapi will not load.');
    }

    if (!nodecg.config.login.twitch.enabled) {
        throw new Error('Twitch authentication is not enabled, lfg-twitchapi will not load.');
    }

    if (!Object.keys(nodecg.bundleConfig).length) {
        throw new Error('No config found in cfg/lfg-twitch.json, aborting!');
    }

    if (!nodecg.bundleConfig.username) {
        throw new Error('"username" key not present in cfg/lfg-twitch.json, aborting!');
    }

    // Non-confidential session details are made available to dashboard & view
    var sessionReplicant = nodecg.Replicant('session', { defaultValue: null, persistent: false });

    // Capture the relevant session data the moment our user logs in
    loginLib.on('login', function(session) {
        var user = session.passport.user;
        _session = session;
        if (user.provider === 'twitch' && user.username === nodecg.bundleConfig.username) {
            // Update the 'session' syncedVar with only the non-confidential information
            sessionReplicant.value = {
                provider: user.provider, // should ALWAYS be 'twitch'
                username: user.username,
                displayName: user.displayName,
                logo: user._json.logo,
                url: user._json._links.self
            };
            accessToken = user.accessToken;
        }
    });

    // If our target user logs out, we can't do anything else until they log back in
    loginLib.on('logout', function(session) {
        var user = session.passport.user;
        if (user && user.provider === 'twitch' && user.username === nodecg.bundleConfig.username) {
            sessionReplicant.value = null;
            accessToken = null;
            _session = null;
        }
    });

    app.get('/lfg-twitchapi/checkuser', function(req, res){
        if (req.session.passport && req.session.passport.user) {
            var user = req.session.passport.user;
            if (user.username === nodecg.bundleConfig.username) {
                // Update the 'session' syncedVar with only the non-confidential information
                sessionReplicant.value = {
                    provider: user.provider, // should ALWAYS be 'twitch'
                    username: user.username,
                    displayName: user.displayName,
                    logo: user._json.logo,
                    url: user._json._links.self
                };
                accessToken = user.accessToken;
                _session = req.session;
            }
        }
        res.sendStatus(200);
    });

    nodecg.mount(app);

    // Return the function used to make API calls, so other bundles can use it
    return {
        get: function(path, options, callback) {
            apiCall('GET', path, options, callback);
        },
        post: function(path, options, callback) {
            apiCall('POST', path, options, callback);
        },
        put: function(path, options, callback) {
            apiCall('PUT', path, options, callback);
        },
        delete: function(path, options, callback) {
            apiCall('DELETE', path, options, callback);
        },
        destroySession: function() {
            if (_session) {
                _session.destroy(function(err) {
                    if (err) {
                        nodecg.log.error(err.stack);
                    } else {
                        var username = _session.passport.user.displayName;
                        sessionReplicant.value = null;
                        accessToken = null;
                        _session = null;
                        nodecg.sendMessage('destroyed', username);
                    }
                });
            }
        }
    };
};

function apiCall(method, path, options, callback) {
    method = typeof method !== 'undefined' ? method : 'GET';
    path = typeof path === 'string' ? path : '';
    options = typeof options !== 'undefined' ? options : {};
    callback = typeof callback === 'function' ? callback : function () {};

    options = querystring.stringify(options);

    var requestOptions = {
        url: 'https://api.twitch.tv/kraken' + path + (options ? '?' + options : ''),
        headers: {
            'Accept': 'application/vnd.twitchtv.v3+json',
            'Client-ID': nodecg.config.login.twitch.clientID
        },
        method: method
    };

    // If {{username}} is present in the url string, replace it with the username from bundleConfig
    if (requestOptions.url.indexOf('{{username}}') >= 0) {
        // If we don't have an active session, error
        if (!accessToken) {
            return callback(new Error('Access token for "' + nodecg.bundleConfig.username + '" has not been captured yet.' +
                ' Once they sign in, this error will stop.'));
        }
        requestOptions.url = requestOptions.url.replace('{{username}}', nodecg.bundleConfig.username);
    }

    if (accessToken) {
        requestOptions.headers.Authorization = 'OAuth ' + accessToken;
    }

    request(requestOptions, function (error, response, body) {
        if (error) { return callback(error); }

        try { body = JSON.parse(body); }
        catch (error) { return callback(error); }

        body.channel = nodecg.bundleConfig.username;

        return callback(null, response.statusCode, body);
    });
}
<|endoftext|>


<commit_before>/*
* angular-confirm
* https://github.com/riderx/angular-modal-confirm
* @version v1.0.0 - 2016-01-11
* @license Apache
*/

angular.module('angularModalConfirm', ['angularModalService'])
.value('$confirmModalDefaults', {
  template:
  '
' +
  '
' +
  '
' +
  '
' +
  '<button type="button" class="close" ng-click="cancel()" data-dismiss="modal" aria-hidden="true">×</button>' +
  '
{{data.title}}
' +
  '
' +
  '
' +
  '
{{data.text}}
' +
  '
' +
  '
' +
  '<button type="button" ng-click="ok()" class="btn btn-default" data-dismiss="modal">{{data.ok}}</button>' +
  '<button type="button" ng-click="cancel()" class="btn btn-primary" data-dismiss="modal">{{data.cancel}}</button>' +
  '
' +
  '
' +
  '
' +
  '
',
  controller: 'ConfirmModalController',
  inputs: {
    title: 'Confirm',
    text:'Hey, are you sure ?',
    ok: 'OK',
    cancel: 'Cancel'
  }
})
.factory('$confirmModal', function ($confirmModalDefaults, ModalService, $q) {
  return function (data, settings) {
    var defaults = angular.copy($confirmModalDefaults);
    settings = angular.extend(defaults, (settings || {}));

    data = angular.extend({}, settings.labels, data || {});

    if ('templateUrl' in settings && 'template' in settings) {
      delete settings.template;
    }

    settings.resolve = {
      data: function () {
        return data;
      }
    };

    var deferred = $q.defer();
    ModalService.showModal(settings)
    .then(function(modal) {
      if (modal && modal.element)
      {
        modal.element.modal();
        modal.close.then(function(result) {
          if (result)
          deferred.resolve(true);
          else {
            deferred.reject(false);
          }
        });
      }
      else {
        console.log(modal);
      }
    }).catch(function(error) {
      console.log(error);
    });
    return deferred.promise;
  };
})
.controller('ConfirmModalController', function ($scope, close, title, ok, cancel, text) {

  $scope.data = {};
  $scope.data.text = angular.copy(text);
  $scope.data.title = angular.copy(title);
  $scope.data.ok = angular.copy(ok);
  $scope.data.cancel = angular.copy(cancel);

  $scope.ok = function () {
    close(true, 500);
  };

  $scope.cancel = function () {
    close(false, 500);
  };

})
.directive('confirmModal', function ($confirmModal) {
  return {
    priority: 1,
    restrict: 'A',
    scope: {
      confirmIf: "=",
      ngClick: '&',
      confirmModal: '@',
      confirmSettings: "=",
      confirmTitle: '@',
      confirmOk: '@',
      confirmCancel: '@'
    },
    link: function (scope, element, attrs) {

      element.unbind("click").bind("click", function ($event) {

        $event.preventDefault();

        if (angular.isUndefined(scope.confirmIf) || scope.confirmIf) {

          var data = {text: scope.confirm};
          if (scope.confirmTitle) {
            data.title = scope.confirmTitle;
          }
          if (scope.confirmOk) {
            data.ok = scope.confirmOk;
          }
          if (scope.confirmCancel) {
            data.cancel = scope.confirmCancel;
          }
          $confirmModal(data, scope.confirmSettings || {}).then(scope.ngClick);
        } else {

          scope.$apply(scope.ngClick);
        }
      });

    }
  }
});
<commit_msg>finish v1.0.0<commit_after>/*
* angular-confirm
* https://github.com/riderx/angular-modal-confirm
* @version v1.0.0 - 2016-01-11
* @license Apache
*/

angular.module('angularModalConfirm', ['angularModalService'])
.value('$confirmModalDefaults', {
  template:
  '
' +
  '
' +
  '
' +
  '
' +
  '<button type="button" class="close" ng-click="cancel()" data-dismiss="modal" aria-hidden="true">×</button>' +
  '
{{data.title}}
' +
  '
' +
  '
' +
  '
{{data.text}}
' +
  '
' +
  '
' +
  '<button type="button" ng-click="ok()" class="btn btn-default" data-dismiss="modal">{{data.ok}}</button>' +
  '<button type="button" ng-click="cancel()" class="btn btn-primary" data-dismiss="modal">{{data.cancel}}</button>' +
  '
' +
  '
' +
  '
' +
  '
',
  controller: 'ConfirmModalController',
  inputs: {
    title: 'Confirm',
    ok: 'OK',
    text:'hey, are you sure ?',
    cancel: 'Cancel'
  }
})
.factory('$confirmModal', function ($confirmModalDefaults, ModalService, $q) {
  return function (data, settings) {
    var defaults = angular.copy($confirmModalDefaults);
    settings = angular.extend(defaults, (settings || {}));
    data = {data: angular.extend({}, settings.inputs, data || {})};

    if ('templateUrl' in settings && 'template' in settings) {
      delete settings.template;
    }
    settings.inputs = data;

    var deferred = $q.defer();
    ModalService.showModal(settings)
    .then(function(modal) {
      if (modal && modal.element)
      {
        modal.element.modal();
        modal.close.then(function(result) {
          if (result)
          deferred.resolve(true);
          else {
            deferred.reject(false);
          }
        });
      }
      else {
        console.log(modal);
      }
    }).catch(function(error) {
      console.log(error);
    });
    return deferred.promise;
  };
})
.controller('ConfirmModalController', function ($scope, close, data) {

  $scope.data = angular.copy(data);

  $scope.ok = function () {
    close(true, 500);
  };

  $scope.cancel = function () {
    close(false, 500);
  };

})
.directive('confirmModal', function ($confirmModal) {
  return {
    priority: 1,
    restrict: 'A',
    scope: {
      confirmIf: "=",
      ngClick: '&',
      confirmModal: '@',
      confirmSettings: "=",
      confirmTitle: '@',
      confirmOk: '@',
      confirmCancel: '@'
    },
    link: function (scope, element, attrs) {

      element.unbind("click").bind("click", function ($event) {

        $event.preventDefault();

        if (angular.isUndefined(scope.confirmIf) || scope.confirmIf) {

          var data = {text: scope.confirm};
          if (scope.confirmTitle) {
            data.title = scope.confirmTitle;
          }
          if (scope.confirmOk) {
            data.ok = scope.confirmOk;
          }
          if (scope.confirmCancel) {
            data.cancel = scope.confirmCancel;
          }
          $confirmModal(data, scope.confirmSettings || {}).then(scope.ngClick);
        } else {

          scope.$apply(scope.ngClick);
        }
      });

    }
  }
});
<|endoftext|>