<commit_before>var fs = require("fs");
var util = require("util");
var winston = require('winston');
var DHT = require('bittorrent-dht');
var magnet = require('magnet-uri');
var Client = require('bittorrent-tracker');
var parseTorrent = require('parse-torrent');
var fs = require('fs');
var HashMap = require('hashmap');
require('string.prototype.endswith');
require('string.prototype.startswith');
//var ips = []; //switching to hashmap!
var ip_hashmap = new HashMap();
var trackers = [];
// winston.level = 'debug';
winston.cli();
var disable_check = function disable_check(argv, b) {
        if (argv.disableDht && argv.disableTrackers) {
            winston.debug("Can't disable DHT AND Trackers. Only one is possible.'");
            return "Failure! You can't disable both DHT and Trackers. Choose one.";
        } else {
            return true;
        }
    };
    //setup arguments parser
var argv = require('yargs')
    .usage('Usage: node peer-harvest.js -o peers.txt some.torrent | INFO_HASH | magnet:url')
    .alias('v', 'verbosity')
    .default('v', 2)
    .describe("v", "output verbosity. 0: errors, 1:warnings, 2:info, 3:debug")
    .boolean('disable-dht')
    .describe('disable-dht', 'Disable DHT and use Trackers only')
    .boolean('disable-trackers')
    .describe('disable-trackers', 'Disable Trackers and use DHT only')
    .check(disable_check)
    .boolean('print-peers')
    .describe('print-peers', "sends all peers to stdout - useful with the --silent option to parse output")
    .boolean('s')
    .alias('s', 'silent')
    .describe('s', "supress any unnecessary output")
    .alias('o', 'out-file')
    .default('o', 'peers.txt')
    .describe('o', 'Specify the file IPs will get written to')
    .default('dht-port', 20000)
    .describe('dht-port', 'The Port to be used for DHT')
    .alias('t', 'timeout')
    .default('t', 300)
    .describe('t', 'The timout in seconds after the program terminates and stops looking for new Peers')
    .alias('p', 'torrent-port')
    .default('p', 6881)
    .describe('p', "The Port we are listening on for Bittorrent connections")
    .alias('l', 'trackers')
    .describe('l', "A comma seperated list of trackers to query")
    .default('l', "udp://open.demonii.com:1337,udp://tracker.coppersurfer.tk:6969")
    .boolean("overwrite")
    .describe("overwrite", "Overwrite output file if it already exists")
    .boolean("disable-tracker-parsing")
    .describe("disable-tracker-parsing", "Disables looking for trackers in the torrent file or magnet link. Only uses those provided in --trackers")
    .demand(1)
    .help('h')
    .alias('h', 'help')
    .epilog("by stefan.github@gmail.com")
    .string('_')
    .argv;
//setup logging verbosity
if (argv.v <= 0) {
    winston.level = 'error';
}
if (argv.v == 1) {
    winston.level = 'warn';
}
if (argv.v == 2) {
    winston.level = 'info';
}
if (argv.v >= 3) {
    winston.level = 'debug';
}
if (argv.silent) {
    winston.remove(winston.transports.Console);

}
winston.debug("initialized.");
winston.debug(util.format("winston level is %s"), winston.level);
// Do some basic parsing
var argv_trackers = argv.trackers.split(","); // convert the tracker string of trackers from the arguments to an array
trackers = trackers.concat(argv_trackers); // append the parsed trackers to the array
var arg1 = argv._[0];
var torrent_type;
if (arg1.endsWith(".torrent")) {
    try {
        stats = fs.lstatSync(arg1);
        if (stats.isFile()) {
            winston.debug("Torrent exists");
            torrent_type = "file";
            winston.debug(util.format("torrent_type is %s", torrent_type));
        }
    } catch (e) {
        winston.error("The Torrent File you specified cannot be found. Please check if the file exists and the name is correct.");
        throw {
            name: "File Error",
            message: util.format("The .torrent file '%s' you specified doesn't exist", arg1),
        };
    };
} else {
    if (arg1.startsWith("magnet:")) {
        torrent_type = "magnet";
        winston.debug("torrent_type is magnet");
    } else {
        //Then it has to be an info_hash
        if (arg1.length < 40) {
            winston.error(utils.format("Invalid info hash '%s' supplied, please check your input.", arg1));
            throw {
                name: "Input Error",
                message: util.format("The info_hash '%s' you specified is not valid", arg1)
            };
        } else {
            torrent_type = "hash";
            winston.debug("torrent_type is hash");
        }
    }
}

//done parsing torrent type

var info_hash;
if (torrent_type == "magnet") {
    var parsed = magnet(arg1);
    if (!argv.disableTrackerParsing) {
        trackers = trackers.concat(parsed.tr);
    }
    winston.info(util.format("converted magnet to info hash: '%s'", parsed.infoHash)); // 'e3811b9539cacff680e418124272177c47477157'
    //console.log(parsed);
    info_hash = parsed.infoHash;

}
if (torrent_type == "hash") {
    info_hash = arg1;
    winston.debug(util.format("info_hash is '%s'", info_hash));
}
if (torrent_type == "file") {
    winston.debug(util.format("reading file '%s'", arg1));
    var torrent = fs.readFileSync(arg1);
    var parsedTorrent = parseTorrent(torrent);
    info_hash = parsedTorrent.infoHash;
    winston.info(util.format("Torrent File has info hash '%s'", info_hash));
}

if (!argv.disableDht) {
    var dht = new DHT();
    dht.listen(argv.dhtPort, function() {
        winston.info(util.format('DHT Listening on Port %d - This may take a while'), argv.dhtPort);
    })

    dht.on('ready', function() {
        winston.info("DHT active");
            // DHT is ready to use (i.e. the routing table contains at least K nodes, discovered
            // via the bootstrap nodes)
            // find peers for the given torrent info hash
        dht.lookup(info_hash);
    })

    dht.on('error', function(err) {
        winston.error(util.format("An Error occured with DHT: %s", err));
    })

    dht.on('peer', function(addr, hash, from) {
        store_ip(addr);
        store_ip(from);
        winston.debug(util.format('found peer %s through %s', addr, from));
    })
    dht.once('peer', function(addr, hash, from) {
        store_ip(addr);
        store_ip(from);
        winston.info(util.format('started receiving peers: %s through %s', addr, from));
    });
}
setTimeout(timeoutCallback, 1000 * argv.timeout);

if (!argv.disableTrackers) {
    trackers = trackers.filter(function(elem, pos) {
        return trackers.indexOf(elem) == pos;
    });
    winston.info(util.format("Deduped Tracker List contains: %s", trackers.join(", ")));

    if (torrent_type == "magnet" || torrent_type == "hash") {
        var parsedTorrent = {
            infoHash: info_hash,
            announceList: [trackers],
            announce: trackers
        };
    }
    var clientstring = "NT0-0-1--";
    var choices = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    var randomstring = "";
    for (var i = 0; i < 11; i++) {
        randomstring += choose(choices);
    }
    var peerId = new Buffer(clientstring + randomstring);
    winston.debug(util.format("using peer id '%s'", peerId));
    var bt_port = argv.torrentPort;

    winston.info(util.format("Preparing to listen for Bittorrent connections on %s", bt_port));
    var client = new Client(peerId, bt_port, parsedTorrent);

    client.on('error', function(err) {
        // fatal client error!
        winston.error(util.format("An Error occured during tracker scrape: %s", err));
    })

    client.on('warning', function(err) {
        // a tracker was unavailable or sent bad data to the client. you can probably ignore it
        winston.warn(util.format("A Warning occured during tracker scrape: %s", err));
    })

    // start getting peers from the tracker
    client.start();
    client.on('update', function(data) {
        winston.debug(data);
        winston.debug('Tracker Announce: ' + data.announce);
        winston.debug('Seeders: ' + data.complete);
        winston.debug('Leechers: ' + data.incomplete);
    })

    client.on('peer', function(addr) {
        store_ip(addr);
    });

    client.once('peer', function(addr) {
        store_ip(addr);
    });
    // announce that download has completed (and you are now a seeder)
    client.complete();
    // force a tracker announce. will trigger more 'update' events and maybe more 'peer' events
    client.update();
    // stop getting peers from the tracker, gracefully leave the swarm
    client.stop();
    // ungracefully leave the swarm (without sending final 'stop' message)
    client.destroy();
    // scrape
    client.scrape();
    client.on('scrape', function(data) {
        winston.debug(data);
        winston.debug('scrape response: ' + data.announce);
        winston.debug('seeders: ' + data.complete);
        winston.debug('leechers: ' + data.incomplete);
        winston.debug('total downloads of torrent: ' + data.incomplete);
    });
}
//storage functions
function store_ip(ip) {
    if (argv.printPeers) {
        console.log(ip);
    }
    //ips.push(ip); //deprecated, we are using a hashmap instead
    ip_hashmap.set(ip, true);
}

function timeoutCallback() {
    winston.info("terminating because of timeout!");
    persist_ips();
    process.exit();
}

function debug_ips() {
    console.log(ip_hashmap.keys());
}

function persist_ips() {
	//BUG: move this check to the beginning of the file to waste less time...
    if (!argv.overwrite) {
    	if(is_file(argv.outFile)) {
    		winston.error(util.format("The output file '%s' aready exists, if you wish to overwrite, add the overwrite option", argv.outFile));
    		throw {
                name: "File Error",
                message: util.format("The output file '%s' you specified already exist", argv.outFile),
            };
    	}
    	else {
    		winston.debug("Passed Output file check. Continuing.'");
    	}

        }
    else {
    	//check if file exists, if yes, delete it!
    }
    ip_hashmap.forEach(function(value, key) {
    	fs.appendFileSync(argv.outFile, key + '\n');
    });
    //for the array:
//    for (var i = 0; i < ips.length; i++) {
//    	//BUG: #6: when --overwrite is set, delete file first if it exists
//    	//TODO: #7 add --disable-out-file switch
//        fs.appendFileSync(argv.outFile, ips[i] + '\n');
//    }
    winston.info(util.format("Got %d ips", ip_hashmap.keys().length));
}

function is_file(file) {
	var throwOutfileError = false;
    try {
        var outfilestats = fs.lstatSync(file);
        if (outfilestats.isFile()) {
            //winston.error(util.format("The output file '%s' aready exists, if you wish to overwrite, add the overwrite option", argv.outFile));
            throwOutfileError = true;

        };

    } catch (e) {
        return false;
    }
    if (throwOutfileError) {
        return true;
    };
}

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

function choose(choices) {
    var index = Math.floor(Math.random() * choices.length);
    return choices[index];
};
<commit_msg>added A LOT of comments to clarify code<commit_after>//require filesystem stuff
var fs = require("fs");
//util for formatting strings
var util = require("util");
//winston for logging
var winston = require('winston');
//torrent libraries
var DHT = require('bittorrent-dht');
var magnet = require('magnet-uri');
var Client = require('bittorrent-tracker');
var parseTorrent = require('parse-torrent');
//Hashmap for storing ips
var HashMap = require('hashmap');
//string utility functions
require('string.prototype.endswith');
require('string.prototype.startswith');
//contains all the gathered IP adresses
var ip_hashmap = new HashMap();
//contains all the trackers we will query
var trackers = [];
// winston.level = 'debug';
//enable some color magic with winston
winston.cli();
//this check is to make sure not all means of gathering IP adresses are disabled
var disable_check = function disable_check(argv, b) {
        if (argv.disableDht && argv.disableTrackers) {
            winston.debug("Can't disable DHT AND Trackers. Only one is possible.'");
            return "Failure! You can't disable both DHT and Trackers. Choose one.";
        } else {
            return true;
        }
    };
    //setup arguments parser
var argv = require('yargs')
    .usage('Usage: node peer-harvest.js -o peers.txt some.torrent | INFO_HASH | magnet:url')
    .alias('v', 'verbosity')
    .default('v', 2)
    .describe("v", "output verbosity. 0: errors, 1:warnings, 2:info, 3:debug")
    .boolean('disable-dht')
    .describe('disable-dht', 'Disable DHT and use Trackers only')
    .boolean('disable-trackers')
    .describe('disable-trackers', 'Disable Trackers and use DHT only')
    .check(disable_check)
    .boolean('print-peers')
    .describe('print-peers', "sends all peers to stdout - useful with the --silent option to parse output")
    .boolean('s')
    .alias('s', 'silent')
    .describe('s', "supress any unnecessary output")
    .alias('o', 'out-file')
    .default('o', 'peers.txt')
    .describe('o', 'Specify the file IPs will get written to')
    .default('dht-port', 20000)
    .describe('dht-port', 'The Port to be used for DHT')
    .alias('t', 'timeout')
    .default('t', 300)
    .describe('t', 'The timout in seconds after the program terminates and stops looking for new Peers')
    .alias('p', 'torrent-port')
    .default('p', 6881)
    .describe('p', "The Port we are listening on for Bittorrent connections")
    .alias('l', 'trackers')
    .describe('l', "A comma seperated list of trackers to query")
    .default('l', "udp://open.demonii.com:1337,udp://tracker.coppersurfer.tk:6969")
    .boolean("overwrite")
    .describe("overwrite", "Overwrite output file if it already exists")
    .boolean("disable-tracker-parsing")
    .describe("disable-tracker-parsing", "Disables looking for trackers in the torrent file or magnet link. Only uses those provided in --trackers")
    .demand(1)
    .help('h')
    .alias('h', 'help')
    .epilog("by stefan.github@gmail.com")
    .string('_')
    .argv;
//setup logging verbosity
if (argv.v <= 0) {
    winston.level = 'error';
}
if (argv.v == 1) {
    winston.level = 'warn';
}
if (argv.v == 2) {
    winston.level = 'info';
}
if (argv.v >= 3) {
    winston.level = 'debug';
}
if (argv.silent) {
    winston.remove(winston.transports.Console);

}
winston.debug("initialized.");
winston.debug(util.format("winston level is %s"), winston.level);

var argv_trackers = argv.trackers.split(","); // convert the tracker string of trackers from the arguments to an array
trackers = trackers.concat(argv_trackers); // append the parsed trackers to the array
var arg1 = argv._[0]; //this is the info_hash, torrent file or magnet link the user specifies
//this contains the type of the torrent the user specified. populated later
var torrent_type; //either "hash", "magnet" or "file"
//Do some basic parsing of the input
//Case: Torrent File
if (arg1.endsWith(".torrent")) {
	//make sure the torrent file exists. If not, throw an exception
    try {
        stats = fs.lstatSync(arg1);
        if (stats.isFile()) {
            winston.debug("Torrent exists");
            torrent_type = "file";
            winston.debug(util.format("torrent_type is %s", torrent_type));
        }
    } catch (e) {
        winston.error("The Torrent File you specified cannot be found. Please check if the file exists and the name is correct.");
        throw {
            name: "File Error",
            message: util.format("The .torrent file '%s' you specified doesn't exist", arg1),
        };
    };
}
//case: Magnet Link
else {
    if (arg1.startsWith("magnet:")) {
        torrent_type = "magnet";
        winston.debug("torrent_type is magnet");
    } else {
        //Then it has to be an info_hash
        if (arg1.length < 40) {
            winston.error(utils.format("Invalid info hash '%s' supplied, please check your input.", arg1));
            throw {
                name: "Input Error",
                message: util.format("The info_hash '%s' you specified is not valid", arg1)
            };
        }
        //case: info hash
        else {
            torrent_type = "hash";
            winston.debug("torrent_type is hash");
        }
    }
}

//done parsing torrent type

var info_hash; //info hash. In case of magnet or file we get this by parsing
if (torrent_type == "magnet") {
    var parsed = magnet(arg1);
    if (!argv.disableTrackerParsing) {
    	//merging the existing tracker array with the trackers extracted from the torrent file
        trackers = trackers.concat(parsed.tr);
    }
    winston.info(util.format("converted magnet to info hash: '%s'", parsed.infoHash)); // 'e3811b9539cacff680e418124272177c47477157'
    //set info hash to the one we got from the magnet link
    info_hash = parsed.infoHash;

}
if (torrent_type == "hash") {
	//set info_hash directly to the one supplied as an argument by the user
    info_hash = arg1;
    winston.debug(util.format("info_hash is '%s'", info_hash));
}
if (torrent_type == "file") {
    winston.debug(util.format("reading file '%s'", arg1));
    //read the torrent file synchronously and store it's contents
    var torrent = fs.readFileSync(arg1);
    //parse the torrent file contents
    var parsedTorrent = parseTorrent(torrent);
    //set the info hash to the one we got from the torrent file
    info_hash = parsedTorrent.infoHash;
    winston.info(util.format("Torrent File has info hash '%s'", info_hash));
}
//If DHT is not explicitly disabled by the user, use it.
if (!argv.disableDht) {
    var dht = new DHT();
    //set DTH to listen on the specified (or default) port
    dht.listen(argv.dhtPort, function() {
        winston.info(util.format('DHT Listening on Port %d - This may take a while'), argv.dhtPort);
    })
    //fires once we are ready to receive stuff over DHT
    dht.on('ready', function() {
        winston.info("DHT active");
            // DHT is ready to use (i.e. the routing table contains at least K nodes, discovered
            // via the bootstrap nodes)
            // find peers for the given torrent info hash
        dht.lookup(info_hash);
    })
    //Fires on a DHT related error
    dht.on('error', function(err) {
        winston.error(util.format("An Error occured with DHT: %s", err));
    })
    //fires ALWAYS when a peer has been discovered
    dht.on('peer', function(addr, hash, from) {
        store_ip(addr);
        store_ip(from);
        winston.debug(util.format('found peer %s through %s', addr, from));
    })
    //fires when the FIRST peer has been discovered
    dht.once('peer', function(addr, hash, from) {
        store_ip(addr);
        store_ip(from);
        winston.info(util.format('started receiving peers: %s through %s', addr, from));
    });
}
//setting a timeout to terminate the program. Otherwise it would just collect information indefinitely
setTimeout(timeoutCallback, 1000 * argv.timeout);

//If Trackers are not explicitly disabled, use them!
if (!argv.disableTrackers) {
	//deduplicate the trackers array
    trackers = trackers.filter(function(elem, pos) {
        return trackers.indexOf(elem) == pos;
    });
    winston.info(util.format("Deduped Tracker List contains: %s", trackers.join(", ")));
    //hackish way of construction a fake torrent object from the magnet link. Seems to work though :D
    if (torrent_type == "magnet" || torrent_type == "hash") {
        var parsedTorrent = {
            infoHash: info_hash,
            announceList: [trackers],
            announce: trackers
        };
    }
    //The fixed peer id part. Contains a string to identify the client type and the client version
    var clientstring = "NT0-0-1--";
    //possible choices for the random part of the peer id
    var choices = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    var randomstring = "";
    //generate random part of peer id
    for (var i = 0; i < 11; i++) {
        randomstring += choose(choices);
    }
    //set the peer id (has to be a Buffer)
    var peerId = new Buffer(clientstring + randomstring);
    winston.debug(util.format("using peer id '%s'", peerId));
    //set the bittorrent port
    var bt_port = argv.torrentPort;

    winston.info(util.format("Preparing to listen for Bittorrent connections on %s", bt_port));
    var client = new Client(peerId, bt_port, parsedTorrent);
    //fires on a bittorrent related error
    client.on('error', function(err) {
        // fatal client error!
        winston.error(util.format("An Error occured during tracker scrape: %s", err));
    })
    //fires on a bittorrent related warning
    client.on('warning', function(err) {
        // a tracker was unavailable or sent bad data to the client. you can probably ignore it
        winston.warn(util.format("A Warning occured during tracker scrape: %s", err));
    })

    // start getting peers from the tracker
    client.start();
    //fires when we get updated info from a tracker
    client.on('update', function(data) {
        winston.debug(data);
        winston.debug('Tracker Announce: ' + data.announce);
        winston.debug('Seeders: ' + data.complete);
        winston.debug('Leechers: ' + data.incomplete);
    })
    //fires ALWAYS when a peer is found
    client.on('peer', function(addr) {
        store_ip(addr);
    });
    //fires once a peer is found
    client.once('peer', function(addr) {
    	winston.info(util.format('started receiving peers: %s from trackers', addr));
        store_ip(addr);
    });
    // announce that download has completed (and you are now a seeder)
    client.complete();
    // force a tracker announce. will trigger more 'update' events and maybe more 'peer' events
    client.update();
    // stop getting peers from the tracker, gracefully leave the swarm
    client.stop();
    // ungracefully leave the swarm (without sending final 'stop' message)
    client.destroy();
    // scrape
    client.scrape();
    client.on('scrape', function(data) {
        winston.debug(data);
        winston.debug('scrape response: ' + data.announce);
        winston.debug('seeders: ' + data.complete);
        winston.debug('leechers: ' + data.incomplete);
        winston.debug('total downloads of torrent: ' + data.incomplete);
    });
}
//storage functions
//store a ip adress (in hashmap, this doesn't write to disk. see persist_ips() for that)
function store_ip(ip) {
    if (argv.printPeers) {
        console.log(ip);
    }
    //ips.push(ip); //deprecated, we are using a hashmap instead
    ip_hashmap.set(ip, true);
}
//this fires once the timeout is reached
function timeoutCallback() {
    winston.info("terminating because of timeout!");
    //write gathered ips to file
    persist_ips();
    //terminate the program
    process.exit();
}
//debug function to list all gathered ips. Currently not used
function debug_ips() {
    console.log(ip_hashmap.keys());
}
//writes gathered ips to disk
function persist_ips() {
	//BUG: move this check to the beginning of the file to waste less time...
    if (!argv.overwrite) {
    	if(is_file(argv.outFile)) {
    		winston.error(util.format("The output file '%s' aready exists, if you wish to overwrite, add the overwrite option", argv.outFile));
    		throw {
                name: "File Error",
                message: util.format("The output file '%s' you specified already exist", argv.outFile),
            };
    	}
    	else {
    		winston.debug("Passed Output file check. Continuing.'");
    	}

        }
    else {
    	//check if file exists, if yes, delete it!
    }
    ip_hashmap.forEach(function(value, key) {
    	fs.appendFileSync(argv.outFile, key + '\n');
    });
    //for the array:
//    for (var i = 0; i < ips.length; i++) {
//    	//BUG: #6: when --overwrite is set, delete file first if it exists
//    	//TODO: #7 add --disable-out-file switch
//        fs.appendFileSync(argv.outFile, ips[i] + '\n');
//    }
    winston.info(util.format("Got %d ips", ip_hashmap.keys().length));
}
//checks if a file exists on the filesystem
function is_file(file) {
	var throwOutfileError = false;
    try {
        var outfilestats = fs.lstatSync(file);
        if (outfilestats.isFile()) {
            //winston.error(util.format("The output file '%s' aready exists, if you wish to overwrite, add the overwrite option", argv.outFile));
            throwOutfileError = true;

        };

    } catch (e) {
        return false;
    }
    if (throwOutfileError) {
        return true;
    };
}
//returns a random number between min and max. Currently unused
function getRandomArbitrary(min, max) {
    return Math.random() * (max - min) + min;
}
//chooses a radnom element from a specified array. Used to generate the peer id
function choose(choices) {
    var index = Math.floor(Math.random() * choices.length);
    return choices[index];
};
<|endoftext|>


<commit_before>"use strict";
/* jshint esversion: 6, strict: global, loopfunc: true, laxbreak: true */
/* globals chrome, getSettings, setTimeout */
// licensed under the MPL 2.0 by (github.com/serv-inc)

/**
 * @fileoverview looks through all received text to find words, adds up
 * score, shows blocking page
 */
const URL_RE = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/;

const BLOCKPAGE_URL = 'blockpage.html';

/** add URLS, and check whether added */
class BlockCache {
  constructor() {
    this._cache = [];
  }

  add(url) {
    if ( this.allow(url) ) {
      this._cache.push(url);
    }
  }

  allow(url) {
    return ! this._cache.includes(url);
  }
}
let blockCache = new BlockCache();

chrome.runtime.onMessage.addListener(function(pageText, sender, sendResponse) {
  if ( ! getSettings().whitelistRegExp.test(sender.url)
       && ! sender.url.includes(BLOCKPAGE_URL) ) {
    if ( blockCache.allow(sender.url) ) {
      scan(pageText, sender);
    } else {
      setBlockPage(sender, ['cached site']);
    }
  }
});

// how to validate blockpage? new URL(blockpage) is not available?
function setBlockPage(sender, phraseArray=[''], limit="???") {
  if ( sender.url.includes(BLOCKPAGE_URL) ) {
    return;
  }

  if ( blockCache.allow(sender.url) ) {
    blockCache.add(sender.url);
  }

  var blockpage;
  if (isValid(getSettings().blockpage)) {
    blockpage = getSettings().blockpage;
  } else {
    blockpage = chrome.extension.getURL(BLOCKPAGE_URL)
      + '?' + encodeURIComponent(sender.tab.url)
      + '&' + encodeURIComponent(JSON.stringify(phraseArray))
      + '&' + limit;
  }
  chrome.tabs.update(sender.tab.id, {'url': blockpage});
}

function isValid(urlString) {
  return typeof urlString === "string" && URL_RE.test(urlString);
}

// td: this is non-testable due to i, score, ... above, maybe refactor
function scan(pageText, sender, score=0, matches=[],
              i=(getSettings().blockvals.length-1)) {
  score += _do_score(pageText, getSettings().blockvals[i], matches);

  if ( score > getSettings().limit ) {
    setBlockPage(sender, matches, ">= " + score);
  }

  if ( i > 0 ) {
    setTimeout(function() {
      scan(pageText, sender, score, matches, i-1);
    }, 0);
  }
}

/** @return score of this blockObject on text */
function _do_score(pageText, blockObject, all_matches) {
  let tmp = pageText.match(RegExp(blockObject.value, "gi"));
  let matches = new Set();
  if ( tmp !== null ) {
    tmp.forEach((el) => {matches.add(el.toLowerCase());});
  }
  matches.forEach((el) => all_matches.push(el));
  return matches.size * blockObject.name;
}
<commit_msg>first attempt at promises<commit_after>"use strict";
/* jshint esversion: 6, strict: global, loopfunc: true, laxbreak: true */
/* globals chrome, getSettings, setTimeout */
// licensed under the MPL 2.0 by (github.com/serv-inc)

/**
 * @fileoverview looks through all received text to find words, adds up
 * score, shows blocking page
 */
const URL_RE = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/;

const BLOCKPAGE_URL = 'blockpage.html';

/** add URLS, and check whether added */
class BlockCache {
  constructor() {
    this._cache = [];
  }

  add(url) {
    if ( this.allow(url) ) {
      this._cache.push(url);
    }
  }

  allow(url) {
    return ! this._cache.includes(url);
  }
}
let blockCache = new BlockCache();

chrome.runtime.onMessage.addListener(function(pageText, sender, sendResponse) {
  if ( ! getSettings().whitelistRegExp.test(sender.url)
       && ! sender.url.includes(BLOCKPAGE_URL) ) {
    if ( blockCache.allow(sender.url) ) {
      scan(pageText, sender);
    } else {
      setBlockPage(sender, ['cached site']);
    }
  }
});

// how to validate blockpage? new URL(blockpage) is not available?
function setBlockPage(sender, phraseArray=[''], limit="???") {
  if ( sender.url.includes(BLOCKPAGE_URL) ) {
    return;
  }

  if ( blockCache.allow(sender.url) ) {
    blockCache.add(sender.url);
  }

  var blockpage;
  if (isValid(getSettings().blockpage)) {
    blockpage = getSettings().blockpage;
  } else {
    blockpage = chrome.extension.getURL(BLOCKPAGE_URL)
      + '?' + encodeURIComponent(sender.tab.url)
      + '&' + encodeURIComponent(JSON.stringify(phraseArray))
      + '&' + limit;
  }
  chrome.tabs.update(sender.tab.id, {'url': blockpage});
}

function isValid(urlString) {
  return typeof urlString === "string" && URL_RE.test(urlString);
}

// td: this is non-testable due to i, score, ... above (and matches),
// maybe refactor
function scan(pageText, sender, score=0, matches=[]) {
  var threads = [];
  getSettings().blockvals.foreach(
    (blockval) => {
      var t =_do_score(pageText, getSettings().blockvals[i], matches);
      threads.push(t);
      t.then(singlescore => {
        score += singlescore;
        if ( score > getSettings().limit ) {
          setBlockPage(sender, matches, ">= " + score);
        }
      });
    }
  );
}

/** @return score of this blockObject on text */
async function _do_score(pageText, blockObject, all_matches) {
  let tmp = pageText.match(RegExp(blockObject.value, "gi"));
  let matches = new Set();
  if ( tmp !== null ) {
    tmp.forEach((el) => {matches.add(el.toLowerCase());});
  }
  matches.forEach((el) => all_matches.push(el));
  return matches.size * blockObject.name;
}
<|endoftext|>


<commit_before>var isAlive = true;
var snakes = [];
var egg = null;
var isFirst = true;
var panel = document.getElementById('container');
var scoreBoard = document.getElementById('score');
var score = 1;
var timer = 0;

var DIR = {
    L: 1,
    R: 2,
    U: 3,
    D: 4
}

function snake(top, left, dir) {
    this.top = top;
    this.left = left;
    this.dir = dir;
}

function newEgg(top, left) {
    this.top = top;
    this.left = left;
}

function gameRun() {
    if (isFirst) {
        snakes.push(new snake(40, 40, DIR.D));
        createEgg();
        isFirst = false;
    }

    sankeRun();

    display();
}

function createEgg() {
    egg = new newEgg(
        Math.floor(Math.random() * 25) * 20,
        Math.floor(Math.random() * 15) * 20
    )
}

document.onkeydown = function (e) {
    var header = snakes[snakes.length - 1];

    switch (e.keyCode) {
        case 37:
            //left
            if (header.dir != DIR.R) {
                header.dir = DIR.L
            }
            break;
        case 38:
            //up
            if (header.dir != DIR.D) {
                header.dir = DIR.U
            }
            break;
        case 39:
            //Right
            if (header.dir != DIR.L) {
                header.dir = DIR.R
            }
            break;
        case 40:
            //Down
            if (header.dir != DIR.U) {
                header.dir = DIR.D
            }
            break;
    }

}.bind(panel)

function sankeRun() {
    var header = snakes[snakes.length - 1];
    var newHeader = null;

    switch (header.dir) {
        case DIR.L:
            newHeader = new snake(header.top, header.left - 20, header.dir);
            break;
        case DIR.R:
            newHeader = new snake(header.top, header.left + 20, header.dir);
            break;
        case DIR.U:
            newHeader = new snake(header.top - 20, header.left, header.dir);
            break;
        case DIR.D:
            newHeader = new snake(header.top + 20, header.left, header.dir);
            break;
    }
    deathCheck(newHeader);
    if (isAlive) {
        snakes.push(newHeader);
        if (newHeader.top == egg.top && newHeader.left == egg.left) {
            createEgg();
        } else {
            snakes.shift();
        }
    }else{
        clearInterval(timer);
    }

}

function deathCheck(header) {
    //var header = snakes[snakes.length - 1];
    if (header.top < 0 || header.top > 480 || header.left < 0 || header.left > 280) {
        isAlive = false;
        return;
    }

    for (var i = 0; i < snakes.length; i++) {
        if (header.top == snakes[i].top && header.left == snakes[i].left) {
            isAlive = false;
            return;
        }
    }
}

function display() {
    if (isAlive) {
        if (score != snakes.length) {
            score = snakes.length;
            scoreBoard.innerHTML = 'Score:' + score;
        }
    }else{
        scoreBoard.innerHTML = 'Game Over, Final Score:' + score;
    }

    var fragment = document.createDocumentFragment();
    for (var i = 0; i < snakes.length; i++) {
        var div = document.createElement('div');
        div.className = 'snake';
        div.style.top = snakes[i].top + 'px';
        div.style.left = snakes[i].left + 'px';
        fragment.appendChild(div);
    }
    //console.log(egg);
    var ediv = document.createElement('div');
    ediv.className = 'egg';
    ediv.style.top = egg.top + 'px';
    ediv.style.left = egg.left + 'px';
    fragment.appendChild(ediv);
    panel.innerHTML = '';
    panel.appendChild(fragment);
}

//gameRun();
timer = setInterval(gameRun, 100);

<commit_msg>add comments a1<commit_after>//This is added by A1
var isAlive = true;
var snakes = [];
var egg = null;
var isFirst = true;
var panel = document.getElementById('container');
var scoreBoard = document.getElementById('score');
var score = 1;
var timer = 0;

var DIR = {
    L: 1,
    R: 2,
    U: 3,
    D: 4
}

function snake(top, left, dir) {
    this.top = top;
    this.left = left;
    this.dir = dir;
}

function newEgg(top, left) {
    this.top = top;
    this.left = left;
}

function gameRun() {
    if (isFirst) {
        snakes.push(new snake(40, 40, DIR.D));
        createEgg();
        isFirst = false;
    }

    sankeRun();

    display();
}

function createEgg() {
    egg = new newEgg(
        Math.floor(Math.random() * 25) * 20,
        Math.floor(Math.random() * 15) * 20
    )
}

document.onkeydown = function (e) {
    var header = snakes[snakes.length - 1];

    switch (e.keyCode) {
        case 37:
            //left
            if (header.dir != DIR.R) {
                header.dir = DIR.L
            }
            break;
        case 38:
            //up
            if (header.dir != DIR.D) {
                header.dir = DIR.U
            }
            break;
        case 39:
            //Right
            if (header.dir != DIR.L) {
                header.dir = DIR.R
            }
            break;
        case 40:
            //Down
            if (header.dir != DIR.U) {
                header.dir = DIR.D
            }
            break;
    }

}.bind(panel)

function sankeRun() {
    var header = snakes[snakes.length - 1];
    var newHeader = null;

    switch (header.dir) {
        case DIR.L:
            newHeader = new snake(header.top, header.left - 20, header.dir);
            break;
        case DIR.R:
            newHeader = new snake(header.top, header.left + 20, header.dir);
            break;
        case DIR.U:
            newHeader = new snake(header.top - 20, header.left, header.dir);
            break;
        case DIR.D:
            newHeader = new snake(header.top + 20, header.left, header.dir);
            break;
    }
    deathCheck(newHeader);
    if (isAlive) {
        snakes.push(newHeader);
        if (newHeader.top == egg.top && newHeader.left == egg.left) {
            createEgg();
        } else {
            snakes.shift();
        }
    }else{
        clearInterval(timer);
    }

}

function deathCheck(header) {
    //var header = snakes[snakes.length - 1];
    if (header.top < 0 || header.top > 480 || header.left < 0 || header.left > 280) {
        isAlive = false;
        return;
    }

    for (var i = 0; i < snakes.length; i++) {
        if (header.top == snakes[i].top && header.left == snakes[i].left) {
            isAlive = false;
            return;
        }
    }
}

function display() {
    if (isAlive) {
        if (score != snakes.length) {
            score = snakes.length;
            scoreBoard.innerHTML = 'Score:' + score;
        }
    }else{
        scoreBoard.innerHTML = 'Game Over, Final Score:' + score;
    }

    var fragment = document.createDocumentFragment();
    for (var i = 0; i < snakes.length; i++) {
        var div = document.createElement('div');
        div.className = 'snake';
        div.style.top = snakes[i].top + 'px';
        div.style.left = snakes[i].left + 'px';
        fragment.appendChild(div);
    }
    //console.log(egg);
    var ediv = document.createElement('div');
    ediv.className = 'egg';
    ediv.style.top = egg.top + 'px';
    ediv.style.left = egg.left + 'px';
    fragment.appendChild(ediv);
    panel.innerHTML = '';
    panel.appendChild(fragment);
}

//gameRun();
timer = setInterval(gameRun, 100);

<|endoftext|>


<commit_before>#!/usr/bin/node

const dash = require('dashdash');
const debug = require('debug')('url-inspector');

const parser = dash.createParser({options: [
	{
		names: ['help', 'h'],
		type: 'bool',
		help: 'Print this help and exit.'
	},
	{
		names: ['all'],
		type: 'bool',
		help: 'enable all metadata'
	},
	{
		names: ['providers', 'p'],
		type: 'string',
		help: 'Path to js file exporting providers list'
	},
	{
		names: ['ua'],
		type: 'string',
		help: 'Custom User-Agent'
	}
]});

let opts;
try {
	opts = parser.parse(process.argv);
} catch(e) {
	console.error(e.toString());
	opts = {help: true};
}

let url = opts._args && opts._args.pop();

if (opts.help || !url) {
	const help = parser.help({includeEnv: true}).trimRight();
	console.info('usage: url-inspector [OPTIONS] <url>\n' + 'options:\n' + help);
	process.exit(0);
}

const inspector = require('..');

opts.file = true;
if (url.startsWith('./') || url.startsWith('/')) url = "file://" + url;

if (opts.providers) {
	opts.providers = require(opts.providers);
}

inspector(url, opts, function(err, meta) {
	if (err) {
		console.error(err);
		process.exit(1);
	} else {
		console.info(require('util').inspect(meta));
		process.exit(0);
	}
});

<commit_msg>Use promise api<commit_after>#!/usr/bin/node

const dash = require('dashdash');
const debug = require('debug')('url-inspector');

const parser = dash.createParser({options: [
	{
		names: ['help', 'h'],
		type: 'bool',
		help: 'Print this help and exit.'
	},
	{
		names: ['all'],
		type: 'bool',
		help: 'enable all metadata'
	},
	{
		names: ['providers', 'p'],
		type: 'string',
		help: 'Path to js file exporting providers list'
	},
	{
		names: ['ua'],
		type: 'string',
		help: 'Custom User-Agent'
	}
]});

let opts;
try {
	opts = parser.parse(process.argv);
} catch(e) {
	console.error(e.toString());
	opts = {help: true};
}

let url = opts._args && opts._args.pop();

if (opts.help || !url) {
	const help = parser.help({includeEnv: true}).trimRight();
	console.info('usage: url-inspector [OPTIONS] <url>\n' + 'options:\n' + help);
	process.exit(0);
}

const inspector = require('..');

opts.file = true;
if (url.startsWith('./') || url.startsWith('/')) url = "file://" + url;

if (opts.providers) {
	opts.providers = require(opts.providers);
}

inspector(url, opts).then((meta) => {
	console.info(require('util').inspect(meta));
	process.exit(0);
}).catch((err) => {
	console.error(err);
	process.exit(1);
});
<|endoftext|>


<commit_before>// Path parsing and rendering code adapted from fabric.js -- Thanks!
var cmdlen = { m:2, l:2, h:1, v:1, c:6, s:4, q:4, t:2, a:7 },
    regexp = [/([MLHVCSQTAZmlhvcsqtaz])/g, /###/, /(\d)-/g, /\s|,|###/];

module.exports = function(pathstr) {
  var result = [],
      path,
      curr,
      chunks,
      parsed, param,
      cmd, len, i, j, n, m;

  // First, break path into command sequence
  path = pathstr
    .slice()
    .replace(regexp[0], '###$1')
    .split(regexp[1])
    .slice(1);

  // Next, parse each command in turn
  for (i=0, n=path.length; i<n; ++i) {
    curr = path[i];
    chunks = curr
      .slice(1)
      .trim()
      .replace(regexp[2],'$1###-')
      .split(regexp[3]);
    cmd = curr.charAt(0);

    parsed = [cmd];
    for (j=0, m=chunks.length; j<m; ++j) {
      if ((param = +chunks[j]) === param) { // not NaN
        parsed.push(param);
      }
    }

    len = cmdlen[cmd.toLowerCase()];
    if (parsed.length-1 > len) {
      for (j=1, m=parsed.length; j<m; j+=len) {
        result.push([cmd].concat(parsed.slice(j, j+len)));
      }
    }
    else {
      result.push(parsed);
    }
  }

  return result;
};
<commit_msg>Update SVG path parse delimiting.<commit_after>// Path parsing and rendering code adapted from fabric.js -- Thanks!
var cmdlen = { m:2, l:2, h:1, v:1, c:6, s:4, q:4, t:2, a:7 },
    regexp = [/([MLHVCSQTAZmlhvcsqtaz])/g, /###/, /(\d)([-+])/g, /\s|,|###/];

module.exports = function(pathstr) {
  var result = [],
      path,
      curr,
      chunks,
      parsed, param,
      cmd, len, i, j, n, m;

  // First, break path into command sequence
  path = pathstr
    .slice()
    .replace(regexp[0], '###$1')
    .split(regexp[1])
    .slice(1);

  // Next, parse each command in turn
  for (i=0, n=path.length; i<n; ++i) {
    curr = path[i];
    chunks = curr
      .slice(1)
      .trim()
      .replace(regexp[2],'$1###$2')
      .split(regexp[3]);
    cmd = curr.charAt(0);

    parsed = [cmd];
    for (j=0, m=chunks.length; j<m; ++j) {
      if ((param = +chunks[j]) === param) { // not NaN
        parsed.push(param);
      }
    }

    len = cmdlen[cmd.toLowerCase()];
    if (parsed.length-1 > len) {
      for (j=1, m=parsed.length; j<m; j+=len) {
        result.push([cmd].concat(parsed.slice(j, j+len)));
      }
    }
    else {
      result.push(parsed);
    }
  }

  return result;
};
<|endoftext|>


<commit_before>'use strict';

angular.module('lemur')

  .config(function config($stateProvider) {

    $stateProvider
      .state('certificates', {
        url: '/certificates',
        templateUrl: '/angular/certificates/view/view.tpl.html',
        controller: 'CertificatesViewController'
      })
      .state('certificate', {
        url: '/certificates/:name',
        templateUrl: '/angular/certificates/view/view.tpl.html',
        controller: 'CertificatesViewController'
      });
  })

  .controller('CertificatesViewController', function ($q, $scope, $uibModal, $stateParams, $location, CertificateApi, CertificateService, MomentService, ngTableParams, toaster) {
    $scope.filter = $stateParams;
    $scope.expiredText = ["Show Expired", "Hide Expired"];
    $scope.expiredValue = 0;
    $scope.expiredButton = $scope.expiredText[$scope.expiredValue];
    $scope.certificateTable = new ngTableParams({
      page: 1,            // show first page
      count: 10,          // count per page
      sorting: {
        id: 'desc'     // initial sorting
      },
      filter: $scope.filter
    }, {
      total: 0,           // length of data
      getData: function ($defer, params) {
        $scope.path = $location.path();
        // Handle Permalink clicks through a separate API
        // Clicking on Permalink adds the certificate name to the URL after "certificates/", which is used to identify the click
        if ($scope.path.indexOf('certificates/') > -1 && $scope.path.split('/')[2].length > 0) {
          $scope.certificateName = $scope.path.split('/')[2];
          CertificateApi.one('name').one($scope.certificateName).getList()
            .then(function (data) {
              params.total(data.total);
              $defer.resolve(data);
            });
        }
        else {
          CertificateApi.getList(params.url())
              .then(function (data) {
                params.total(data.total);
                $defer.resolve(data);
              });
        }
      }
    });

    $scope.showExpired = function () {
      if ($scope.expiredValue === 0) {
        $scope.expiredValue = 1;
      }
      else {
        $scope.expiredValue = 0;
      }
      $scope.expiredButton = $scope.expiredText[$scope.expiredValue];
      $scope.certificateTable = new ngTableParams({
        page: 1,            // show first page
        count: 10,          // count per page
        sorting: {
          id: 'desc'     // initial sorting
        },
        filter: $scope.filter
      }, {
        getData: function ($defer, params) {
          $scope.temp = angular.copy(params.url());
          $scope.temp.showExpired = $scope.expiredValue;
          CertificateApi.getList($scope.temp)
            .then(function (data) {
              params.total(data.total);
              $defer.resolve(data);
            });
        }
      })
    };

    $scope.momentService = MomentService;

    $scope.remove = function (certificate) {
      certificate.remove().then(
        function () {
          $scope.certificateTable.reload();
        },
        function (response) {
          toaster.pop({
            type: 'error',
            title: certificate.name,
            body: 'Unable to remove certificate! ' + response.data.message,
            timeout: 100000
          });
        });
    };

    $scope.loadPrivateKey = function (certificate) {
      if (certificate.privateKey !== undefined) {
        return;
      }

      CertificateService.loadPrivateKey(certificate).then(
        function (response) {
          if (response.key === null) {
            toaster.pop({
              type: 'warning',
              title: certificate.name,
              body: 'No private key found!'
            });
          } else {
            certificate.privateKey = response.key;
          }
        },
        function () {
          toaster.pop({
            type: 'error',
            title: certificate.name,
            body: 'You do not have permission to view this key!',
            timeout: 100000
          });
        });
    };

    $scope.updateNotify = function (certificate) {
      CertificateService.updateNotify(certificate).then(
        function () {
          toaster.pop({
            type: 'success',
            title: certificate.name,
            body: 'Updated!'
          });
        },
        function (response) {
          toaster.pop({
            type: 'error',
            title: certificate.name,
            body: 'Unable to update! ' + response.data.message,
            timeout: 100000
          });
          certificate.notify = false;
        });
    };
    $scope.getCertificateStatus = function () {
      var def = $q.defer();
      def.resolve([{'title': 'True', 'id': true}, {'title': 'False', 'id': false}]);
      return def;
    };

    $scope.show = {title: 'Current User', value: 'currentUser'};

    $scope.fields = [{title: 'Current User', value: 'currentUser'}, {title: 'All', value: 'all'}];

    $scope.create = function () {
      var uibModalInstance = $uibModal.open({
        animation: true,
        controller: 'CertificateCreateController',
        templateUrl: '/angular/certificates/certificate/certificateWizard.tpl.html',
        size: 'lg',
        backdrop: 'static'
      });

      uibModalInstance.result.then(function () {
        $scope.certificateTable.reload();
      });
    };

    $scope.clone = function (certificateId) {
      var uibModalInstance = $uibModal.open({
        animation: true,
        controller: 'CertificateCloneController',
        templateUrl: '/angular/certificates/certificate/certificateWizard.tpl.html',
        size: 'lg',
        backdrop: 'static',
        resolve: {
          editId: function () {
            return certificateId;
          }
        }
      });

      uibModalInstance.result.then(function () {
        $scope.certificateTable.reload();
      });
    };

    $scope.edit = function (certificateId) {
      var uibModalInstance = $uibModal.open({
        animation: true,
        controller: 'CertificateEditController',
        templateUrl: '/angular/certificates/certificate/edit.tpl.html',
        size: 'lg',
        backdrop: 'static',
        resolve: {
          editId: function () {
            return certificateId;
          }
        }
      });

      uibModalInstance.result.then(function () {
        $scope.certificateTable.reload();
      });
    };

    $scope.import = function () {
      var uibModalInstance = $uibModal.open({
        animation: true,
        controller: 'CertificateUploadController',
        templateUrl: '/angular/certificates/certificate/upload.tpl.html',
        size: 'lg',
        backdrop: 'static'
      });

      uibModalInstance.result.then(function () {
        $scope.certificateTable.reload();
      });
    };

    $scope.export = function (certificateId) {
      $uibModal.open({
        animation: true,
        controller: 'CertificateExportController',
        templateUrl: '/angular/certificates/certificate/export.tpl.html',
        size: 'lg',
        backdrop: 'static',
        resolve: {
          editId: function () {
            return certificateId;
          }
        }
      });
    };

     $scope.revoke = function (certificateId) {
      $uibModal.open({
        animation: true,
        controller: 'CertificateRevokeController',
        templateUrl: '/angular/certificates/certificate/revoke.tpl.html',
        size: 'lg',
        backdrop: 'static',
        resolve: {
          revokeId: function () {
            return certificateId;
          }
        }
      });
    };
  });
<commit_msg>Added missing semi-colon and changed double quotes to single quotes<commit_after>'use strict';

angular.module('lemur')

  .config(function config($stateProvider) {

    $stateProvider
      .state('certificates', {
        url: '/certificates',
        templateUrl: '/angular/certificates/view/view.tpl.html',
        controller: 'CertificatesViewController'
      })
      .state('certificate', {
        url: '/certificates/:name',
        templateUrl: '/angular/certificates/view/view.tpl.html',
        controller: 'CertificatesViewController'
      });
  })

  .controller('CertificatesViewController', function ($q, $scope, $uibModal, $stateParams, $location, CertificateApi, CertificateService, MomentService, ngTableParams, toaster) {
    $scope.filter = $stateParams;
    $scope.expiredText = ['Show Expired', 'Hide Expired'];
    $scope.expiredValue = 0;
    $scope.expiredButton = $scope.expiredText[$scope.expiredValue];
    $scope.certificateTable = new ngTableParams({
      page: 1,            // show first page
      count: 10,          // count per page
      sorting: {
        id: 'desc'     // initial sorting
      },
      filter: $scope.filter
    }, {
      total: 0,           // length of data
      getData: function ($defer, params) {
        $scope.path = $location.path();
        // Handle Permalink clicks through a separate API
        // Clicking on Permalink adds the certificate name to the URL after "certificates/", which is used to identify the click
        if ($scope.path.indexOf('certificates/') > -1 && $scope.path.split('/')[2].length > 0) {
          $scope.certificateName = $scope.path.split('/')[2];
          CertificateApi.one('name').one($scope.certificateName).getList()
            .then(function (data) {
              params.total(data.total);
              $defer.resolve(data);
            });
        }
        else {
          CertificateApi.getList(params.url())
              .then(function (data) {
                params.total(data.total);
                $defer.resolve(data);
              });
        }
      }
    });

    $scope.showExpired = function () {
      if ($scope.expiredValue === 0) {
        $scope.expiredValue = 1;
      }
      else {
        $scope.expiredValue = 0;
      }
      $scope.expiredButton = $scope.expiredText[$scope.expiredValue];
      $scope.certificateTable = new ngTableParams({
        page: 1,            // show first page
        count: 10,          // count per page
        sorting: {
          id: 'desc'     // initial sorting
        },
        filter: $scope.filter
      }, {
        getData: function ($defer, params) {
          $scope.temp = angular.copy(params.url());
          $scope.temp.showExpired = $scope.expiredValue;
          CertificateApi.getList($scope.temp)
            .then(function (data) {
              params.total(data.total);
              $defer.resolve(data);
            });
        }
      });
    };

    $scope.momentService = MomentService;

    $scope.remove = function (certificate) {
      certificate.remove().then(
        function () {
          $scope.certificateTable.reload();
        },
        function (response) {
          toaster.pop({
            type: 'error',
            title: certificate.name,
            body: 'Unable to remove certificate! ' + response.data.message,
            timeout: 100000
          });
        });
    };

    $scope.loadPrivateKey = function (certificate) {
      if (certificate.privateKey !== undefined) {
        return;
      }

      CertificateService.loadPrivateKey(certificate).then(
        function (response) {
          if (response.key === null) {
            toaster.pop({
              type: 'warning',
              title: certificate.name,
              body: 'No private key found!'
            });
          } else {
            certificate.privateKey = response.key;
          }
        },
        function () {
          toaster.pop({
            type: 'error',
            title: certificate.name,
            body: 'You do not have permission to view this key!',
            timeout: 100000
          });
        });
    };

    $scope.updateNotify = function (certificate) {
      CertificateService.updateNotify(certificate).then(
        function () {
          toaster.pop({
            type: 'success',
            title: certificate.name,
            body: 'Updated!'
          });
        },
        function (response) {
          toaster.pop({
            type: 'error',
            title: certificate.name,
            body: 'Unable to update! ' + response.data.message,
            timeout: 100000
          });
          certificate.notify = false;
        });
    };
    $scope.getCertificateStatus = function () {
      var def = $q.defer();
      def.resolve([{'title': 'True', 'id': true}, {'title': 'False', 'id': false}]);
      return def;
    };

    $scope.show = {title: 'Current User', value: 'currentUser'};

    $scope.fields = [{title: 'Current User', value: 'currentUser'}, {title: 'All', value: 'all'}];

    $scope.create = function () {
      var uibModalInstance = $uibModal.open({
        animation: true,
        controller: 'CertificateCreateController',
        templateUrl: '/angular/certificates/certificate/certificateWizard.tpl.html',
        size: 'lg',
        backdrop: 'static'
      });

      uibModalInstance.result.then(function () {
        $scope.certificateTable.reload();
      });
    };

    $scope.clone = function (certificateId) {
      var uibModalInstance = $uibModal.open({
        animation: true,
        controller: 'CertificateCloneController',
        templateUrl: '/angular/certificates/certificate/certificateWizard.tpl.html',
        size: 'lg',
        backdrop: 'static',
        resolve: {
          editId: function () {
            return certificateId;
          }
        }
      });

      uibModalInstance.result.then(function () {
        $scope.certificateTable.reload();
      });
    };

    $scope.edit = function (certificateId) {
      var uibModalInstance = $uibModal.open({
        animation: true,
        controller: 'CertificateEditController',
        templateUrl: '/angular/certificates/certificate/edit.tpl.html',
        size: 'lg',
        backdrop: 'static',
        resolve: {
          editId: function () {
            return certificateId;
          }
        }
      });

      uibModalInstance.result.then(function () {
        $scope.certificateTable.reload();
      });
    };

    $scope.import = function () {
      var uibModalInstance = $uibModal.open({
        animation: true,
        controller: 'CertificateUploadController',
        templateUrl: '/angular/certificates/certificate/upload.tpl.html',
        size: 'lg',
        backdrop: 'static'
      });

      uibModalInstance.result.then(function () {
        $scope.certificateTable.reload();
      });
    };

    $scope.export = function (certificateId) {
      $uibModal.open({
        animation: true,
        controller: 'CertificateExportController',
        templateUrl: '/angular/certificates/certificate/export.tpl.html',
        size: 'lg',
        backdrop: 'static',
        resolve: {
          editId: function () {
            return certificateId;
          }
        }
      });
    };

     $scope.revoke = function (certificateId) {
      $uibModal.open({
        animation: true,
        controller: 'CertificateRevokeController',
        templateUrl: '/angular/certificates/certificate/revoke.tpl.html',
        size: 'lg',
        backdrop: 'static',
        resolve: {
          revokeId: function () {
            return certificateId;
          }
        }
      });
    };
  });
<|endoftext|>


<commit_before>var perfectSteak=function(div){

	var setup=function(div){
		var displayDiv = $("

");
		var inputTable = $("

");
		var inpTabHeader=$("
Time (s)	Side 1 (°C)	Side 2 (°C)
");
		var timeStep=15;

		var meatTemp=23;
		var thickness=8;

		var addButton;
		var subButton;
		var flipButton;
		var numRows=5;

		var addButtonFun=function(){
			addButton.on("click", function(){
				numRows++;
				inputTable.empty();
				buildTable(numRows);
				});
		};

		var subButtonFun=function(){
			subButton.on("click", function(){
			numRows--;
			inputTable.empty();
			buildTable(numRows);
			if (numRows == 1){
				inputTable.empty();
				buildTable(numRows);
				subButton.remove();
			}else{
				inputTable.empty();
				buildTable(numRows);
			}
			});
		};

		var timeFun=function(j){
			$("#row"+j+"time").change(function(){

				if (j==0){
					timeStep=parseInt($("#row"+j+"time").value);

				}
			})
		};

		var flipButtonFun=function(k){
			flipButton.on("click", function(){

				side1data=0
				side1data+=parseInt(parseFloat($('#inp1_'+k).val()))||0;
				side2data=parseInt(parseFloat($('#inp2_'+k).val()));
				$('#inp1_'+k).val(side2data);
				$('#inp2_'+k).val(side1data);

			})
		};

		timeFun(0);

		var data=[];

		//THIS FUNCTION IS CALLED WITHIN TABLE AND MAKES US SOME SHINY NICE DATA
		var buildData=function(numRows){
			data = [];
			for (var q=0; q<numRows; q++){
				//var currentTime=$("#row"+q+"time").val();
				var currentTime=timeStep*q;
				var currentSide1=$("#inp1_"+q).val()||23;
				var currentSide2=$("#inp2_"+q).val()||23;
				data.push([currentTime, currentSide1, currentSide2])
			}
		};

		var buildTable=function(n){
			var counter=0;
			inputTable.append(inpTabHeader);
			for(var i=0; i<n; i++){
				addButton=$("<button class='btn btn-mini' id='addButton'>+</button>");
				subButton=$("<button class='btn btn-mini' id='subButton'>-</button>");
				flipButton=$("<button class='btn btn-mini' id='flipButton"+i+"'>↔</button>");
				var row=$("

");
				var timeCol=$("<input type='text' value="+counter*timeStep+">	");
				var inp1=$("<input type='text' id='inp1_"+counter+"'>");
				var inp2=$("<input type='text' id='inp2_"+counter+"'>");
				var step1Col=$("</input>");
				step1Col.append(inp1);
				var step2Col=$("	");
				step2Col.append(inp2);
				step1Col.append(flipButton);
				row.append(timeCol, step1Col, step2Col);
				inputTable.append(row);
				if (counter == n-1){
					inputTable.append(addButton, subButton);
				}
				if (data.length==0){

					if (i == 0){
						inp1.val(23);
						inp2.val(23);
						//data.push([timeStep, 25, 25])
					} else{
						var iminus = i-1;
						inp1.val($("#inp1_"+iminus).val()||23);
						inp2.val($("#inp2_"+iminus).val()||23);
						//data.push([timeStep*i, $("#inp1_"+iminus).val(), $("#inp2_"+iminus).val()])
					}
				}else if (data.length<=n){

					for (var a=0; a<data.length; a++){
						inp1.val(data[a][1]);
						inp2.val(data[a][2]);
					}
					if (i==n){
						inp1.val(data[n-1][1]);
						inp2.val(data[n-1][2]);
					}
				}else if (data.length>n){

					for (var b=0; b<n; b++){
						inp1.val(data[b][1]);
						inp2.val(data[b][2])
					}
				}

				timeFun(counter);
				flipButtonFun(i);
				counter++;
			}

			addButtonFun();
			subButtonFun();
			buildData(n)
			console.log(data);
			//reset data
		};

		buildTable(1);

		displayDiv.append(GoButton);
		displayDiv.append(inputTable);
		div.append(displayDiv);
		var steak=[data[0][1]];
		for(var i=0;i<thickness*10;i++)
		{
			steak.push(meatTemp)
		}
		steak.push[data[0][2]]
		console.log(steak);
		var GoButton=$("<button class='btn' id='GoButton'>Lets get Cooking!</button>");
		GoButton.on("click",function(){calculate(data,steak)});
		displayDiv.append(GoButton);

	};
	return {setup:setup};
}();

//call setup when the document is ready
$(document).ready(function(){
    $('.perfectSteak').each(function(){
        perfectSteak.setup($(this));});
});<commit_msg>here is front end<commit_after>var perfectSteak=function(div){

	function Model(div){
		var meatTemp=25;
		var thickness=6;
		var data=[];
		var timeStep=15;
		var inputTable=$(".inputTable");

		var changeThickness=function(newVal){
			console.log(newVal)
			thickness=newVal;
			console.log(thickness);
		}

		var changeMeatTemp=function(newVal){
			meatTemp=newVal;
		}

		//OK THIS WORKS NOW
		var dataClear=function(){
			while(data.length>0){
				data.pop();
			}
		}

		//THIS ADDS AN ELEMENT TO THE DATA ARRAY
		var dataAdd=function(item){
			data.push(item);
		}

		//THIS IS FOR CHANGING THE ENTIRE DATA ARRAY
		var dataChange=function(array){
			data=array;
			for (var d=0; d<array.length; d++){
				data.push[array[d]]
			}
		}

		return {meatTemp:meatTemp, thickness:thickness, data:data, changeThickness:changeThickness, timeStep:timeStep, dataAdd:dataAdd,
		dataClear:dataClear, dataChange:dataChange, changeMeatTemp:changeMeatTemp}
	}

	function View(div, model){

		var inputTable = $("

");
		var displayDiv = $("

");
		displayDiv.append(inputTable);
		div.append(displayDiv);

		div.append
		var addButton;
		var subButton;
		var flipButton;
		var cookButton;

		var numRows=2;

		var buildTable=function(){
			var inpTabHeader=$("
Time (s)	Side 1 (°C)	Side 2 (°C)
");
			inputTable.append(inpTabHeader);
			var timeStep=model.timeStep;
			console.log("data="+model.data)
			var len=model.data.length;
			var newData=[]
			$("#cookButton").remove();

			for(var i=0; i<numRows; i++){
				var iminus=i-1;
				addButton=$("<button class='btn btn-mini' id='addButton'>+</button>");
				subButton=$("<button class='btn btn-mini' id='subButton'>-</button>");
				flipButton=$("<button class='btn btn-mini' id='flipButton"+i+"'>↔</button>");
				cookButton=$("<button class='btn' id='cookButton'>Cook!</button>");
				var row=$("

");
				var timeCol=$("<input type='text' value="+i*model.timeStep+">	");
				var inp1=$("<input type='text' id='inp1_"+i+"'>");
				var inp2=$("<input type='text' id='inp2_"+i+"'>");
				var step1Col=$("</input>");
				step1Col.append(inp1);
				var step2Col=$("	");
				step2Col.append(inp2);
				step1Col.append(flipButton);
				row.append(timeCol, step1Col, step2Col);
				inputTable.append(row);
				if (i == numRows-1){
					inputTable.append(addButton, subButton);
					displayDiv.append(cookButton);
				}
				if (len==0){
					console.log("len0")
					inp1.val(25);
					inp2.val(25);
					model.dataAdd([timeStep*i, $("#inp1_"+i).val(), $("#inp2_"+i).val()])
					console.log(model.data)
				} else if (i<=len){
					console.log("i<=len")
					console.log(model.data[i][1]);
					inp1.val(model.data[i][1]);
					inp2.val(model.data[i][2]);
					model.dataAdd([timeStep*i, $("#inp1_"+i).val(), $("#inp2_"+i).val()])
				} else{
					console.log("i>len")
					inp1.val(model.data[i-1][1]);
					inp2.val(model.data[i-1][2]);
					model.dataAdd([timeStep*i, $("#inp1_"+iminus).val(), $("#inp2_"+iminus).val()])
				}
				timeFun(i);
				flipButtonFun(i);
			}
			model.dataClear();
			addButtonFun();
			subButtonFun();

			cookButton.on("click", function(){
			console.log("clicked");
			model.dataClear();
			for (var e=0; e<numRows; e++){
				var curTime=model.timeStep*e;
				var cur1=$("#inp1_"+e).val();
				var cur2=$("#inp2_"+e).val();
				model.dataAdd([curTime, cur1, cur2]);
				model.changeMeatTemp($("#steakTemp").val())
				model.changeThickness($("#thicknessInp").val())
				console.log(model.data);
				console.log(model.thickness);
				console.log(model.meatTemp)
			}
			var steak = [model.data[0][1]]
			for(var m=0;m<model.thickness*10;m++)
			{steak.push(model.meatTemp)
			}
			steak.push(model.data[0][2]);
			calculate(model.data, steak)
			})
		};

		var addButtonFun=function(){
			addButton.on("click", function(){
				numRows++;
				$(".inputTable").empty();
				buildTable(numRows);
				});
		};

		var subButtonFun=function(){
			subButton.on("click", function(){
			numRows--;
			//$(".inputTable").empty();
			//buildTable(numRows);
			if (numRows == 1){
				$(".inputTable").empty();
				buildTable(numRows);
				subButton.remove();
			}else{
				$(".inputTable").empty();
				buildTable(numRows);
			}
			});
		};

		var timeFun=function(j){
			$("#row"+j+"time").change(function(){
				if (j==0){
					timeStep=parseInt($("#row"+j+"time").value);
				}
			})
		};

		var flipButtonFun=function(k){
			flipButton.on("click", function(){
				side1data=0
				side1data+=parseInt(parseFloat($('#inp1_'+k).val()))||0;
				side2data=parseInt(parseFloat($('#inp2_'+k).val()));
				$('#inp1_'+k).val(side2data);
				$('#inp2_'+k).val(side1data);
			})
		};

		return {buildTable: buildTable, addButtonFun:addButtonFun, subButtonFun:subButtonFun, flipButtonFun:flipButtonFun, timeFun:timeFun}

	}

	var setup=function(div){
		var model=Model();
		var view=View(div, model);

		//timeFun(0);

		view.buildTable();
		var thicknessInp=($("
<input type='text' id='thicknessInp'></input> Meat Thickness
"));
		var steakTemp=($("
<input type='text' id='steakTemp'></input>Meat Temperature
"));
		// thicknessInp.change(function(){
			// model.changethickness(thicknessInp.val());
		// })

		div.append(thicknessInp, steakTemp);
	};
	return {setup:setup};
}();

//call setup when the document is ready
$(document).ready(function(){
    $('.perfectSteak').each(function(){
        perfectSteak.setup($(this));});
});<|endoftext|>


<commit_before>import modal from '../../utils/modal';
import i18n from '../../utils/i18n';
import utterance from '../../utils/utterance';
import RSVP from 'rsvp';
import stashes from '../../utils/_stashes';
import { computed } from '@ember/object';

export default modal.ModalController.extend({
  opening: function() {
    this.set('lang', stashes.get('display_lang'));
  },
  locales: computed(function() {
    var list = i18n.locales_translated || ['en'];
    return list.map(function(loc) {
      var auto_translated = loc.match(/\*/);
      var loc = loc.replace(/\*/, '');
      var name = i18n.locales_localized[loc] || i18n.locales[loc] || loc;
      if(auto_translated) {
        name = name + " (auto-translated)";
      }
      return {
        name: name,
        id: loc
      };
    });
  }),
  actions: {
    update: function() {
      stashes.persist('display_lang', this.get('lang'));
      location.reload();
    },
  }
});
<commit_msg>i18n fixup<commit_after>import modal from '../../utils/modal';
import i18n from '../../utils/i18n';
import utterance from '../../utils/utterance';
import RSVP from 'rsvp';
import stashes from '../../utils/_stashes';
import { computed } from '@ember/object';

export default modal.ModalController.extend({
  opening: function() {
    this.set('lang', stashes.get('display_lang'));
  },
  locales: computed(function() {
    var list = i18n.locales_translated || ['en'];
    return list.map(function(loc) {
      var auto_translated = loc.match(/\*/);
      var loc = loc.replace(/\*/, '');
      var name = i18n.locales_localized[loc] || i18n.locales[loc] || loc;
      if(auto_translated) {
        name = name + " (auto-translated)";
      }
      return {
        name: name,
        id: loc
      };
    });
  }),
  actions: {
    update: function() {
      stashes.persist('display_lang', this.get('lang'));
      setTimeout(function() {
        location.reload();
      }, 1000);
    },
  }
});
<|endoftext|>


<commit_before>define(['ddt', 'knockout'], function(ddt, ko) {

    'use strict';

    var DATA_KEY = 'ddtObject';

    ko.bindingHandlers.ddt = {
        init: function(element, valueAccessor) {
            var table = new DDT.DragAndDropTable($(element));
            var value = ko.unwrap(valueAccessor());

            if (!value) {
                table.disable();
            }

            element.data(DATA_KEY, table);
        },

        update: function(element, valueAccessor) {
            var value = ko.unwrap(valueAccessor());
            var table = element.data(DATA_KEY);

            if (value) {
                table.enable();
            } else {
                table.disable();
            }
        }
    };

});<commit_msg>Fix the knockout binding<commit_after>define(['ddt', 'knockout'], function(ddt, ko) {

    'use strict';

    var DATA_KEY = 'ddtObject';

    ko.bindingHandlers.ddt = {
        init: function(element, valueAccessor) {
            var table = new ddt.DragAndDropTable($(element));
            var value = ko.unwrap(valueAccessor());

            if (!value) {
                table.disable();
            }

            ko.utils.domData.set(element, DATA_KEY, table);
        },

        update: function(element, valueAccessor) {
            var value = ko.unwrap(valueAccessor());
            var table = ko.utils.domData.get(element, DATA_KEY);

            if (value) {
                table.enable();
            } else {
                table.disable();
            }
        }
    };

});<|endoftext|>


<commit_before>import { createSelector } from 'reselect'
import { ipcRenderer } from 'electron'
import db from 'store/db'

import { showNotification } from 'lib/utils/notifications'
import { btc } from 'lib/utils'

import { fetchBalance } from './balance'
import { setError } from './error'

// ------------------------------------
// Constants
// ------------------------------------
export const SEARCH_INVOICES = 'SEARCH_INVOICES'

export const SET_INVOICE = 'SET_INVOICE'

export const GET_INVOICE = 'GET_INVOICE'
export const RECEIVE_INVOICE = 'RECEIVE_INVOICE'
export const RECEIVE_FORM_INVOICE = 'RECEIVE_FORM_INVOICE'

export const GET_INVOICES = 'GET_INVOICES'
export const RECEIVE_INVOICES = 'RECEIVE_INVOICES'

export const SEND_INVOICE = 'SEND_INVOICE'
export const INVOICE_SUCCESSFUL = 'INVOICE_SUCCESSFUL'

export const INVOICE_FAILED = 'INVOICE_FAILED'

export const UPDATE_INVOICE = 'UPDATE_INVOICE'

// ------------------------------------
// Helpers
// ------------------------------------

// Decorate invoice object with custom/computed properties.
const decorateInvoice = invoice => {
  invoice.finalAmount = invoice.amt_paid_sat ? invoice.amt_paid_sat : invoice.value
  return invoice
}

// ------------------------------------
// Actions
// ------------------------------------
export function searchInvoices(invoicesSearchText) {
  return {
    type: SEARCH_INVOICES,
    invoicesSearchText
  }
}

export function setInvoice(invoice) {
  return {
    type: SET_INVOICE,
    invoice
  }
}

export function getInvoice() {
  return {
    type: GET_INVOICE
  }
}

export function receiveInvoice(invoice) {
  return {
    type: RECEIVE_INVOICE,
    invoice
  }
}

export function getInvoices() {
  return {
    type: GET_INVOICES
  }
}

export function sendInvoice() {
  return {
    type: SEND_INVOICE
  }
}

// Send IPC event for a specific invoice
export const fetchInvoice = payreq => dispatch => {
  dispatch(getInvoice())
  ipcRenderer.send('lnd', { msg: 'invoice', data: { payreq } })
}

// Send IPC event for invoices
export const fetchInvoices = () => dispatch => {
  dispatch(getInvoices())
  ipcRenderer.send('lnd', { msg: 'invoices' })
}

// Receive IPC event for invoices
export const receiveInvoices = (event, { invoices }) => dispatch => {
  invoices.forEach(decorateInvoice)
  dispatch({ type: RECEIVE_INVOICES, invoices })
}

// Send IPC event for creating an invoice
export const createInvoice = (amount, currency, memo) => async dispatch => {
  // backend needs value in satoshis no matter what currency we are using
  const value = btc.convert(currency, 'sats', amount)

  dispatch(sendInvoice())

  // Grab the activeWallet type from our local store. If the active connection type is local (light clients using
  // neutrino) we will have to flag private as true when creating this invoice. All light cliets open private channels
  // (both manual and autopilot ones). In order for these clients to receive money through these channels the invoices
  // need to come with routing hints for private channels
  const activeWallet = await db.settings.get({ key: 'activeWallet' })
  const wallet = db.wallets.get({ id: activeWallet.value })

  ipcRenderer.send('lnd', {
    msg: 'createInvoice',
    data: { value, memo, private: wallet.type === 'local' }
  })
}

// Receive IPC event for newly created invoice
export const createdInvoice = (event, invoice) => dispatch => {
  decorateInvoice(invoice)

  // Add new invoice to invoices list
  dispatch({ type: INVOICE_SUCCESSFUL, invoice })

  // Set current invoice to newly created invoice.
  dispatch(setInvoice(invoice.payment_request))
}

export const invoiceFailed = (event, { error }) => dispatch => {
  dispatch({ type: INVOICE_FAILED })
  dispatch(setError(error))
}

// Listen for invoice updates pushed from backend from subscribeToInvoices
export const invoiceUpdate = (event, { invoice }) => dispatch => {
  decorateInvoice(invoice)

  dispatch({ type: UPDATE_INVOICE, invoice })

  // Fetch new balance
  dispatch(fetchBalance())

  if (invoice.settled) {
    // HTML 5 desktop notification for the invoice update
    const notifTitle = "You've been Zapped"
    const notifBody = 'Congrats, someone just paid an invoice of yours'

    showNotification(notifTitle, notifBody)
  }
}
// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [SEARCH_INVOICES]: (state, { invoicesSearchText }) => ({ ...state, invoicesSearchText }),

  [SET_INVOICE]: (state, { invoice }) => ({ ...state, invoice }),

  [GET_INVOICE]: state => ({ ...state, invoiceLoading: true }),
  [RECEIVE_INVOICE]: (state, { invoice }) => ({ ...state, invoiceLoading: false, invoice }),
  [RECEIVE_FORM_INVOICE]: state => ({ ...state, invoiceLoading: false }),

  [GET_INVOICES]: state => ({ ...state, invoiceLoading: true }),
  [RECEIVE_INVOICES]: (state, { invoices }) => ({ ...state, invoiceLoading: false, invoices }),

  [SEND_INVOICE]: state => ({ ...state, invoiceLoading: true }),
  [INVOICE_SUCCESSFUL]: (state, { invoice }) => ({
    ...state,
    invoiceLoading: false,
    invoices: [...state.invoices, invoice]
  }),
  [INVOICE_FAILED]: state => ({ ...state, invoiceLoading: false, data: null }),

  [UPDATE_INVOICE]: (state, action) => {
    let isNew = true
    const updatedInvoices = state.invoices.map(invoice => {
      if (invoice.r_hash.toString('hex') === action.invoice.r_hash.toString('hex')) {
        isNew = false
        return {
          ...invoice,
          ...action.invoice
        }
      }
      return invoice
    })

    if (isNew) {
      updatedInvoices.push(action.invoice)
    }

    return { ...state, invoices: updatedInvoices }
  }
}

const invoiceSelectors = {}
const invoiceSelector = state => state.invoice.invoice
const invoicesSelector = state => state.invoice.invoices
const invoicesSearchTextSelector = state => state.invoice.invoicesSearchText

invoiceSelectors.invoiceModalOpen = createSelector(invoiceSelector, invoice => !!invoice)

invoiceSelectors.invoices = createSelector(
  invoicesSelector,
  invoicesSearchTextSelector,
  (invoices, invoicesSearchText) =>
    invoices.filter(invoice => invoice.memo && invoice.memo.includes(invoicesSearchText))
)

export { invoiceSelectors }

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
  invoiceLoading: false,
  invoices: [],
  invoice: null,
  invoicesSearchText: '',
  data: {},
  formInvoice: {
    payreq: '',
    r_hash: '',
    amount: '0'
  }
}

export default function invoiceReducer(state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}
<commit_msg>fix(invoice): include route hints for local nodes<commit_after>import { createSelector } from 'reselect'
import { ipcRenderer } from 'electron'
import db from 'store/db'

import { showNotification } from 'lib/utils/notifications'
import { btc } from 'lib/utils'

import { fetchBalance } from './balance'
import { setError } from './error'

// ------------------------------------
// Constants
// ------------------------------------
export const SEARCH_INVOICES = 'SEARCH_INVOICES'

export const SET_INVOICE = 'SET_INVOICE'

export const GET_INVOICE = 'GET_INVOICE'
export const RECEIVE_INVOICE = 'RECEIVE_INVOICE'
export const RECEIVE_FORM_INVOICE = 'RECEIVE_FORM_INVOICE'

export const GET_INVOICES = 'GET_INVOICES'
export const RECEIVE_INVOICES = 'RECEIVE_INVOICES'

export const SEND_INVOICE = 'SEND_INVOICE'
export const INVOICE_SUCCESSFUL = 'INVOICE_SUCCESSFUL'

export const INVOICE_FAILED = 'INVOICE_FAILED'

export const UPDATE_INVOICE = 'UPDATE_INVOICE'

// ------------------------------------
// Helpers
// ------------------------------------

// Decorate invoice object with custom/computed properties.
const decorateInvoice = invoice => {
  invoice.finalAmount = invoice.amt_paid_sat ? invoice.amt_paid_sat : invoice.value
  return invoice
}

// ------------------------------------
// Actions
// ------------------------------------
export function searchInvoices(invoicesSearchText) {
  return {
    type: SEARCH_INVOICES,
    invoicesSearchText
  }
}

export function setInvoice(invoice) {
  return {
    type: SET_INVOICE,
    invoice
  }
}

export function getInvoice() {
  return {
    type: GET_INVOICE
  }
}

export function receiveInvoice(invoice) {
  return {
    type: RECEIVE_INVOICE,
    invoice
  }
}

export function getInvoices() {
  return {
    type: GET_INVOICES
  }
}

export function sendInvoice() {
  return {
    type: SEND_INVOICE
  }
}

// Send IPC event for a specific invoice
export const fetchInvoice = payreq => dispatch => {
  dispatch(getInvoice())
  ipcRenderer.send('lnd', { msg: 'invoice', data: { payreq } })
}

// Send IPC event for invoices
export const fetchInvoices = () => dispatch => {
  dispatch(getInvoices())
  ipcRenderer.send('lnd', { msg: 'invoices' })
}

// Receive IPC event for invoices
export const receiveInvoices = (event, { invoices }) => dispatch => {
  invoices.forEach(decorateInvoice)
  dispatch({ type: RECEIVE_INVOICES, invoices })
}

// Send IPC event for creating an invoice
export const createInvoice = (amount, currency, memo) => async dispatch => {
  // backend needs value in satoshis no matter what currency we are using
  const value = btc.convert(currency, 'sats', amount)

  dispatch(sendInvoice())

  // Grab the activeWallet type from our local store. If the active connection type is local (light clients using
  // neutrino) we will have to flag private as true when creating this invoice. All light cliets open private channels
  // (both manual and autopilot ones). In order for these clients to receive money through these channels the invoices
  // need to come with routing hints for private channels
  const activeWallet = await db.settings.get({ key: 'activeWallet' })
  const wallet = await db.wallets.get({ id: activeWallet.value })

  ipcRenderer.send('lnd', {
    msg: 'createInvoice',
    data: { value, memo, private: wallet.type === 'local' }
  })
}

// Receive IPC event for newly created invoice
export const createdInvoice = (event, invoice) => dispatch => {
  decorateInvoice(invoice)

  // Add new invoice to invoices list
  dispatch({ type: INVOICE_SUCCESSFUL, invoice })

  // Set current invoice to newly created invoice.
  dispatch(setInvoice(invoice.payment_request))
}

export const invoiceFailed = (event, { error }) => dispatch => {
  dispatch({ type: INVOICE_FAILED })
  dispatch(setError(error))
}

// Listen for invoice updates pushed from backend from subscribeToInvoices
export const invoiceUpdate = (event, { invoice }) => dispatch => {
  decorateInvoice(invoice)

  dispatch({ type: UPDATE_INVOICE, invoice })

  // Fetch new balance
  dispatch(fetchBalance())

  if (invoice.settled) {
    // HTML 5 desktop notification for the invoice update
    const notifTitle = "You've been Zapped"
    const notifBody = 'Congrats, someone just paid an invoice of yours'

    showNotification(notifTitle, notifBody)
  }
}
// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [SEARCH_INVOICES]: (state, { invoicesSearchText }) => ({ ...state, invoicesSearchText }),

  [SET_INVOICE]: (state, { invoice }) => ({ ...state, invoice }),

  [GET_INVOICE]: state => ({ ...state, invoiceLoading: true }),
  [RECEIVE_INVOICE]: (state, { invoice }) => ({ ...state, invoiceLoading: false, invoice }),
  [RECEIVE_FORM_INVOICE]: state => ({ ...state, invoiceLoading: false }),

  [GET_INVOICES]: state => ({ ...state, invoiceLoading: true }),
  [RECEIVE_INVOICES]: (state, { invoices }) => ({ ...state, invoiceLoading: false, invoices }),

  [SEND_INVOICE]: state => ({ ...state, invoiceLoading: true }),
  [INVOICE_SUCCESSFUL]: (state, { invoice }) => ({
    ...state,
    invoiceLoading: false,
    invoices: [...state.invoices, invoice]
  }),
  [INVOICE_FAILED]: state => ({ ...state, invoiceLoading: false, data: null }),

  [UPDATE_INVOICE]: (state, action) => {
    let isNew = true
    const updatedInvoices = state.invoices.map(invoice => {
      if (invoice.r_hash.toString('hex') === action.invoice.r_hash.toString('hex')) {
        isNew = false
        return {
          ...invoice,
          ...action.invoice
        }
      }
      return invoice
    })

    if (isNew) {
      updatedInvoices.push(action.invoice)
    }

    return { ...state, invoices: updatedInvoices }
  }
}

const invoiceSelectors = {}
const invoiceSelector = state => state.invoice.invoice
const invoicesSelector = state => state.invoice.invoices
const invoicesSearchTextSelector = state => state.invoice.invoicesSearchText

invoiceSelectors.invoiceModalOpen = createSelector(invoiceSelector, invoice => !!invoice)

invoiceSelectors.invoices = createSelector(
  invoicesSelector,
  invoicesSearchTextSelector,
  (invoices, invoicesSearchText) =>
    invoices.filter(invoice => invoice.memo && invoice.memo.includes(invoicesSearchText))
)

export { invoiceSelectors }

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
  invoiceLoading: false,
  invoices: [],
  invoice: null,
  invoicesSearchText: '',
  data: {},
  formInvoice: {
    payreq: '',
    r_hash: '',
    amount: '0'
  }
}

export default function invoiceReducer(state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}
<|endoftext|>