<commit_before>var jsdom = require('jsdom');
var Path = require('path');
var URL = require('url');
var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var Step = require('step');
var format = require('util').format;

var notemplate = module.exports = new EventEmitter();

var views = Object.create(null);

// keep that in memory
var jquery = fs.readFileSync(Path.join(Path.dirname(require.resolve('jquery-browser')), 'lib/jquery.js')).toString();

function load(path, cb) {
	var view = views[path] || { path: path };
	fs.stat(path, function(err, result) {
		if (err) return cb(err);
		if (view.mtime && result.mtime <= view.mtime) {
			view.hit = true;
			return cb(null, view);
		}
		fs.readFile(view.path, function(err, str) {
			if (err) return cb(err, view);
			view.window = getWindow(str);
			view.mtime = result.mtime;
			view.hit = false;
			views[view.path] = view;
			return cb(null, view);
		});
	});
}

function getWindow(str) {
	// create window with jquery
	var window = jsdom.jsdom(str, null, {			// default DOM, but eventually will be level2.html
		features: {
			FetchExternalResources: false,				// loaded depending on script[notemplate] attribute
			ProcessExternalResources: false,			// same
			MutationEvents: false,								// not needed
			QuerySelector: false									// not needed, we use jquery's bundled sizzle instead of jsdom's one.
		},
		xhtml: true
	}).createWindow();
	window.console = console;
	var tempfun = window.setTimeout;
	window.setTimeout = function(fun, tt) { fun(); };
	window.run(jquery);
	window.setTimeout = tempfun;
	jqueryPatches(window.jQuery);
	return window;
}

function loadScript(root, src, cb) {
	var url = URL.parse(src);
	if (url.hostname) return cb(format("express-notemplate error - cannot load remote script\n%s", src), null);
	var path = Path.join(root, url.pathname);
	Path.exists(path, function(exists) {
		if (exists) fs.readFile(path, cb);
		else cb(format("express-notemplate error - cannot find local script\n%s", path));
	});
}

function outer($nodes) {
	var ret = '';
	$nodes.each(function() {
		ret += this.outerHTML;
	});
	return ret;
}

function jqueryPatches($) {
	// jQuery monkey-patch
	$.buildFragmentOrig = $.buildFragment;
	$.buildFragment = function(args, nodes, scripts) {
		var r = $.buildFragmentOrig(args, nodes, scripts);
		// or else script.contentText will be run, this is a security risk
		if (Array.isArray(scripts)) scripts.length = 0;
		return r;
	};
}

function merge(view, options, callback) {
	var window = view.window;
	var $ = window.$;
	var document = window.document;
	document.replaceChild(view.root.cloneNode(true), document.documentElement);
	// global listeners
	notemplate.emit('data', view, options);
	// listeners from scripts loaded inside view.window
	$(document).triggerHandler('data', options);
	// global listeners
	notemplate.emit('render', view, options);
	var output;
	if (options.fragment) output = outer($(options.fragment)); // output selected nodes
	else output = document.doctype.toString() + "\n" + document.outerHTML; // outputs doctype because of jsdom bug
	// global listeners can modify output (sync)
	var obj = { output : output };
	notemplate.emit('output', obj, options);
	callback(null, obj.output);
}

notemplate.__express = function(filename, options, callback) {
	load(filename, function(err, view) {
		if (err) return callback(err);
		// the first time the DOM is ready is an event
		var window = view.window;
		if (!view.hit) {
			Step(function() {
				var group = this.group();
				window.$('script').each(function() {
					var script = this;
					var done = group();
					var att = script.attributes.notemplate;
					// default is notemplate="client"
					if (!att) return done();
					att = att.value;
					script.attributes.removeNamedItem('notemplate');
					// any other value is "client"
					if (att != "server" && att != "both") return done();
					var src = script.attributes.src;
					// html5 runs script content only when src is not set
					if (!src && script.textContent) window.run(script.textContent);
					if (att == "server") script.parentNode.remove
					if (!src) return done();
					loadScript(options.settings.statics || process.cwd() + '/public', src.value, function(err, textContent) {
						if (err) done(err);
						else {
							window.run(textContent.toString());
							done();
						}
					});
				});
			}, function(err) {
				if (err) console.error(err); // errors are not fatal
				notemplate.emit('ready', view, options);
				view.hit = true;
				view.root = window.document.documentElement;
				// all scripts have been loaded
				// now we can deal with data merging
				merge(view, options, callback);
			});
		} else merge(view, options, callback);
	});
};
<commit_msg>Run scripts in order after they are all loaded in disorder. Fix race condition.<commit_after>var jsdom = require('jsdom');
var Path = require('path');
var URL = require('url');
var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var Step = require('step');
var format = require('util').format;

var notemplate = module.exports = new EventEmitter();

var views = Object.create(null);

// keep that in memory
var jquery = fs.readFileSync(Path.join(Path.dirname(require.resolve('jquery-browser')), 'lib/jquery.js')).toString();

function load(path, cb) {
	var view = views[path] || { path: path };
	fs.stat(path, function(err, result) {
		if (err) return cb(err);
		if (view.mtime && result.mtime <= view.mtime) {
			view.hit = true;
			return cb(null, view);
		}
		fs.readFile(view.path, function(err, str) {
			if (err) return cb(err, view);
			view.window = getWindow(str);
			view.mtime = result.mtime;
			view.hit = false;
			views[view.path] = view;
			return cb(null, view);
		});
	});
}

function getWindow(str) {
	// create window with jquery
	var window = jsdom.jsdom(str, null, {			// default DOM, but eventually will be level2.html
		features: {
			FetchExternalResources: false,				// loaded depending on script[notemplate] attribute
			ProcessExternalResources: false,			// same
			MutationEvents: false,								// not needed
			QuerySelector: false									// not needed, we use jquery's bundled sizzle instead of jsdom's one.
		},
		xhtml: true
	}).createWindow();
	window.console = console;
	var tempfun = window.setTimeout;
	window.setTimeout = function(fun, tt) { fun(); };
	window.run(jquery);
	window.setTimeout = tempfun;
	jqueryPatches(window.jQuery);
	return window;
}

function loadScript(root, src, cb) {
	var url = URL.parse(src);
	if (url.hostname) return cb(format("express-notemplate error - cannot load remote script\n%s", src), null);
	var path = Path.join(root, url.pathname);
	Path.exists(path, function(exists) {
		if (exists) fs.readFile(path, cb);
		else cb(format("express-notemplate error - cannot find local script\n%s", path));
	});
}

function outer($nodes) {
	var ret = '';
	$nodes.each(function() {
		ret += this.outerHTML;
	});
	return ret;
}

function jqueryPatches($) {
	// jQuery monkey-patch
	$.buildFragmentOrig = $.buildFragment;
	$.buildFragment = function(args, nodes, scripts) {
		var r = $.buildFragmentOrig(args, nodes, scripts);
		// or else script.contentText will be run, this is a security risk
		if (Array.isArray(scripts)) scripts.length = 0;
		return r;
	};
}

function merge(view, options, callback) {
	var window = view.window;
	var $ = window.$;
	var document = window.document;
	document.replaceChild(view.root.cloneNode(true), document.documentElement);
	// global listeners
	notemplate.emit('data', view, options);
	// listeners from scripts loaded inside view.window
	$(document).triggerHandler('data', options);
	// global listeners
	notemplate.emit('render', view, options);
	var output;
	if (options.fragment) output = outer($(options.fragment)); // output selected nodes
	else output = document.doctype.toString() + "\n" + document.outerHTML; // outputs doctype because of jsdom bug
	// global listeners can modify output (sync)
	var obj = { output : output };
	notemplate.emit('output', obj, options);
	callback(null, obj.output);
}

notemplate.__express = function(filename, options, callback) {
	load(filename, function(err, view) {
		if (err) return callback(err);
		// the first time the DOM is ready is an event
		var window = view.window;
		if (!view.hit) {
			Step(function() {
				var group = this.group();
				window.$('script').each(function() {
					var script = this;
					var done = group();
					var att = script.attributes.notemplate;
					// default is notemplate="client"
					if (!att) return done();
					att = att.value;
					script.attributes.removeNamedItem('notemplate');
					// any other value is "client"
					if (att != "server" && att != "both") return done();
					var src = script.attributes.src;
					// html5 runs script content only when src is not set
					if (!src && script.textContent) window.run(script.textContent);
					if (att == "server") script.parentNode.remove
					if (!src) return done();
					loadScript(options.settings.statics || process.cwd() + '/public', src.value, done);
				});
			}, function(err, scripts) {
				if (err) console.error(err); // errors are not fatal
				scripts.forEach(function(txt) {
					if (txt) window.run(txt.toString());
				});
				notemplate.emit('ready', view, options);
				view.hit = true;
				view.root = window.document.documentElement;
				// all scripts have been loaded
				// now we can deal with data merging
				merge(view, options, callback);
			});
		} else merge(view, options, callback);
	});
};
<|endoftext|>


<commit_before>/* nrdb-local.js
 * Written by Dominic Shelton.
 * Provides functions for searching and storing a local copy of the netrunnedb card database.
 */
'use strict';
var request = require('request');
var schedule = require('node-schedule');
var FuzzySearch = require('fuzzysearch-js');
var LevenshteinFS = require('fuzzysearch-js/js/modules/LevenshteinFS');
var IndexOfFS = require('fuzzysearch-js/js/modules/IndexOfFS');
// var SiftFS = require('fuzzysearch-js/js/modules/Sift3FS');

var nrdbCardURL = 'https://netrunnerdb.com/api/2.0/public/cards';
var nrdbMWLURL = 'https://netrunnerdb.com/api/2.0/public/mwl';
var nrdbDeckURL = 'https://netrunnerdb.com/api/2.0/public/decklist/';
var nrdbDeckDisplayURL = 'https://netrunnerdb.com/en/decklist/';
var nrdbCardDisplayURL = 'https://netrunnerdb.com/en/card/';
var updateTime = process.env.UPDATE_TIME | 0;
var cards = {};
var fuzzySearch;
var scheduledUpdate;
var loadPromise;
var indexPromise;

module.exports = {
    init: init,
    getCardByTitle: getCardByTitle,
    getCardByCode: getCardByCode,
    getDecklist: getDecklist
};

// Cards in a decklist need a type and quantity, return them via promise.
function createDecklistCard (code, quantity) {
    return new Promise (function (resolve, reject) {
        getCardByCode(code).then(function (card) {
            var newCard = {};
            newCard.type = card.type;
            newCard.quantity = quantity;
            newCard.card = card;
            resolve(newCard);
        }, reject);
    });
}

function getDecklist (id) {
    return new Promise (function (resolve, reject) {
        request(nrdbDeckURL + id, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                return reject(error);
            }
            var decklist = {cards: {}};
            var deck = JSON.parse(body).data[0];
            var promises = [];
            decklist.name = deck.name;
            decklist.url = nrdbDeckDisplayURL + id;
            decklist.creator = deck.user_name;
            Object.keys(deck.cards).forEach((code) => {
                promises.push(createDecklistCard(code, deck.cards[code]));
            });
            Promise.all(promises).then(function (values) {
                values.forEach((card, i) => {
                    var type = card.card.type;
                    // The ICE type must be further broken into subtypes.
                    if (type === 'ICE') {
                        var typematch = card.card.subtype.match(/(Barrier|Code\ Gate|Sentry)/g);
                        if (!typematch) {
                            // The ice matched no standard type
                            type = 'Other';
                        } else if (typematch.length > 1) {
                            // The ice matched multiple types
                            type = 'Multi';
                        } else {
                            // The ice matched only a single type
                            type = typematch[0];
                        }
                    // Likewise Icebreakers must be separated from programs.
                    } else if (type === 'Program' && card.card.subtype
                            && card.card.subtype.match(/Icebreaker/)) {
                        type = 'Icebreaker';
                    }
                    // Add the card to the array of the correct type.
                    if (!decklist.cards[type]) {
                        decklist.cards[type] = [];
                    }
                    decklist.cards[type].push({
                        quantity: card.quantity, card: card.card
                    });
                });
                for (let type in decklist.cards) {
                    decklist.cards[type].sort(function (a, b) {
                        return a.card.title.localeCompare(b.card.title);
                    });
                }
                resolve(decklist);
            }, function (err) {
                console.log(err);
                reject (err)
            });
        });
    });
}

// Get a card by its nrdb code.
function getCardByCode (code) {
    return new Promise (function (resolve, reject) {
        loadPromise.then(function () {
            if (cards[code]) {
                resolve(cards[code]);
            } else {
                reject();
            }
        });
    });
}

// Search for a card by title text (fuzzy)
function getCardByTitle (text) {
    text = text.trim();
    return new Promise (function (resolve, reject) {
        if (text.length < 2) {
            resolve(undefined);
        } else {
            indexPromise.then(function (cardArray) {
                // Perform a fuzzy search for the card title.
                var results = fuzzySearch.search(text);
                if (results) {
                    // Output the fuzzysearch score for each card found on the console.
                    // console.info('\n\nSearching for ' + text);
                    // results.forEach((result, i) => {
                    //     var details = '';
                    //     result.details.forEach((detail) => {
                    //         details += detail.name + ': ' + detail.score + ' ';
                    //     });
                    //     console.info(i +': ' + result.value.title + ' score: ' + result.score + '\t\t' + details);
                    // });
                    // Return the best hit.
                    getCardByCode(results[0].value.code).then(resolve, reject);
                }else{
                    // Fall back to an acronym search.
                    var acronym = new RegExp(text.replace(/\W/g, '').replace(/(.)/g, '\\b$1.*?'), 'i');
                    var result = cardArray.find(function (e) {
                        return e.title.match(acronym);
                    });
                    if (result) {
                        getCardByCode(result.code).then(resolve, reject);
                    } else {
                        resolve(undefined);
                    }
                }
            });
        }
    });
}

// Initialise the local card array
function init (cardArray) {
    // Initialised as a promise so requests can be queued while the db is downloading.
    loadPromise = new Promise (function (resolve, reject) {
        if (cardArray) {
            console.log('Used passed card DB');
            resolve(cardArray);
        } else {
            // Load the json data from netrunnerdb.
            request(nrdbCardURL, function (error, response, body) {
                if (error || response.statusCode !== 200) {
                    console.error('Failed to fetch card DB: status', response.statusCode);
                    reject (error);
                } else {
                    cardArray = JSON.parse(body).data;
                    // Cancel any other updates as only the newest is necessary.
                    if (scheduledUpdate) {
                        scheduledUpdate.cancel();
                    }
                    console.log('Fetched card DB');
                    // Schedule the init method to be called again at the next midnight (NZ time)
                    var date = new Date();
                    if (date.getUTCHours() >= updateTime) {
                        date.setDate(date.getDate() + 1);
                    }
                    date.setUTCHours(updateTime);
                    date.setMinutes(0);
                    date.setSeconds(0);
                    date.setMilliseconds(0);
                    console.log('Scheduling DB update for ' + date.toUTCString());
                    scheduledUpdate = schedule.scheduleJob(date, init);
                    resolve(cardArray);
                }
            });
        }
    });
    // Create a new promise for the most-wanted-list
    var mwlPromise = new Promise ((resolve, reject) => {
        loadPromise.then(() => {
            // Fetch the Most Wanted List from the NetrunnerDB API
            request(nrdbMWLURL, (error, response, body) => {
                if (error || response.statusCode !== 200) {
                    console.error('Failed to fetch Most Wanted List: status', response.statusCode);
                    reject (error);
                } else {
                    // The nrdb api provides several possible MWL rules, choose the latest official one.
                    var rulesets = JSON.parse(body).data;
                    var latest = {name: "Empty List", start: "1970-01-01", cards: {}};
                    rulesets.forEach((rule) => {
                        if (rule.active) {
                            latest = rule;
                        }
                    });
                    console.log('Using Most Wanted List: ', latest.name);
                    resolve (latest.cards);
                }
            });
        });
    });
    // Create another promise for the search indexing of the db so that title searches can be queued.
    indexPromise = new Promise ((resolve, reject) => {
        Promise.all([loadPromise, mwlPromise]).then((results) => {
            var cardArray = results[0];
            var liteArray = [];
            var mwl = results[1];
            cardArray.forEach((card) => {
                var liteCard = {
                    title: card.title,
                    text: card.text,
                    code: card.code,
                    uniqueness: card.uniqueness,
                    side: card.side_code,
                    faction: card.faction_code,
                    type: card.type_code.replace(/^(ice|[a-z])/, (c) => {return c.toUpperCase();}),
                    subtype: card.keywords,
                    factioncost: card.faction_cost,
                    baselink: card.base_link,
                    cost: card.cost,
                    memoryunits: card.memory_cost,
                    strength: card.strength,
                    trash: card.trash_cost,
                    advancementcost: card.advancement_cost,
                    minimumdecksize: card.minimum_deck_size,
                    influencelimit: card.influence_limit,
                    agendapoints: card.agenda_points,
                    url: nrdbCardDisplayURL + card.code
                }
                if (liteCard.type !== "Identity" && liteCard.cost === null) {
                    liteCard.cost = "X";
                }
                // For some cards, the cost is actually a rez cost
                if (liteCard.type === 'Asset' || liteCard.type === 'Upgrade' || liteCard.type === 'ICE') {
                    liteCard.rezcost = liteCard.cost;
                    liteCard.cost = null;
                }
                // Add a mwl flag to each card on the Most Wanted List.
                if(mwl[card.code]) {
                    liteCard.mwl = mwl[card.code];
                }
                // Add the card to the card sparse array for quick lookup.
                cards[card.code] = liteCard;
                liteArray.push({title: card.title, code: card.code});
            });
            // Index the CardArray.
            fuzzySearch = new FuzzySearch(liteArray, {
                'caseSensitive': false,
                'termPath': 'title',
                'minimumScore': 200
            });
            fuzzySearch.addModule(IndexOfFS({'minTermLength': 3, 'maxIterations': 500, 'factor': 2}));
            // fuzzySearch.addModule(SiftFS({'maxDistanceTolerance': 4, 'factor': 1}));
            fuzzySearch.addModule(LevenshteinFS({'maxDistanceTolerance': 4, 'factor': 1}));
            console.log('Cards indexed');
            resolve(liteArray);
        }, (error) => {
            reject (error);
        });
    });
    return indexPromise;
}
<commit_msg>Add private decklist search. Will fallback if id not found<commit_after>/* nrdb-local.js
 * Written by Dominic Shelton.
 * Provides functions for searching and storing a local copy of the netrunnedb card database.
 */
'use strict';
var request = require('request');
var schedule = require('node-schedule');
var FuzzySearch = require('fuzzysearch-js');
var LevenshteinFS = require('fuzzysearch-js/js/modules/LevenshteinFS');
var IndexOfFS = require('fuzzysearch-js/js/modules/IndexOfFS');
// var SiftFS = require('fuzzysearch-js/js/modules/Sift3FS');

var nrdbCardURL = 'https://netrunnerdb.com/api/2.0/public/cards';
var nrdbMWLURL = 'https://netrunnerdb.com/api/2.0/public/mwl';
var nrdbDeckURL = 'https://netrunnerdb.com/api/2.0/public/decklist/';
var nrdbPrivateDeckURL = 'https://netrunnerdb.com/api/2.0/public/deck/';
var nrdbDeckDisplayURL = 'https://netrunnerdb.com/en/decklist/';
var nrdbPrivateDeckDisplayURL = 'https://netrunnerdb.com/en/deck/view/';
var nrdbCardDisplayURL = 'https://netrunnerdb.com/en/card/';
var updateTime = process.env.UPDATE_TIME | 0;
var cards = {};
var fuzzySearch;
var scheduledUpdate;
var loadPromise;
var indexPromise;

module.exports = {
    init: init,
    getCardByTitle: getCardByTitle,
    getCardByCode: getCardByCode,
    getDecklist: getDecklist
};

// Cards in a decklist need a type and quantity, return them via promise.
function createDecklistCard (code, quantity) {
    return new Promise (function (resolve, reject) {
        getCardByCode(code).then(function (card) {
            var newCard = {};
            newCard.type = card.type;
            newCard.quantity = quantity;
            newCard.card = card;
            resolve(newCard);
        }, reject);
    });
}

function getDecklist (id, privateDeck) {
    return new Promise (function (resolve, reject) {
        var requestURL = nrdbDeckURL + id;
        if (privateDeck) {
            requestURL = nrdbPrivateDeckURL + id;
        }
        request(requestURL, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                if (privateDeck) {
                    return reject();
                } else {
                    return getDecklist(id, true).then(resolve,reject);
                }
            }
            var decklist = {cards: {}};
            var deck = JSON.parse(body).data[0];
            var promises = [];
            if (privateDeck) {
                decklist.name = 'Private decklist';
                decklist.url = nrdbPrivateDeckDisplayURL + id;
            } else {
                decklist.name = deck.name;
                decklist.url = nrdbDeckDisplayURL + id;
            }
            decklist.creator = deck.user_name;
            Object.keys(deck.cards).forEach((code) => {
                promises.push(createDecklistCard(code, deck.cards[code]));
            });
            Promise.all(promises).then(function (values) {
                values.forEach((card, i) => {
                    var type = card.card.type;
                    // The ICE type must be further broken into subtypes.
                    if (type === 'ICE') {
                        var typematch = card.card.subtype.match(/(Barrier|Code\ Gate|Sentry)/g);
                        if (!typematch) {
                            // The ice matched no standard type
                            type = 'Other';
                        } else if (typematch.length > 1) {
                            // The ice matched multiple types
                            type = 'Multi';
                        } else {
                            // The ice matched only a single type
                            type = typematch[0];
                        }
                    // Likewise Icebreakers must be separated from programs.
                    } else if (type === 'Program' && card.card.subtype
                            && card.card.subtype.match(/Icebreaker/)) {
                        type = 'Icebreaker';
                    }
                    // Add the card to the array of the correct type.
                    if (!decklist.cards[type]) {
                        decklist.cards[type] = [];
                    }
                    decklist.cards[type].push({
                        quantity: card.quantity, card: card.card
                    });
                });
                for (let type in decklist.cards) {
                    decklist.cards[type].sort(function (a, b) {
                        return a.card.title.localeCompare(b.card.title);
                    });
                }
                resolve(decklist);
            }, function (err) {
                console.log(err);
                reject (err)
            });
        });
    });
}

// Get a card by its nrdb code.
function getCardByCode (code) {
    return new Promise (function (resolve, reject) {
        loadPromise.then(function () {
            if (cards[code]) {
                resolve(cards[code]);
            } else {
                reject();
            }
        });
    });
}

// Search for a card by title text (fuzzy)
function getCardByTitle (text) {
    text = text.trim();
    return new Promise (function (resolve, reject) {
        if (text.length < 2) {
            resolve(undefined);
        } else {
            indexPromise.then(function (cardArray) {
                // Perform a fuzzy search for the card title.
                var results = fuzzySearch.search(text);
                if (results) {
                    // Output the fuzzysearch score for each card found on the console.
                    // console.info('\n\nSearching for ' + text);
                    // results.forEach((result, i) => {
                    //     var details = '';
                    //     result.details.forEach((detail) => {
                    //         details += detail.name + ': ' + detail.score + ' ';
                    //     });
                    //     console.info(i +': ' + result.value.title + ' score: ' + result.score + '\t\t' + details);
                    // });
                    // Return the best hit.
                    getCardByCode(results[0].value.code).then(resolve, reject);
                }else{
                    // Fall back to an acronym search.
                    var acronym = new RegExp(text.replace(/\W/g, '').replace(/(.)/g, '\\b$1.*?'), 'i');
                    var result = cardArray.find(function (e) {
                        return e.title.match(acronym);
                    });
                    if (result) {
                        getCardByCode(result.code).then(resolve, reject);
                    } else {
                        resolve(undefined);
                    }
                }
            });
        }
    });
}

// Initialise the local card array
function init (cardArray) {
    // Initialised as a promise so requests can be queued while the db is downloading.
    loadPromise = new Promise (function (resolve, reject) {
        if (cardArray) {
            console.log('Used passed card DB');
            resolve(cardArray);
        } else {
            // Load the json data from netrunnerdb.
            request(nrdbCardURL, function (error, response, body) {
                if (error || response.statusCode !== 200) {
                    console.error('Failed to fetch card DB: status', response.statusCode);
                    reject (error);
                } else {
                    cardArray = JSON.parse(body).data;
                    // Cancel any other updates as only the newest is necessary.
                    if (scheduledUpdate) {
                        scheduledUpdate.cancel();
                    }
                    console.log('Fetched card DB');
                    // Schedule the init method to be called again at the next midnight (NZ time)
                    var date = new Date();
                    if (date.getUTCHours() >= updateTime) {
                        date.setDate(date.getDate() + 1);
                    }
                    date.setUTCHours(updateTime);
                    date.setMinutes(0);
                    date.setSeconds(0);
                    date.setMilliseconds(0);
                    console.log('Scheduling DB update for ' + date.toUTCString());
                    scheduledUpdate = schedule.scheduleJob(date, init);
                    resolve(cardArray);
                }
            });
        }
    });
    // Create a new promise for the most-wanted-list
    var mwlPromise = new Promise ((resolve, reject) => {
        loadPromise.then(() => {
            // Fetch the Most Wanted List from the NetrunnerDB API
            request(nrdbMWLURL, (error, response, body) => {
                if (error || response.statusCode !== 200) {
                    console.error('Failed to fetch Most Wanted List: status', response.statusCode);
                    reject (error);
                } else {
                    // The nrdb api provides several possible MWL rules, choose the latest official one.
                    var rulesets = JSON.parse(body).data;
                    var latest = {name: "Empty List", start: "1970-01-01", cards: {}};
                    rulesets.forEach((rule) => {
                        if (rule.active) {
                            latest = rule;
                        }
                    });
                    console.log('Using Most Wanted List: ', latest.name);
                    resolve (latest.cards);
                }
            });
        });
    });
    // Create another promise for the search indexing of the db so that title searches can be queued.
    indexPromise = new Promise ((resolve, reject) => {
        Promise.all([loadPromise, mwlPromise]).then((results) => {
            var cardArray = results[0];
            var liteArray = [];
            var mwl = results[1];
            cardArray.forEach((card) => {
                var liteCard = {
                    title: card.title,
                    text: card.text,
                    code: card.code,
                    uniqueness: card.uniqueness,
                    side: card.side_code,
                    faction: card.faction_code,
                    type: card.type_code.replace(/^(ice|[a-z])/, (c) => {return c.toUpperCase();}),
                    subtype: card.keywords,
                    factioncost: card.faction_cost,
                    baselink: card.base_link,
                    cost: card.cost,
                    memoryunits: card.memory_cost,
                    strength: card.strength,
                    trash: card.trash_cost,
                    advancementcost: card.advancement_cost,
                    minimumdecksize: card.minimum_deck_size,
                    influencelimit: card.influence_limit,
                    agendapoints: card.agenda_points,
                    url: nrdbCardDisplayURL + card.code
                }
                if (liteCard.type !== "Identity" && liteCard.cost === null) {
                    liteCard.cost = "X";
                }
                // For some cards, the cost is actually a rez cost
                if (liteCard.type === 'Asset' || liteCard.type === 'Upgrade' || liteCard.type === 'ICE') {
                    liteCard.rezcost = liteCard.cost;
                    liteCard.cost = null;
                }
                // Add a mwl flag to each card on the Most Wanted List.
                if(mwl[card.code]) {
                    liteCard.mwl = mwl[card.code];
                }
                // Add the card to the card sparse array for quick lookup.
                cards[card.code] = liteCard;
                liteArray.push({title: card.title, code: card.code});
            });
            // Index the CardArray.
            fuzzySearch = new FuzzySearch(liteArray, {
                'caseSensitive': false,
                'termPath': 'title',
                'minimumScore': 200
            });
            fuzzySearch.addModule(IndexOfFS({'minTermLength': 3, 'maxIterations': 500, 'factor': 2}));
            // fuzzySearch.addModule(SiftFS({'maxDistanceTolerance': 4, 'factor': 1}));
            fuzzySearch.addModule(LevenshteinFS({'maxDistanceTolerance': 4, 'factor': 1}));
            console.log('Cards indexed');
            resolve(liteArray);
        }, (error) => {
            reject (error);
        });
    });
    return indexPromise;
}
<|endoftext|>


<commit_before>'use strict';
var request = require('request');
var schedule = require('node-schedule');
var FuzzySearch = require('fuzzysearch-js');
var LevenshteinFS = require('fuzzysearch-js/js/modules/LevenshteinFS');
var IndexOfFS = require('fuzzysearch-js/js/modules/IndexOfFS');

var netrunnerCardURL = 'http://netrunnerdb.com/api/cards/';
var netrunnerDeckURL = 'http://netrunnerdb.com/api/decklist/';
var cards = {};
var fuzzySearch;
var scheduledUpdate;
var initPromise;
var indexPromise;

module.exports = {
    init: init,
    getCardByTitle: getCardByTitle,
    getCardByCode: getCardByCode,
    getDecklist: getDecklist
}

function createDecklistCard (code, quantity) {
    return new Promise (function (resolve, reject) {
        getCardByCode(code).then(function (card) {
            var newCard = {};
            newCard.type = card.type;
            newCard.quantity = quantity;
            newCard.card = card;
            resolve(newCard);
        }, reject);
    });
}

function getDecklist (id) {
    return new Promise (function (resolve, reject) {
        request(netrunnerDeckURL + id, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                return reject(error);
            }
            var decklist = {cards: {}};
            var deck = JSON.parse(body);
            var promises = [];
            decklist.name = deck.name;
            decklist.url = netrunnerDeckURL + id;
            decklist.creator = deck.username;
            for (var code in deck.cards) {
                promises.push(createDecklistCard(code, deck.cards[code]));
            }
            Promise.all(promises).then(function (values) {
                for (var i in values) {
                    if (!decklist.cards[values[i].type]) {
                        decklist.cards[values[i].type] = [];
                    }
                    decklist.cards[values[i].type].push({
                        quantity: values[i].quantity, card: values[i].card
                    });
                }
                resolve(decklist);
            }, function (err) {
                console.log(err);
                reject (err)
            });
        });
    });
}

function getCardByCode (code) {
    return new Promise (function (resolve, reject) {
        initPromise.then(function () {
            if (cards[code]) {
                resolve(cards[code]);
            } else {
                reject(new Error('No hits'));
            }
        });
    });
}

function getCardByTitle (text) {
    return new Promise (function (resolve, reject) {
        indexPromise.then(function (cardArray) {
            var results = fuzzySearch.search(text);
            if (results) {
                // for (var i in results) {
                //     var details = '   ';
                //     for (var j in results[i].details) {
                //         details += results[i].details[j].name + ': ' + results[i].details[j].score + ' ';
                //     }
                //     console.info(i +': ' + results[i].value.title + ' score: ' + results[i].score + '\t\t' + details);
                // }
                resolve(results[0].value);
            }else{
                var acronym = new RegExp(text.replace(/\W/g, '').replace(/(.)/g, '\\b$1.*?'), 'i');
                var result = cardArray.find(function (e) {
                    return e.title.match(acronym);
                });
                resolve(result);
            }
        });
    });
}

function init (cardArray) {
    initPromise = new Promise (function (resolve, reject) {
        if (cardArray) {
            console.log('Used passed card DB');
            resolve(cardArray);
        } else {
            request(netrunnerCardURL, function (error, response, body) {
                if (!error && response.statusCode === 200) {
                    var date = new Date(response.headers.expires);
                    cardArray = JSON.parse(body);
                    if (scheduledUpdate) {
                        scheduledUpdate.cancel();
                    }
                    console.log('Fetched card DB, valid until '+date);
                    scheduledUpdate = schedule.scheduleJob(date, init);
                    resolve(cardArray);
                } else {
                    console.error('Failed to fetch card DB: error %u', response.statusCode);
                    reject (error);
                }
            });
        }
    });
    indexPromise = new Promise (function (resolve, reject) {
        initPromise.then(function(cardArray) {
            for (var i = 0; i < cardArray.length; i++) {
                cards[cardArray[i].code] = cardArray[i];
            }
            fuzzySearch = new FuzzySearch(cardArray, {
                'caseSensitive': false,
                'termPath': 'title',
                'minimumScore': 200
            });
            fuzzySearch.addModule(LevenshteinFS({'maxDistanceTolerance': 6, 'factor': 1}));
            fuzzySearch.addModule(IndexOfFS({'minTermLength': 3, 'maxIterations': 500, 'factor': 2}));
            resolve(cardArray);
        }, function (error) {
            reject (error);
        });
    });
    return indexPromise;
}
<commit_msg>Decklist cards now sorted within categories<commit_after>'use strict';
var request = require('request');
var schedule = require('node-schedule');
var FuzzySearch = require('fuzzysearch-js');
var LevenshteinFS = require('fuzzysearch-js/js/modules/LevenshteinFS');
var IndexOfFS = require('fuzzysearch-js/js/modules/IndexOfFS');

var netrunnerCardURL = 'http://netrunnerdb.com/api/cards/';
var netrunnerDeckURL = 'http://netrunnerdb.com/api/decklist/';
var cards = {};
var fuzzySearch;
var scheduledUpdate;
var initPromise;
var indexPromise;

module.exports = {
    init: init,
    getCardByTitle: getCardByTitle,
    getCardByCode: getCardByCode,
    getDecklist: getDecklist
}

function createDecklistCard (code, quantity) {
    return new Promise (function (resolve, reject) {
        getCardByCode(code).then(function (card) {
            var newCard = {};
            newCard.type = card.type;
            newCard.quantity = quantity;
            newCard.card = card;
            resolve(newCard);
        }, reject);
    });
}

function getDecklist (id) {
    return new Promise (function (resolve, reject) {
        request(netrunnerDeckURL + id, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                return reject(error);
            }
            var decklist = {cards: {}};
            var deck = JSON.parse(body);
            var promises = [];
            decklist.name = deck.name;
            decklist.url = netrunnerDeckURL + id;
            decklist.creator = deck.username;
            for (var code in deck.cards) {
                promises.push(createDecklistCard(code, deck.cards[code]));
            }
            Promise.all(promises).then(function (values) {
                for (var i in values) {
                    if (!decklist.cards[values[i].type]) {
                        decklist.cards[values[i].type] = [];
                    }
                    decklist.cards[values[i].type].push({
                        quantity: values[i].quantity, card: values[i].card
                    });
                }
                for (var type in decklist.cards) {
                    decklist.cards[type].sort(function (a, b) {
                        return a.card.title.localeCompare(b.card.title);
                    });
                }
                resolve(decklist);
            }, function (err) {
                console.log(err);
                reject (err)
            });
        });
    });
}

function getCardByCode (code) {
    return new Promise (function (resolve, reject) {
        initPromise.then(function () {
            if (cards[code]) {
                resolve(cards[code]);
            } else {
                reject(new Error('No hits'));
            }
        });
    });
}

function getCardByTitle (text) {
    return new Promise (function (resolve, reject) {
        indexPromise.then(function (cardArray) {
            var results = fuzzySearch.search(text);
            if (results) {
                // for (var i in results) {
                //     var details = '   ';
                //     for (var j in results[i].details) {
                //         details += results[i].details[j].name + ': ' + results[i].details[j].score + ' ';
                //     }
                //     console.info(i +': ' + results[i].value.title + ' score: ' + results[i].score + '\t\t' + details);
                // }
                resolve(results[0].value);
            }else{
                var acronym = new RegExp(text.replace(/\W/g, '').replace(/(.)/g, '\\b$1.*?'), 'i');
                var result = cardArray.find(function (e) {
                    return e.title.match(acronym);
                });
                resolve(result);
            }
        });
    });
}

function init (cardArray) {
    initPromise = new Promise (function (resolve, reject) {
        if (cardArray) {
            console.log('Used passed card DB');
            resolve(cardArray);
        } else {
            request(netrunnerCardURL, function (error, response, body) {
                if (!error && response.statusCode === 200) {
                    var date = new Date(response.headers.expires);
                    cardArray = JSON.parse(body);
                    if (scheduledUpdate) {
                        scheduledUpdate.cancel();
                    }
                    console.log('Fetched card DB, valid until '+date);
                    scheduledUpdate = schedule.scheduleJob(date, init);
                    resolve(cardArray);
                } else {
                    console.error('Failed to fetch card DB: error %u', response.statusCode);
                    reject (error);
                }
            });
        }
    });
    indexPromise = new Promise (function (resolve, reject) {
        initPromise.then(function(cardArray) {
            for (var i = 0; i < cardArray.length; i++) {
                cards[cardArray[i].code] = cardArray[i];
            }
            fuzzySearch = new FuzzySearch(cardArray, {
                'caseSensitive': false,
                'termPath': 'title',
                'minimumScore': 200
            });
            fuzzySearch.addModule(LevenshteinFS({'maxDistanceTolerance': 6, 'factor': 1}));
            fuzzySearch.addModule(IndexOfFS({'minTermLength': 3, 'maxIterations': 500, 'factor': 2}));
            resolve(cardArray);
        }, function (error) {
            reject (error);
        });
    });
    return indexPromise;
}
<|endoftext|>


<commit_before>'use strict';
var request = require('request');
var schedule = require('node-schedule');
var FuzzySearch = require('fuzzysearch-js');
var LevenshteinFS = require('fuzzysearch-js/js/modules/LevenshteinFS');
var IndexOfFS = require('fuzzysearch-js/js/modules/IndexOfFS');

var netrunnerCardURL = 'http://netrunnerdb.com/api/cards/';
var netrunnerDeckURL = 'http://netrunnerdb.com/api/decklist/';
var cards = {};
var fuzzySearch;
var scheduledUpdate;
var initPromise;
var indexPromise;

module.exports = {
    init: init,
    getCardByTitle: getCardByTitle,
    getCardByCode: getCardByCode,
    getDecklist: getDecklist
}

function createDecklistCard (code, quantity) {
    return new Promise (function (resolve, reject) {
        getCardByCode(code).then(function (card) {
            var newCard = {};
            newCard.type = card.type;
            newCard.quantity = quantity;
            newCard.card = card;
            resolve(newCard);
        }, reject);
    });
}

function getDecklist (id) {
    return new Promise (function (resolve, reject) {
        request(netrunnerDeckURL + id, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                return reject(error);
            }
            var decklist = {cards: {}};
            var deck = JSON.parse(body);
            var promises = [];
            decklist.name = deck.name;
            decklist.url = netrunnerDeckURL + id;
            decklist.creator = deck.username;
            for (var code in deck.cards) {
                promises.push(createDecklistCard(code, deck.cards[code]));
            }
            Promise.all(promises).then(function (values) {
                for (var i in values) {
                    if (!decklist.cards[values[i].type]) {
                        decklist.cards[values[i].type] = [];
                    }
                    decklist.cards[values[i].type].push({
                        quantity: values[i].quantity, card: values[i].card
                    });
                }
                for (var type in decklist.cards) {
                    decklist.cards[type].sort(function (a, b) {
                        return a.card.title.localeCompare(b.card.title);
                    });
                }
                resolve(decklist);
            }, function (err) {
                console.log(err);
                reject (err)
            });
        });
    });
}

function getCardByCode (code) {
    return new Promise (function (resolve, reject) {
        initPromise.then(function () {
            if (cards[code]) {
                resolve(cards[code]);
            } else {
                reject(new Error('No hits'));
            }
        });
    });
}

function getCardByTitle (text) {
    return new Promise (function (resolve, reject) {
        indexPromise.then(function (cardArray) {
            var results = fuzzySearch.search(text);
            if (results) {
                // for (var i in results) {
                //     var details = '   ';
                //     for (var j in results[i].details) {
                //         details += results[i].details[j].name + ': ' + results[i].details[j].score + ' ';
                //     }
                //     console.info(i +': ' + results[i].value.title + ' score: ' + results[i].score + '\t\t' + details);
                // }
                resolve(results[0].value);
            }else{
                var acronym = new RegExp(text.replace(/\W/g, '').replace(/(.)/g, '\\b$1.*?'), 'i');
                var result = cardArray.find(function (e) {
                    return e.title.match(acronym);
                });
                resolve(result);
            }
        });
    });
}

function init (cardArray) {
    initPromise = new Promise (function (resolve, reject) {
        if (cardArray) {
            console.log('Used passed card DB');
            resolve(cardArray);
        } else {
            request(netrunnerCardURL, function (error, response, body) {
                if (!error && response.statusCode === 200) {
                    var date = new Date(response.headers.expires);
                    cardArray = JSON.parse(body);
                    if (scheduledUpdate) {
                        scheduledUpdate.cancel();
                    }
                    console.log('Fetched card DB, valid until '+date);
                    scheduledUpdate = schedule.scheduleJob(date, init);
                    resolve(cardArray);
                } else {
                    console.error('Failed to fetch card DB: error %u', response.statusCode);
                    reject (error);
                }
            });
        }
    });
    indexPromise = new Promise (function (resolve, reject) {
        initPromise.then(function(cardArray) {
            for (var i = 0; i < cardArray.length; i++) {
                cards[cardArray[i].code] = cardArray[i];
            }
            fuzzySearch = new FuzzySearch(cardArray, {
                'caseSensitive': false,
                'termPath': 'title',
                'minimumScore': 200
            });
            fuzzySearch.addModule(LevenshteinFS({'maxDistanceTolerance': 6, 'factor': 1}));
            fuzzySearch.addModule(IndexOfFS({'minTermLength': 3, 'maxIterations': 500, 'factor': 2}));
            resolve(cardArray);
        }, function (error) {
            reject (error);
        });
    });
    return indexPromise;
}
<commit_msg>Changed decklist link url to display not API URL<commit_after>'use strict';
var request = require('request');
var schedule = require('node-schedule');
var FuzzySearch = require('fuzzysearch-js');
var LevenshteinFS = require('fuzzysearch-js/js/modules/LevenshteinFS');
var IndexOfFS = require('fuzzysearch-js/js/modules/IndexOfFS');

var netrunnerCardURL = 'http://netrunnerdb.com/api/cards/';
var netrunnerDeckURL = 'http://netrunnerdb.com/api/decklist/';
var netrunnerDeckDisplayURL = 'http://netrunnerdb.com/en/decklist/';
var cards = {};
var fuzzySearch;
var scheduledUpdate;
var initPromise;
var indexPromise;

module.exports = {
    init: init,
    getCardByTitle: getCardByTitle,
    getCardByCode: getCardByCode,
    getDecklist: getDecklist
}

function createDecklistCard (code, quantity) {
    return new Promise (function (resolve, reject) {
        getCardByCode(code).then(function (card) {
            var newCard = {};
            newCard.type = card.type;
            newCard.quantity = quantity;
            newCard.card = card;
            resolve(newCard);
        }, reject);
    });
}

function getDecklist (id) {
    return new Promise (function (resolve, reject) {
        request(netrunnerDeckURL + id, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                return reject(error);
            }
            var decklist = {cards: {}};
            var deck = JSON.parse(body);
            var promises = [];
            decklist.name = deck.name;
            decklist.url = netrunnerDeckDisplayURL + id;
            decklist.creator = deck.username;
            for (var code in deck.cards) {
                promises.push(createDecklistCard(code, deck.cards[code]));
            }
            Promise.all(promises).then(function (values) {
                for (var i in values) {
                    if (!decklist.cards[values[i].type]) {
                        decklist.cards[values[i].type] = [];
                    }
                    decklist.cards[values[i].type].push({
                        quantity: values[i].quantity, card: values[i].card
                    });
                }
                for (var type in decklist.cards) {
                    decklist.cards[type].sort(function (a, b) {
                        return a.card.title.localeCompare(b.card.title);
                    });
                }
                resolve(decklist);
            }, function (err) {
                console.log(err);
                reject (err)
            });
        });
    });
}

function getCardByCode (code) {
    return new Promise (function (resolve, reject) {
        initPromise.then(function () {
            if (cards[code]) {
                resolve(cards[code]);
            } else {
                reject(new Error('No hits'));
            }
        });
    });
}

function getCardByTitle (text) {
    return new Promise (function (resolve, reject) {
        indexPromise.then(function (cardArray) {
            var results = fuzzySearch.search(text);
            if (results) {
                // for (var i in results) {
                //     var details = '   ';
                //     for (var j in results[i].details) {
                //         details += results[i].details[j].name + ': ' + results[i].details[j].score + ' ';
                //     }
                //     console.info(i +': ' + results[i].value.title + ' score: ' + results[i].score + '\t\t' + details);
                // }
                resolve(results[0].value);
            }else{
                var acronym = new RegExp(text.replace(/\W/g, '').replace(/(.)/g, '\\b$1.*?'), 'i');
                var result = cardArray.find(function (e) {
                    return e.title.match(acronym);
                });
                resolve(result);
            }
        });
    });
}

function init (cardArray) {
    initPromise = new Promise (function (resolve, reject) {
        if (cardArray) {
            console.log('Used passed card DB');
            resolve(cardArray);
        } else {
            request(netrunnerCardURL, function (error, response, body) {
                if (!error && response.statusCode === 200) {
                    var date = new Date(response.headers.expires);
                    cardArray = JSON.parse(body);
                    if (scheduledUpdate) {
                        scheduledUpdate.cancel();
                    }
                    console.log('Fetched card DB, valid until '+date);
                    scheduledUpdate = schedule.scheduleJob(date, init);
                    resolve(cardArray);
                } else {
                    console.error('Failed to fetch card DB: error %u', response.statusCode);
                    reject (error);
                }
            });
        }
    });
    indexPromise = new Promise (function (resolve, reject) {
        initPromise.then(function(cardArray) {
            for (var i = 0; i < cardArray.length; i++) {
                cards[cardArray[i].code] = cardArray[i];
            }
            fuzzySearch = new FuzzySearch(cardArray, {
                'caseSensitive': false,
                'termPath': 'title',
                'minimumScore': 200
            });
            fuzzySearch.addModule(LevenshteinFS({'maxDistanceTolerance': 6, 'factor': 1}));
            fuzzySearch.addModule(IndexOfFS({'minTermLength': 3, 'maxIterations': 500, 'factor': 2}));
            resolve(cardArray);
        }, function (error) {
            reject (error);
        });
    });
    return indexPromise;
}
<|endoftext|>


<commit_before>/*
*	numeric.ly
*	author: steve kaliski
*	feb 2012
*-----------------------------------------------------------------------------*/

var numeric = function(){

	//basic addition of vals in an array
	function addition(arr){
		var total = 0;
		for(var i = 0 ; i < arr.length ; i++){
			total = total + arr[i];
		}
		return total;
	}

	//subtract items in an array (descending)
	function subtraction(arr){
		var total = arr[arr.length - 1];
		for (var i = arr.length - 2; i >= 0; i--) {
			total -= arr[i];
		}
		return total;
	}

	//product of items in array
	function product(arr){
		var total = arr[0];
		for (var i = 1; i < arr.length; i++) {
			total = total * arr[i];
		};
		return total;
	}

	//greatest common denominator
	function gcd(num1, num2){
		var result;
		if(num1 > num2){
			for(i = 0 ; i <= num2 ; i++){
				if(num2%i == 0){
					if(num1%i == 0){
						result = i;
					}
				}
			}
			return result;
		}else if(num2 > num1){
			for(i = 0 ; i <= num2 ; i++){
				if(num1%i == 0){
					if(num2%i == 0){
						var result = i;
					}
				}
			}
			return result;
		}else{
			var result = num1*num2/num1;
			return result;
		}
	}

	//least common multiple
	function lcm(num1, num2){
		var result = Math.abs(num1*num2) / gcd(num1,num2);
		return result;
	}

	//evaluate function at val (func passed as a string)
	function evaluate(func, val){
		return parseFloat(eval("with(Math){var x = " + val + ";" + func+'};'));
	}

/*-----------------------------------------------------------------------------*/

	//a variety of calculus tools
	var calculus = function(){

		//function, point at which differentiation occurs (func passed as string)
		function pointDiff(func, point){
			var a = evaluate(func, point - .00001);
			var b = evaluate(func, point + .00001);
			return (b-a)/(.00002);
		}

		//calculate riemann integral (left hand) (func passed as string)
		function riemann(func, start, finish, n){
			var inc = (finish - start)/n;
			var result = 0;
			for (var i = start; i < finish; i+= inc) {
				result += (evaluate(func, i) * inc);
			}
			return result;
		}

		//estimate integral with adaptive simpson quadrature
		function simpsonDef(func, a, b){
			var c = (a + b) / 2;
			var d = Math.abs(b - a) / 6;
			return d * (evaluate(func, a) + 4 * evaluate(func, c) + evaluate(func, b));
		}
		function simpsonRecursive(func, a, b, eps, whole){
			var c = a + b;
			var left = simpsonDef(func, a, c);
			var right = simpsonDef(func, c, b);
			if(Math.abs(left + right - whole) <= 15 * eps){
				return left + right + (left + right - whole) / 15;
			}else{
				return simpsonRecursive(func, a, c, eps/2, left) + simpsonRecursive(f, c, b, eps/2, right);
			}
		}
		//execute this
		function adaptiveSimpson(func, a, b, eps){
			return simpsonRecursive(func, a, b, eps, simpsonDef(func, a, b));
		}

		//take the limit of a function (left, middle, or right)
		function limit(func, point, approach){
			if(approach == 'left'){
				return evaluate(func, point - .001);
			}else if(approach == 'right'){
				return evaluate(func, point + .001);
			}else if(approach == 'middle'){
				return (limit(func, point, 'left') + limit(func, point, 'right')) / 2;
			}else{
				return 'Error: approach not provided';
			}
		}

		//return taylor polynomial for function (func) at point (point) to degree (degree)
		function taylor(func, point, degree){

		}

		//integrate taylor polynomial for some func
		function polyIntegrate(func){
			var taylor = taylor(func, 0, 8);
		}

		return{
			pointDiff: pointDiff,
			riemann: riemann,
			adaptiveSimpson: adaptiveSimpson,
			limit: limit,
			taylor: taylor,
			polyIntegrate: polyIntegrate
		}
	}()

/*-----------------------------------------------------------------------------*/

	/*
	a variety of matrix analysis tools

	let's consider the matrix A:

	[1 2 3]
	[4 5 6]

	As input, set arg = [[1,2,3],[4,5,6]];

	Note: The "structure" of matrices may be neglected in some calculations,
	however in and transformation, mapping, etc, we recognize the proper
	format.
	*/
	var matrix = function(){

		//add two arrays (arrA and arrB)
		function addition(arrA, arrB){
			if((arrA.length == arrB.length) && (arrA[0].length == arrB[0].length)){
				var result = new Array(arrA.length);
				for (var i = 0; i < arrA.length; i++) {
					result[i] = new Array(arrA[i].length);
					for (var j = 0; j < arrA[i].length; j++) {
						result[i][j] = arrA[i][j] + arrB[i][j];
					}
				}
				return result;
			}else{
				return 'Error: Array mismatch';
			}
		}

		//multiple array (arr) by a scalar (val)
		function scalar(arr,val){
			for (var i = 0; i < arr.length; i++) {
				for (var j = 0; j < arr[i].length; j++) {
					arr[i][j] = val * arr[i][j];
				}
			}
			return arr;
		}

		//transpose array
		function transpose(arr){
			var result = new Array(arr[0].length);
			for (var i = 0; i < arr.length; i++) {
				arr[i] = new Array(arr.length);
				for(var j = 0 ; j < arr[i].length){
					arr[j][i] = arr[i][j];
				}
			}
		}
		//return identity matrix of dimension n x n
		function identity(n){
			var result = new Array(n);
			for(var i = 0 ; i < n ; i++){
				arr[i] = new Array(n);
				for(var j = 0 ; j < n ; j++){
					if(i == j)
						arr[i][j] = 1;
				}
			}
		}

		//dot product
		function dotproduct(vectorA, vectorB){
			if(vectorA.length == vectorB.length){
				var result = 0;
				for(var i = 0 ; i < vectorA.length ; i++){
					result += vectorA[i]*vectorB[i];
				}
				return result;
			}else{
				return 'Error: Vector mismatch';
			}
		}

		//multiply two matrices
		function multiply(arrA, arrB){
			if(arrA[0].length == arrB.length){
				var result = new Array(arrA.length);
				for(var i = 0 ; i < arrA.length ; i++){
					result[i] = new Array(arrB[0].length);
				}
				var arrB_T = transpose(arrB);
				for(var i = 0 ; i < result.length ; i++){
					for(var j = 0 ; j < result[i].length ; j++){
						result[i][j] = dotproduct(arrA[i],arrB_T[j]);
					}
				}
				return result;
			}else{
				return 'Error: Array mismatch';
			}
		}

		//determinant, only for n x n -> 2 and 3
		function determinant(m){
			if((m.length == 2) && (m[0].length == 2)){
				return m[0][0]*m[1][1] - m[0][1]*m[1][0];
			}else if((m.length == 3) && (m[0].length == 3)){
				return m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + m[0][2]*m[1][0]*m[2][1] - m[0][2]*m[1][1]*m[2][0] - m[0][1]*m[1][0]*m[2][2] - m[0][0]*m[1][2]*m[2][1];
			}
		}

		return{
			addition: addition,
			scalar: scalar,
			transpose: transpose,
			identity: identity,
			dotproduct: dotproduct,
			multiply: multiply
		}
	}()

/*-----------------------------------------------------------------------------*/

	//collection of prime associated tools
	var prime = function(){

		//standard prime evaluation (no consideration towards efficiency)
		function simple(val){
			if(val == 1)
				return false;
			else if(val == 2)
				return true;
			else if(val != null){
				start = 2;
				result = true;
				while(start < val){
					if(val % start == 0){
						result = false;
						break;
					}else{
						start++;
					}
				}
				return result;
			}
		}

		//determine primality using elliptic curve testing
		function elliptic(){
			return null;
		}

		return{
			simple: simple,
			elliptic: elliptic
		}
	}()

/*-----------------------------------------------------------------------------*/

	//statistic oriented tools
	var statistic = function(){

		//get mean value of the numbers in an array
		function mean(arr){
			var count = arr.length;
			var sum = addition(arr);
			return sum/count
		}

		//get median value of the numbers in an array
		function median(arr){
			var count = arr.length;
			var middle;
			if(count % 2 == 0){
				return (arr[count/2] + arr[(count/2 - 1)])/2;
			}else{
				return arr[Math.floor(count / 2)];
			}
		}

		//get mode value (most common value) of the numbers in an array
		function mode(arr){
			//sort array
			var maxIndex = 0, maxOccurence = 0, tempIndex = 0, tempOccurence = 0;
			arr = arr.sort();
			while(tempIndex < arr.length) {
				for (var j = tempIndex; j < arr.length; j++) {
					if(arr[j] == arr[tempIndex]){
						tempOccurence++;
					}else{
						break;
					}
				}
				if(tempOccurence > maxOccurence){
					maxOccurence = tempOccurence;
					maxIndex = tempIndex;
				}
				tempIndex = j;
				tempOccurence = 0;
			}
			return arr[maxIndex];
		}

		//generate a random sample of n-values within lower/upper bounds
		function randomSample(lower,upper,n){
			var sample = new Array();
			var temp;
			for(var i = 0 ; i < n ; i++){
				temp = Math.random()*upper;
				if(temp > lower)
					sample[i] = temp;
			}
			return sample;
		}

		//standard deviation
		function standardDev(arr){
			var count = arr.length;
			var mean = mean(arr);
			var squaredArr = new Array();
			for (var i = 0; i < arr.length; i++) {
				squaredArr[i] = Math.pow((arr[i] - mean),2);
			}
			return Math.sqrt((1/count) * addition(squaredArr));
		}

		//correlation of two arrays
		function correlation(arrX,arrY){
			if(arrX.length == arrY.length){
				var numerator = 0;
				var denominator = (arrX.length)*(standardDev(arrX))*(standardDev(arrY));
				var xMean = mean(arrX);
				var yMean = mean(arrY);
				for(int i = 0 ; i < arrX.length ; i++){
					numerator += (arrX[i] - xMean) * (arrY[i] - yMean);
				}
				return numerator / denominator;
			}else{
				return 'Error: Array mismatch';
			}
		}

		return{
			mean: mean,
			median: median,
			mode: mode,
			randomSample: randomSample,
			standardDev: standardDev,
			correlation: correlation
		}
	}()

/*-----------------------------------------------------------------------------*/

	// return functions
	return{
		addition: addition,
		subtraction: subtraction,
		product: product,
		gcd: gcd,
		lcm: lcm,
		evaluate: evaluate,
		calculus: calculus,
		matrix: matrix,
		prime: prime,
		statistic: statistic
	}
}()<commit_msg>switched to method invocation<commit_after>/*
*	numeric.ly
*	author: steve kaliski
*	feb 2012
*-----------------------------------------------------------------------------*/

var numeric = {

	//basic addition of vals in an array
	addition: function(arr){
		var total = 0;
		for(var i = 0 ; i < arr.length ; i++){
			total = total + arr[i];
		}
		return total;
	}

	//subtract items in an array (descending)
	subtraction: function(arr){
		var total = arr[arr.length - 1];
		for (var i = arr.length - 2; i >= 0; i--) {
			total -= arr[i];
		}
		return total;
	}

	//product of items in array
	product: function(arr){
		var total = arr[0];
		for (var i = 1; i < arr.length; i++) {
			total = total * arr[i];
		};
		return total;
	}

	//greatest common denominator
	product: function(num1, num2){
		var result;
		if(num1 > num2){
			for(i = 0 ; i <= num2 ; i++){
				if(num2%i == 0){
					if(num1%i == 0){
						result = i;
					}
				}
			}
			return result;
		}else if(num2 > num1){
			for(i = 0 ; i <= num2 ; i++){
				if(num1%i == 0){
					if(num2%i == 0){
						var result = i;
					}
				}
			}
			return result;
		}else{
			var result = num1*num2/num1;
			return result;
		}
	}

	//least common multiple
	lcm: function(num1, num2){
		var result = Math.abs(num1*num2) / gcd(num1,num2);
		return result;
	}

	//evaluate function at val (func passed as a string)
	evaluate: function(func, val){
		return parseFloat(eval("with(Math){var x = " + val + ";" + func+'};'));
	}

/*-----------------------------------------------------------------------------*/

	//a variety of calculus tools
 	calculus: {

		//function, point at which differentiation occurs (func passed as string)
		pointDiff: function(func, point){
			var a = evaluate(func, point - .00001);
			var b = evaluate(func, point + .00001);
			return (b-a)/(.00002);
		}

		//calculate riemann integral (left hand) (func passed as string)
		riemann: function(func, start, finish, n){
			var inc = (finish - start)/n;
			var result = 0;
			for (var i = start; i < finish; i+= inc) {
				result += (evaluate(func, i) * inc);
			}
			return result;
		}

		//estimate integral with adaptive simpson quadrature
		simpsonDef: function(func, a, b){
			var c = (a + b) / 2;
			var d = Math.abs(b - a) / 6;
			return d * (evaluate(func, a) + 4 * evaluate(func, c) + evaluate(func, b));
		}
		simpsonRecursive: function(func, a, b, eps, whole){
			var c = a + b;
			var left = simpsonDef(func, a, c);
			var right = simpsonDef(func, c, b);
			if(Math.abs(left + right - whole) <= 15 * eps){
				return left + right + (left + right - whole) / 15;
			}else{
				return simpsonRecursive(func, a, c, eps/2, left) + simpsonRecursive(f, c, b, eps/2, right);
			}
		}
		//execute this
		adaptiveSimpson: function(func, a, b, eps){
			return simpsonRecursive(func, a, b, eps, simpsonDef(func, a, b));
		}

		//take the limit of a function (left, middle, or right)
		limit: function(func, point, approach){
			if(approach == 'left'){
				return evaluate(func, point - .001);
			}else if(approach == 'right'){
				return evaluate(func, point + .001);
			}else if(approach == 'middle'){
				return (limit(func, point, 'left') + limit(func, point, 'right')) / 2;
			}else{
				return 'Error: approach not provided';
			}
		}

		//return taylor polynomial for function (func) at point (point) to degree (degree: 0, 1 and 2 for now)
		taylor: function(func, point, degree){
			if(degree == 0){

			}else if(degree == 1){

			}else if(degree == 2){

			}
		}

		//integrate taylor polynomial for some func
		polyIntegrate: function(func){
			var taylor = taylor(func, 0, 8);
		}
	}

/*-----------------------------------------------------------------------------*/

	/*
	a variety of matrix analysis tools

	let's consider the matrix A:

	[1 2 3]
	[4 5 6]

	As input, set arg = [[1,2,3],[4,5,6]];

	Note: The "structure" of matrices may be neglected in some calculations,
	however in and transformation, mapping, etc, we recognize the proper
	format.
	*/
	matrix: {

		//add two arrays (arrA and arrB)
		addition: function(arrA, arrB){
			if((arrA.length == arrB.length) && (arrA[0].length == arrB[0].length)){
				var result = new Array(arrA.length);
				for (var i = 0; i < arrA.length; i++) {
					result[i] = new Array(arrA[i].length);
					for (var j = 0; j < arrA[i].length; j++) {
						result[i][j] = arrA[i][j] + arrB[i][j];
					}
				}
				return result;
			}else{
				return 'Error: Array mismatch';
			}
		}

		//multiple array (arr) by a scalar (val)
		scalar: function(arr,val){
			for (var i = 0; i < arr.length; i++) {
				for (var j = 0; j < arr[i].length; j++) {
					arr[i][j] = val * arr[i][j];
				}
			}
			return arr;
		}

		//transpose array
		transpose: function(arr){
			var result = new Array(arr[0].length);
			for (var i = 0; i < arr.length; i++) {
				arr[i] = new Array(arr.length);
				for(var j = 0 ; j < arr[i].length){
					arr[j][i] = arr[i][j];
				}
			}
		}
		//return identity matrix of dimension n x n
		identity: function(n){
			var result = new Array(n);
			for(var i = 0 ; i < n ; i++){
				arr[i] = new Array(n);
				for(var j = 0 ; j < n ; j++){
					if(i == j)
						arr[i][j] = 1;
				}
			}
		}

		//dot product
		dotproduct: function(vectorA, vectorB){
			if(vectorA.length == vectorB.length){
				var result = 0;
				for(var i = 0 ; i < vectorA.length ; i++){
					result += vectorA[i]*vectorB[i];
				}
				return result;
			}else{
				return 'Error: Vector mismatch';
			}
		}

		//multiply two matrices
		multiply: function(arrA, arrB){
			if(arrA[0].length == arrB.length){
				var result = new Array(arrA.length);
				for(var i = 0 ; i < arrA.length ; i++){
					result[i] = new Array(arrB[0].length);
				}
				var arrB_T = transpose(arrB);
				for(var i = 0 ; i < result.length ; i++){
					for(var j = 0 ; j < result[i].length ; j++){
						result[i][j] = dotproduct(arrA[i],arrB_T[j]);
					}
				}
				return result;
			}else{
				return 'Error: Array mismatch';
			}
		}

		//determinant, only for n x n -> 2 and 3
		determinant: function(m){
			if((m.length == 2) && (m[0].length == 2)){
				return m[0][0]*m[1][1] - m[0][1]*m[1][0];
			}else if((m.length == 3) && (m[0].length == 3)){
				return m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + m[0][2]*m[1][0]*m[2][1] - m[0][2]*m[1][1]*m[2][0] - m[0][1]*m[1][0]*m[2][2] - m[0][0]*m[1][2]*m[2][1];
			}
		}
	}

/*-----------------------------------------------------------------------------*/

	//collection of prime associated tools
	prime: {

		//standard prime evaluation (no consideration towards efficiency)
		simple: function(val){
			if(val == 1)
				return false;
			else if(val == 2)
				return true;
			else if(val != null){
				start = 2;
				result = true;
				while(start < val){
					if(val % start == 0){
						result = false;
						break;
					}else{
						start++;
					}
				}
				return result;
			}
		}

		//determine primality using elliptic curve testing
		elliptic: function(){
			return null;
		}
	}

/*-----------------------------------------------------------------------------*/

	//statistic oriented tools
	statistic: {

		//get mean value of the numbers in an array
		mean: function(arr){
			var count = arr.length;
			var sum = addition(arr);
			return sum/count
		}

		//get median value of the numbers in an array
		median: function(arr){
			var count = arr.length;
			var middle;
			if(count % 2 == 0){
				return (arr[count/2] + arr[(count/2 - 1)])/2;
			}else{
				return arr[Math.floor(count / 2)];
			}
		}

		//get mode value (most common value) of the numbers in an array
		mode: function(arr){
			//sort array
			var maxIndex = 0, maxOccurence = 0, tempIndex = 0, tempOccurence = 0;
			arr = arr.sort();
			while(tempIndex < arr.length) {
				for (var j = tempIndex; j < arr.length; j++) {
					if(arr[j] == arr[tempIndex]){
						tempOccurence++;
					}else{
						break;
					}
				}
				if(tempOccurence > maxOccurence){
					maxOccurence = tempOccurence;
					maxIndex = tempIndex;
				}
				tempIndex = j;
				tempOccurence = 0;
			}
			return arr[maxIndex];
		}

		//generate a random sample of n-values within lower/upper bounds
		randomSample: function(lower,upper,n){
			var sample = new Array();
			var temp;
			for(var i = 0 ; i < n ; i++){
				temp = Math.random()*upper;
				if(temp > lower)
					sample[i] = temp;
			}
			return sample;
		}

		//standard deviation
		standardDev: function(arr){
			var count = arr.length;
			var mean = mean(arr);
			var squaredArr = new Array();
			for (var i = 0; i < arr.length; i++) {
				squaredArr[i] = Math.pow((arr[i] - mean),2);
			}
			return Math.sqrt((1/count) * addition(squaredArr));
		}

		//correlation of two arrays
		correlation: function(arrX,arrY){
			if(arrX.length == arrY.length){
				var numerator = 0;
				var denominator = (arrX.length)*(standardDev(arrX))*(standardDev(arrY));
				var xMean = mean(arrX);
				var yMean = mean(arrY);
				for(int i = 0 ; i < arrX.length ; i++){
					numerator += (arrX[i] - xMean) * (arrY[i] - yMean);
				}
				return numerator / denominator;
			}else{
				return 'Error: Array mismatch';
			}
		}
	}
}
/*-----------------------------------------------------------------------------*/
<|endoftext|>


<commit_before>const path = require('path');

module.exports = {
  webpack: {
    config(config) {
      // eslint-disable-next-line
      config.resolve.alias = {
        ...config.alias,
        react: path.resolve(__dirname, './node_modules', 'react'),
      };

      // eslint-disable-next-line
      config.resolve.extensions = config.resolve.extensions || [];
      config.resolve.extensions.push('.js', '.jsx', '.ts', '.tsx');

      config.module.rules.push({
        test: /\.tsx?$/,
        loader: 'awesome-typescript-loader',
      });

      return config;
    },
  },
};
<commit_msg>tried updating nwb config file. but to no avail. nwb doesn't work with typescript<commit_after>const path = require('path');

module.exports = {
  type: 'react-component',
  npm: {
    esModules: true,
    umd: {
      entry: './src/ReactTelephoneInput.tsx',
      global: 'ReactTelephoneInput',
      externals: {
        react: 'React',
      },
    },
  },
  webpack: {
    config(config) {
      config.entry = './src/ReactTelephoneInput';
      // eslint-disable-next-line
      config.resolve.alias = {
        ...config.alias,
        react: path.resolve(__dirname, './node_modules', 'react'),
      };

      // eslint-disable-next-line
      config.resolve.extensions = config.resolve.extensions || [];
      config.resolve.extensions.push('.js', '.jsx', '.ts', '.tsx');

      config.module.rules.push({
        test: /\.tsx?$/,
        loader: 'awesome-typescript-loader',
      });

      return config;
    },
  },
};
<|endoftext|>