jam/build/jamsh/jamsh.debug

133783 lines
4.1 MiB
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env jx
var CoreModule = {};
CoreModule['crypto']='crypto';
CoreModule['util']='util';
CoreModule['http']='http';
CoreModule['fs']='fs';
CoreModule['stream']='stream';
CoreModule['url']='url';
CoreModule['os']='os';
CoreModule['net']='net';
CoreModule['zlib']='zlib';
CoreModule['path']='path';
CoreModule['dgram']='dgram';
CoreModule['child_process']='child_process';
CoreModule['events']='events';
CoreModule['string_decoder']='string_decoder';
CoreModule['assert']='assert';
CoreModule['buffer']='buffer';
var BundleModuleCode=[];
var BundleObjectCode=[];
var BundleModules = [];
function _isdir(path) {
var stats=Fs.statSync(path);
return stats && stats.isDirectory()};
function _search(index,file) {
if (PATH.length==index) return file;
var path=PATH[index];
if (Fs.existsSync(path+"/"+file+".js")) return path+"/"+file+".js";
else if (Fs.existsSync(path+"/"+file) && !_isdir(path+"/"+fil)) return path+"/"+file;
else return _search(index+1,file);
}
var Fs = require("fs");
if (typeof __dirname == 'undefined') __dirname = '';
if (typeof __filename == 'undefined') __filename = '/home/sbosse/proj/jam/js/top/jamsh.js';
if (typeof global == 'undefined') global={};
PATH=[process.cwd(),".","/home/sbosse/proj/jam/js"];
function search(index,file) {
if (file.indexOf("/")==-1) return file;
if (PATH.length==index) return file;
var path=PATH[index];
if (Fs.existsSync(path+"/"+file+".js")) return path+"/"+file+".js";
else if (Fs.existsSync(path+"/"+file)) return path+"/"+file;
else return search(index+1,file);
}
function Require(modupath) {
var file,filepath;
if (BundleModules[modupath]) return BundleModules[modupath];
var exports={}; var module={exports:exports};
if (CoreModule[modupath]!=undefined) modupath=CoreModule[modupath];
if (modupath=='') return undefined;
if (BundleModuleCode[modupath]) BundleModuleCode[modupath](module,exports);
else if (BundleObjectCode[modupath]) BundleObjectCode[modupath](module,exports);
else { try { file=search(0,modupath); module = require(file)}
catch (e) { var more="";
if ((e.name==="SyntaxError"||e.name==="TypeError") && file) {
var src=Fs.readFileSync(file,"utf8");
var Esprima = Require("parser/esprima");
try {
var ast = Esprima.parse(src, { tolerant: true, loc:true });
if (ast.errors && ast.errors.length>0) more = ", "+ast.errors[0];
} catch (e) {
if (e.lineNumber) more = ", in line "+e.lineNumber;
}
}
console.log("Require import of "+file+" failed: "+e+more);
// if (e.stack) console.log(e.stack);
throw e; // process.exit(-1);
}}
BundleModules[modupath]=module.exports||module;
return module.exports||module;};
FilesEmbedded=global.FilesEmbedded = {};
FileEmbedd=global.FileEmbedd = function (path,format) {};
FileEmbedded=global.FileEmbedded = function (path,format) {return FilesEmbedded[path](format);};
global.TARGET='node';
BundleModuleCode['com/io']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: sbosse on 28-3-15.
** $VERSION: 1.12.1
**
** $INFO:
*
* This module encapsulates all IO operations (except networking) supporting
* node.js applications.
*
** $ENDOFINFO
*/
/*
************
** Node.js/jxcore
************
*/
var util = Require('util');
var os = Require('os');
var child = Require('child_process');
var GetEnv = Require('os/getenv');
var Base64 = Require('os/base64');
var Fs = Require('fs');
Require('os/polyfill')
var stderr_fun = function (str) { process.stderr.write(str); };
var stdout_fun = function (str) { process.stdout.write(str); };
/*
** node.js specific
*/
var tracefile = undefined;
var tracing = false;
var timestamp = false;
/**
* Open a module and append all exported properties to the context (e.g., global or this).
* (top-level scope)
*/
global.open = function(name,context,as) {
var module = Require(name);
if (!context) context=global;
for (var p in module) {
context[p] = module[p];
};
if (as) context[as]=module;
}
global.print = console.log;
var NL = '\n';
global.checkOptions = function(options,defaultOptions) {
return Object.assign({}, defaultOptions||{}, options) };
global.checkOption = function (option,defaultOption) {
return option==undefined? defaultOption:option };
var io = {
options: {
columns: undefined,
rows: undefined,
log: console.log.bind(console),
err: console.err,
warn: console.warn,
},
/**
*
* @param fd
*/
close: function (fd) {
Fs.closeSync(fd);
},
/**
** Return current date in year-month-day format
*/
Date: function ()
{
var now = new Date();
var year = "" + now.getFullYear();
var month = "0" + (now.getMonth()+1);
month = month.substring(month.length-2);
var date = "0" + now.getDate();
date = date.substring(date.length-2);
return year + "-" + month + "-" + date;
},
/**
*
* @param msg
*/
debug: function (msg) {
io.options.err('Debug: ' + msg);
},
/**
*
* @param path
*/
exists: function (path) {
return Fs.existsSync(path);
},
error: undefined,
/**
*
* @param msg
*/
err: function (msg) {
io.options.err('Error: ' + msg);
throw Error(msg);
},
exit: function (n) {
process.exit(n);
},
/**
*
* @param msg
*/
fail: function (msg) {
io.options.err('Fatal Error: ' + msg);
process.exit(0);
},
/**
*
* @param path
*/
file_exists: function (path) {
return Fs.existsSync(path);
},
/** Search a file by iterating global PATH variable.
*
* @param name File name or partial (relative) path
*/
file_search: function (name) {
// Expecting global PATH variable !?
if (io.file_exists(name)) return name;
else if (typeof PATH !== 'undefined') {
for (var p in PATH) {
if (io.file_exists(PATH[p]+'/'+name)) return (PATH[p]+'/'+name);
}
return undefined;
} else return undefined;
},
/**
*
* @param path
* @returns {number}
*/
file_size: function (path) {
var stat = Fs.statSync(path);
if (stat != undefined)
return stat.size;
else
return -1;
},
/**
*
* @param path
* @param timekind a c m
* @returns {number}
*/
file_time: function (path,timekind) {
var stat = Fs.statSync(path);
if (stat != undefined)
switch (timekind) {
case 'a': return stat.atime.getTime()/1000;
case 'c': return stat.ctime.getTime()/1000;
case 'm': return stat.mtime.getTime()/1000;
default: return stat.mtime.getTime()/1000;
}
else
return -1;
},
/**
* @return {string []}
*/
getargs: function () {
return process.argv;
},
getenv: function (name, def) {
return GetEnv(name, def);
},
/**
*
* @param obj
*/
inspect: util.inspect,
/**
*
* @param {boolean|string} condmsg conditional message var log=X; log((log lt. N)||(msg))
*/
log: function (condmsg) {
if (condmsg==true) return;
if (!timestamp) console.warn(condmsg);
else {
var date = new Date();
var time = Math.floor(date.getTime());
console.warn('['+process.pid+':'+time+']'+condmsg);
}
},
/**
*
* @returns {*} RSS HEAP in kBytes {data,heap}
*/
mem: function () {
var mem = process.memoryUsage();
return {data:(mem.rss/1024)|0,heap:(mem.heapUsed/1024)|0};
},
/**
*
* @param path
* @param mode
* @returns {*}
*/
open: function (path, mode) {
return Fs.openSync(path, mode);
},
/**
*
* @param msg
*/
out: function (msg) {
io.options.log(msg)
},
/**
*
* @param e
* @param where
*/
printstack: function (e,where) {
if (!e.stack) e=new Error(e);
if (!e.stack) e.stack ='empty stack';
var stack = e.stack //.replace(/^[^\(]+?[\n$]/gm, '')
.replace(/^\s+at\s+/gm, '')
.replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@')
.split('\n');
if (where==undefined) io.out(e);
else io.out(where+': '+e);
io.out('Stack Trace');
io.out('--------------------------------');
for(var i in stack) {
if (i>0) {
var line = stack[i];
if(line.indexOf('Module.',0)>=0) break;
io.out(' at '+line);
}
}
io.out('--------------------------------');
},
/**
*
* @param fd
* @param len
* @param foff
*/
read: function (fd, len, foff) {
// TODO
},
/**
*
* @param path
* @returns {string|undefined}
*/
read_file: function (path) {
try {
return Fs.readFileSync(path,'utf8');
} catch (e) {
io.error=e;
return undefined;
}
},
/**
*
* @param path
* @returns {*}
*/
read_file_bin: function (path) {
try {
return Fs.readFileSync(path);
} catch (e) {
io.error=e;
return undefined;
}
},
/**
*
* @param fd
*/
read_line: function (fd) {
// TODO
},
/**
*
* @param fd
* @param buf
* @param boff
* @param len
* @param [foff]
* @returns {number}
*/
read_buf: function (fd, buf, boff, len, foff) {
return Fs.readSync(fd, buf, boff, len, foff);
},
/**
*
* @param e
* @param where
*/
sprintstack: function (e) {
var str='';
if (e==_ || !e.stack) e=new Error(e);
if (!e.stack) e.stack ='empty stack';
var stack = e.stack //.replace(/^[^\(]+?[\n$]/gm, '')
.replace(/^\s+at\s+/gm, '')
.replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@')
.replace(/^Object.eval\s*\(/gm, '')
.split('\n');
for(var i in stack) {
if (i>0) {
var line = stack[i];
if(line.indexOf('Module.',0)>=0) break;
if (str!='') str += '\n';
str += ' at '+line;
}
}
return str;
},
/**
*
*/
stacktrace: function () {
var e = new Error('dummy');
if (!e.stack) e.stack ='empty stack';
var stack = e.stack.replace(/^[^\(]+?[\n$]/gm, '')
.replace(/^\s+at\s+/gm, '')
.replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@')
.split('\n');
io.out('Stack Trace');
io.out('--------------------------------');
for(var i in stack) {
if (i>0) {
var line = stack[i];
if(line.indexOf('Module.',0)>=0) break;
io.out(' at '+line);
}
}
io.out('--------------------------------');
},
/**
*
* @param fun
*/
set_stderr: function(fun) {
stderr_fun=fun;
},
/**
*
* @param fun
*/
set_stdout: function(fun) {
stdout_fun=fun;
},
/**
*
* @param msg
*/
stderr: function (msg) {
stderr_fun(msg);
},
// sleep(ms)
sleep: function(delay) {
var start = new Date().getTime();
while (new Date().getTime() < start + delay);
},
/**
*
* @param msg
*/
stdout: function (msg) {
stdout_fun(msg);
},
/**
*
* @param fd
*/
sync: function (fd) {
Fs.fsyncSync(fd);
},
date: function () {
var date = Date();
return date.split(' ').slice(1,5).join(' ');
},
/** Return system time in milliseconds
*/
time: function () {
var date = new Date();
return Math.floor(date.getTime());
},
/**
** Return current time in hour:minute:second:milli format
*/
Time: function ()
{
var now = new Date();
var hour = "0" + now.getHours();
hour = hour.substring(hour.length-2);
var minute = "0" + now.getMinutes();
minute = minute.substring(minute.length-2);
var second = "0" + now.getSeconds();
second = second.substring(second.length-2);
var milli = "0" + Math.floor(now.getMilliseconds()/10);
milli = milli.substring(milli.length-2);
return hour + ":" + minute + ":" + second+':'+milli;
},
/** Write a message with a time stamp written to the trace file.
*
* @param {boolean|string} condmsg conditional message var trace=Io.tracing; trace(trace||(msg))
*/
trace: function (condmsg) {
if (condmsg != true && tracefile != undefined) {
var date = new Date();
var time = Math.floor(date.getTime());
Fs.writeSync(tracefile, '[' + time + '] ' + condmsg + '\n');
}
},
tracing: tracing,
/**
*
* @param {string} path
*/
trace_open: function (path) {
tracefile = Fs.openSync(path, 'w+');
if (tracefile != undefined) io.tracing = false;
},
/**
*
* @param msg
*/
warn: function (msg) {
if (!timestamp) io.options.warn('Warning: ' + msg);
else {
var date = new Date();
var time = Math.floor(date.getTime());
console.warn('['+process.pid+':'+time+'] Warning: '+msg);
}
},
workdir: function () {
return io.getenv('PWD',io.getenv('CWD',''));
},
/**
*
* @param fd
* @param data
* @param [foff]
* @returns {number}
*/
write: function (fd, data, foff) {
return Fs.writeSync(fd, data, foff);
},
/**
*
* @param fd
* @param buf
* @param bpos
* @param blen
* @param [foff]
* @returns {number}
*/
write_buf: function (fd, buf, bpos, blen, foff) {
return Fs.writeSync(fd, buf, bpos, blen, foff);
},
/**
*
* @param path
* @param {string} buf
*/
write_file: function (path,str) {
try {
Fs.writeFileSync(path, str, 'utf8');
return str.length;
} catch (e) {
return -1;
}
},
/**
*
* @param path
* @param buf
* @returns {*}
*/
write_file_bin: function (path,buf) {
try {
Fs.writeFileSync(path, buf, 'binary');
return buf.length;
} catch (e) {
io.error=e;
return -1;
}
},
/**
*
* @param fd
* @param {string} str
* @returns {number}
*/
write_line: function (fd, str) {
return Fs.writeSync(fd, str+NL);
},
/**
* Process management
*/
fork: child.fork,
exec: child.exec,
execSync: child.execSync,
spawn: child.spawn,
/**
* OS
*/
hostname: os.hostname
};
module.exports = io;
};
BundleModuleCode['os/getenv']=function (module,exports){
var util = require("util");
//var url = require("url");
var fallbacksDisabled = false;
function _value(varName, fallback) {
var value = process.env[varName];
if (value === undefined) {
if (fallback === undefined) {
throw new Error('GetEnv.Nonexistent: ' + varName + ' does not exist ' +
'and no fallback value provided.');
}
if (fallbacksDisabled) {
throw new Error('GetEnv.DisabledFallbacks: ' + varName + ' relying on fallback ' +
'when fallbacks have been disabled');
}
return '' + fallback;
}
return value;
}
var convert = {
string: function(value) {
return '' + value;
},
int: function(value) {
var isInt = value.match(/^-?\d+$/);
if (!isInt) {
throw new Error('GetEnv.NoInteger: ' + value + ' is not an integer.');
}
return +value;
},
float: function(value) {
var isInfinity = (+value === Infinity || +value === -Infinity);
if (isInfinity) {
throw new Error('GetEnv.Infinity: ' + value + ' is set to +/-Infinity.');
}
var isFloat = !(isNaN(value) || value === '');
if (!isFloat) {
throw new Error('GetEnv.NoFloat: ' + value + ' is not a number.');
}
return +value;
},
bool: function(value) {
var isBool = (value === 'true' || value === 'false');
if (!isBool) {
throw new Error('GetEnv.NoBoolean: ' + value + ' is not a boolean.');
}
return (value === 'true');
}
// , url: url.parse
};
function converter(type) {
return function(varName, fallback) {
if(typeof varName == 'string') { // default
var value = _value(varName, fallback);
return convert[type](value);
} else { // multibert!
return getenv.multi(varName);
}
};
};
var getenv = converter('string');
Object.keys(convert).forEach(function(type) {
getenv[type] = converter(type);
});
getenv.array = function array(varName, type, fallback) {
type = type || 'string';
if (Object.keys(convert).indexOf(type) === -1) {
throw new Error('GetEnv.ArrayUndefinedType: Unknown array type ' + type);
}
var value = _value(varName, fallback);
return value.split(/\s*,\s*/).map(convert[type]);
};
getenv.multi = function multi(spec) {
var key, value;
var result = {};
for(var key in spec) {
var value = spec[key];
if(util.isArray(value)) { // default value & typecast
switch(value.length) {
case 1: // no default value
case 2: // no type casting
result[key] = getenv(value[0], value[1]); // dirty, when case 1: value[1] is undefined
break;
case 3: // with typecast
result[key] = getenv[value[2]](value[0], value[1]);
break;
default: // wtf?
throw('getenv.multi(): invalid spec');
break;
}
} else { // value or throw
result[key] = getenv(value);
}
}
return result;
};
getenv.disableFallbacks = function() {
fallbacksDisabled = true;
};
getenv.enableFallbacks = function() {
fallbacksDisabled = false;
};
module.exports = getenv;
};
BundleModuleCode['os/base64']=function (module,exports){
var keyStr = "ABCDEFGHIJKLMNOP" +
"QRSTUVWXYZabcdef" +
"ghijklmnopqrstuv" +
"wxyz0123456789+/" +
"=";
var Base64 = {
encode: function (input) {
input = escape(input);
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
keyStr.charAt(enc1) +
keyStr.charAt(enc2) +
keyStr.charAt(enc3) +
keyStr.charAt(enc4);
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
} while (i < input.length);
return output;
},
encodeBuf: function (input) {
var output = "";
var NaN = output.charCodeAt(2);
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;
var len = input.length;
do {
chr1 = input.readUInt8(i++);
chr2 = (i<len)?input.readUInt8(i++):NaN;
chr3 = (i<len)?input.readUInt8(i++):NaN;
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
keyStr.charAt(enc1) +
keyStr.charAt(enc2) +
keyStr.charAt(enc3) +
keyStr.charAt(enc4);
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
} while (i < len);
return output;
},
decode: function (input) {
var output = "";
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
} while (i < input.length);
return unescape(output);
},
decodeBuf: function (input) {
var len = input.length;
var buf = new Buffer(len);
var chr1, chr2, chr3 = "";
var enc1, enc2, enc3, enc4 = "";
var i = 0;
var buflen = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
buf.fill(0);
do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
buf.writeUInt8(chr1,buflen);
buflen++;
if (enc3 != 64) {
buf.writeUInt8(chr2,buflen);
buflen++;
}
if (enc4 != 64) {
buf.writeUInt8(chr3,buflen);
buflen++;
}
chr1 = chr2 = chr3 = "";
enc1 = enc2 = enc3 = enc4 = "";
} while (i < input.length);
return buf.slice(0,buflen);
}
};
module.exports = Base64;
};
BundleModuleCode['os/polyfill']=function (module,exports){
/********** OBJECT **************/
Object.addProperty = function (obj,name,fun) {
if (obj.prototype[name]) return;
obj.prototype[name]=fun;
Object.defineProperty(obj.prototype, name, {enumerable: false});
};
Object.updateProperty = function (obj,name,fun) {
obj.prototype[name]=fun;
Object.defineProperty(obj.prototype, name, {enumerable: false});
};
if (typeof Object.assign != 'function') {
// Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
/************** ARRAY ********************/
if (!Array.prototype.find) {
Object.addProperty(Array, 'find', function(predicate) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, ( kValue, k, O ))).
// d. If testResult is true, return kValue.
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
// e. Increase k by 1.
k++;
}
// 7. Return undefined.
return undefined;
});
}
// String prototype extensions
if (!String.prototype.contains){
Object.addProperty(String,'contains', function (el) {
return this.indexOf(el)!=-1
})
}
// Check Options Extension
checkOptions = function(options,defaultOptions) {
return Object.assign({}, defaultOptions||{}, options) };
checkOption = function (option,defaultOption) {
return option==undefined? defaultOption:option };
};
BundleModuleCode['com/path']=function (module,exports){
var Fs = Require('fs');
var _process = process || {};
(function () {
"use strict";
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var isWindows = _process.platform === 'win32';
var util = Require('util');
if (!util.deprecate) util.deprecate=function(f,w) {return f;};
// resolves . and .. elements in a path array with directory names there
// must be no slashes, empty elements, or device names (c:\) in the array
// (so also no leading and trailing slashes - it does not distinguish
// relative and absolute paths)
function normalizeArray(parts, allowAboveRoot) {
// if the path tries to go above the root, `up` ends up > 0
var up = 0;
for (var i = parts.length - 1; i >= 0; i--) {
var last = parts[i];
if (last === '.') {
parts.splice(i, 1);
} else if (last === '..') {
parts.splice(i, 1);
up++;
} else if (up) {
parts.splice(i, 1);
up--;
}
}
// if the path is allowed to go above the root, restore leading ..s
if (allowAboveRoot) {
for (; up--; up) {
parts.unshift('..');
}
}
return parts;
}
if (isWindows) {
// Regex to split a windows path into three parts: [*, device, slash,
// tail] windows-only
var splitDeviceRe =
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
// Regex to split the tail part of the above into [*, dir, basename, ext]
var splitTailRe =
/^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/;
// Function to split a filename into [root, dir, basename, ext]
// windows version
var splitPath = function(filename) {
// Separate device+slash from tail
var result = splitDeviceRe.exec(filename),
device = (result[1] || '') + (result[2] || ''),
tail = result[3] || '';
// Split the tail into dir, basename and extension
var result2 = splitTailRe.exec(tail),
dir = result2[1],
basename = result2[2],
ext = result2[3];
return [device, dir, basename, ext];
};
var normalizeUNCRoot = function(device) {
return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
};
// path.resolve([from ...], to)
// windows version
exports.resolve = function() {
var resolvedDevice = '',
resolvedTail = '',
resolvedAbsolute = false;
for (var i = arguments.length - 1; i >= -1; i--) {
var path;
if (i >= 0) {
path = arguments[i];
} else if (!resolvedDevice) {
path = _process.cwd();
} else {
// Windows has the concept of drive-specific current working
// directories. If we've resolved a drive letter but not yet an
// absolute path, get cwd for that drive. We're sure the device is not
// an unc path at this points, because unc paths are always absolute.
path = _process.env['=' + resolvedDevice];
// Verify that a drive-local cwd was found and that it actually points
// to our drive. If not, default to the drive's root.
if (!path || path.substr(0, 3).toLowerCase() !==
resolvedDevice.toLowerCase() + '\\') {
path = resolvedDevice + '\\';
}
}
// Skip empty and invalid entries
if (!util.isString(path)) {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = device && device.charAt(1) !== ':',
isAbsolute = exports.isAbsolute(path),
tail = result[3];
if (device &&
resolvedDevice &&
device.toLowerCase() !== resolvedDevice.toLowerCase()) {
// This path points to another device so it is not applicable
continue;
}
if (!resolvedDevice) {
resolvedDevice = device;
}
if (!resolvedAbsolute) {
resolvedTail = tail + '\\' + resolvedTail;
resolvedAbsolute = isAbsolute;
}
if (resolvedDevice && resolvedAbsolute) {
break;
}
}
// Convert slashes to backslashes when `resolvedDevice` points to an UNC
// root. Also squash multiple slashes into a single one where appropriate.
if (isUnc) {
resolvedDevice = normalizeUNCRoot(resolvedDevice);
}
// At this point the path should be resolved to a full absolute path,
// but handle relative paths to be safe (might happen when process.cwd()
// fails)
// Normalize the tail path
function f(p) {
return !!p;
}
resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f),
!resolvedAbsolute).join('\\');
return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) ||
'.';
};
// windows version
exports.normalize = function(path) {
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = device && device.charAt(1) !== ':',
isAbsolute = exports.isAbsolute(path),
tail = result[3],
trailingSlash = /[\\\/]$/.test(tail);
// If device is a drive letter, we'll normalize to lower case.
if (device && device.charAt(1) === ':') {
device = device[0].toLowerCase() + device.substr(1);
}
// Normalize the tail path
tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) {
return !!p;
}), !isAbsolute).join('\\');
if (!tail && !isAbsolute) {
tail = '.';
}
if (tail && trailingSlash) {
tail += '\\';
}
// Convert slashes to backslashes when `device` points to an UNC root.
// Also squash multiple slashes into a single one where appropriate.
if (isUnc) {
device = normalizeUNCRoot(device);
}
return device + (isAbsolute ? '\\' : '') + tail;
};
// windows version
exports.isAbsolute = function(path) {
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = !!device && device.charAt(1) !== ':';
// UNC paths are always absolute
return !!result[2] || isUnc;
};
// windows version
exports.join = function() {
function f(p) {
if (!util.isString(p)) {
throw new TypeError('Arguments to path.join must be strings');
}
return p;
}
var paths = Array.prototype.filter.call(arguments, f);
var joined = paths.join('\\');
// Make sure that the joined path doesn't start with two slashes, because
// normalize() will mistake it for an UNC path then.
//
// This step is skipped when it is very clear that the user actually
// intended to point at an UNC path. This is assumed when the first
// non-empty string arguments starts with exactly two slashes followed by
// at least one more non-slash character.
//
// Note that for normalize() to treat a path as an UNC path it needs to
// have at least 2 components, so we don't filter for that here.
// This means that the user can use join to construct UNC paths from
// a server name and a share name; for example:
// path.join('//server', 'share') -> '\\\\server\\share\')
if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) {
joined = joined.replace(/^[\\\/]{2,}/, '\\');
}
return exports.normalize(joined);
};
// path.relative(from, to)
// it will solve the relative path from 'from' to 'to', for instance:
// from = 'C:\\orandea\\test\\aaa'
// to = 'C:\\orandea\\impl\\bbb'
// The output of the function should be: '..\\..\\impl\\bbb'
// windows version
exports.relative = function(from, to) {
from = exports.resolve(from);
to = exports.resolve(to);
// windows is not case sensitive
var lowerFrom = from.toLowerCase();
var lowerTo = to.toLowerCase();
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '') break;
}
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '') break;
}
if (start > end) return [];
return arr.slice(start, end + 1);
}
var toParts = trim(to.split('\\'));
var lowerFromParts = trim(lowerFrom.split('\\'));
var lowerToParts = trim(lowerTo.split('\\'));
var length = Math.min(lowerFromParts.length, lowerToParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (lowerFromParts[i] !== lowerToParts[i]) {
samePartsLength = i;
break;
}
}
if (samePartsLength == 0) {
return to;
}
var outputParts = [];
for (var i = samePartsLength; i < lowerFromParts.length; i++) {
outputParts.push('..');
}
outputParts = outputParts.concat(toParts.slice(samePartsLength));
return outputParts.join('\\');
};
exports.sep = '\\';
exports.delimiter = ';';
} else /* posix */ {
// Split a filename into [root, dir, basename, ext], unix version
// 'root' is just a slash, or nothing.
var splitPathRe =
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
var splitPath = function(filename) {
return splitPathRe.exec(filename).slice(1);
};
// path.resolve([from ...], to)
// posix version
exports.resolve = function() {
var resolvedPath = '',
resolvedAbsolute = false;
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
var path = (i >= 0) ? arguments[i] : _process.cwd();
// Skip empty and invalid entries
if (!util.isString(path)) {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
resolvedPath = path + '/' + resolvedPath;
resolvedAbsolute = path.charAt(0) === '/';
}
// At this point the path should be resolved to a full absolute path, but
// handle relative paths to be safe (might happen when process.cwd() fails)
// Normalize the path
resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) {
return !!p;
}), !resolvedAbsolute).join('/');
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
};
// path.normalize(path)
// posix version
exports.normalize = function(path) {
var isAbsolute = exports.isAbsolute(path),
trailingSlash = path[path.length - 1] === '/',
segments = path.split('/'),
nonEmptySegments = [];
// Normalize the path
for (var i = 0; i < segments.length; i++) {
if (segments[i]) {
nonEmptySegments.push(segments[i]);
}
}
path = normalizeArray(nonEmptySegments, !isAbsolute).join('/');
if (!path && !isAbsolute) {
path = '.';
}
if (path && trailingSlash) {
path += '/';
}
return (isAbsolute ? '/' : '') + path;
};
// posix version
exports.isAbsolute = function(path) {
return path.charAt(0) === '/';
};
// posix version
exports.join = function() {
var path = '';
for (var i = 0; i < arguments.length; i++) {
var segment = arguments[i];
if (!util.isString(segment)) {
throw new TypeError('Arguments to path.join must be strings');
}
if (segment) {
if (!path) {
path += segment;
} else {
path += '/' + segment;
}
}
}
return exports.normalize(path);
};
// path.relative(from, to)
// posix version
exports.relative = function(from, to) {
from = exports.resolve(from).substr(1);
to = exports.resolve(to).substr(1);
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '') break;
}
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '') break;
}
if (start > end) return [];
return arr.slice(start, end + 1);
}
var fromParts = trim(from.split('/'));
var toParts = trim(to.split('/'));
var length = Math.min(fromParts.length, toParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (fromParts[i] !== toParts[i]) {
samePartsLength = i;
break;
}
}
var outputParts = [];
for (var i = samePartsLength; i < fromParts.length; i++) {
outputParts.push('..');
}
outputParts = outputParts.concat(toParts.slice(samePartsLength));
return outputParts.join('/');
};
exports.sep = '/';
exports.delimiter = ':';
}
exports.dirname = function(path) {
var result = splitPath(path),
root = result[0],
dir = result[1];
if (!root && !dir) {
// No dirname whatsoever
return '.';
}
if (dir) {
// It has a dirname, strip trailing slash
dir = dir.substr(0, dir.length - 1);
}
return root + dir;
};
exports.basename = function(path, ext) {
var f = splitPath(path)[2];
// TODO: make this comparison case-insensitive on windows?
if (ext && f.substr(-1 * ext.length) === ext) {
f = f.substr(0, f.length - ext.length);
}
return f;
};
exports.extname = function(path) {
return splitPath(path)[3];
};
exports.exists = util.deprecate(function(path, callback) {
if (Fs) Fs.exists(path, callback);
else callback(false);
}, 'path.exists is now called `fs.exists`.');
exports.existsSync = util.deprecate(function(path) {
if (Fs) return Fs.existsSync(path);
else return false;
}, 'path.existsSync is now called `fs.existsSync`.');
if (isWindows) {
exports._makeLong = function(path) {
// Note: this will *probably* throw somewhere.
if (!util.isString(path))
return path;
if (!path) {
return '';
}
var resolvedPath = exports.resolve(path);
if (/^[a-zA-Z]\:\\/.test(resolvedPath)) {
// path is local filesystem path, which needs to be converted
// to long UNC path.
return '\\\\?\\' + resolvedPath;
} else if (/^\\\\[^?.]/.test(resolvedPath)) {
// path is network UNC path, which needs to be converted
// to long UNC path.
return '\\\\?\\UNC\\' + resolvedPath.substring(2);
}
return path;
};
} else {
exports._makeLong = function(path) {
return path;
};
}
}());
};
BundleModuleCode['com/sprintf']=function (module,exports){
(function(window) {
var re = {
not_string: /[^s]/,
number: /[diefg]/,
json: /[j]/,
not_json: /[^j]/,
text: /^[^\x25]+/,
modulo: /^\x25{2}/,
placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/,
key: /^([a-z_][a-z_\d]*)/i,
key_access: /^\.([a-z_][a-z_\d]*)/i,
index_access: /^\[(\d+)\]/,
sign: /^[\+\-]/
}
function sprintf() {
var key = arguments[0], cache = sprintf.cache
if (!(cache[key] && cache.hasOwnProperty(key))) {
cache[key] = sprintf.parse(key)
}
return sprintf.format.call(null, cache[key], arguments)
}
sprintf.format = function(parse_tree, argv) {
var cursor = 1, tree_length = parse_tree.length, node_type = "", arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = ""
for (i = 0; i < tree_length; i++) {
node_type = get_type(parse_tree[i])
if (node_type === "string") {
output[output.length] = parse_tree[i]
}
else if (node_type === "array") {
match = parse_tree[i] // convenience purposes only
if (match[2]) { // keyword argument
arg = argv[cursor]
for (k = 0; k < match[2].length; k++) {
if (!arg.hasOwnProperty(match[2][k])) {
throw new Error(sprintf("[sprintf] property '%s' does not exist", match[2][k]))
}
arg = arg[match[2][k]]
}
}
else if (match[1]) { // positional argument (explicit)
arg = argv[match[1]]
}
else { // positional argument (implicit)
arg = argv[cursor++]
}
if (get_type(arg) == "function") {
arg = arg()
}
if (re.not_string.test(match[8]) && re.not_json.test(match[8]) && (get_type(arg) != "number" && isNaN(arg))) {
throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg)))
}
if (re.number.test(match[8])) {
is_positive = arg >= 0
}
switch (match[8]) {
case "b":
arg = arg.toString(2)
break
case "c":
arg = String.fromCharCode(arg)
break
case "d":
case "i":
arg = parseInt(arg, 10)
break
case "j":
arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0)
break
case "e":
arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential()
break
case "f":
arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg)
break
case "g":
arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg)
break
case "o":
arg = arg.toString(8)
break
case "s":
arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg)
break
case "u":
arg = arg >>> 0
break
case "x":
arg = arg.toString(16)
break
case "X":
arg = arg.toString(16).toUpperCase()
break
}
if (re.json.test(match[8])) {
output[output.length] = arg
}
else {
if (re.number.test(match[8]) && (!is_positive || match[3])) {
sign = is_positive ? "+" : "-"
arg = arg.toString().replace(re.sign, "")
}
else {
sign = ""
}
pad_character = match[4] ? match[4] === "0" ? "0" : match[4].charAt(1) : " "
pad_length = match[6] - (sign + arg).length
pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : "") : ""
output[output.length] = match[5] ? sign + arg + pad : (pad_character === "0" ? sign + pad + arg : pad + sign + arg)
}
}
}
return output.join("")
}
sprintf.cache = {}
sprintf.parse = function(fmt) {
var _fmt = fmt, match = [], parse_tree = [], arg_names = 0
while (_fmt) {
if ((match = re.text.exec(_fmt)) !== null) {
parse_tree[parse_tree.length] = match[0]
}
else if ((match = re.modulo.exec(_fmt)) !== null) {
parse_tree[parse_tree.length] = "%"
}
else if ((match = re.placeholder.exec(_fmt)) !== null) {
if (match[2]) {
arg_names |= 1
var field_list = [], replacement_field = match[2], field_match = []
if ((field_match = re.key.exec(replacement_field)) !== null) {
field_list[field_list.length] = field_match[1]
while ((replacement_field = replacement_field.substring(field_match[0].length)) !== "") {
if ((field_match = re.key_access.exec(replacement_field)) !== null) {
field_list[field_list.length] = field_match[1]
}
else if ((field_match = re.index_access.exec(replacement_field)) !== null) {
field_list[field_list.length] = field_match[1]
}
else {
throw new SyntaxError("[sprintf] failed to parse named argument key")
}
}
}
else {
throw new SyntaxError("[sprintf] failed to parse named argument key")
}
match[2] = field_list
}
else {
arg_names |= 2
}
if (arg_names === 3) {
throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported")
}
parse_tree[parse_tree.length] = match
}
else {
throw new SyntaxError("[sprintf] unexpected placeholder")
}
try {_fmt = _fmt.substring(match[0].length)} catch (e) {throw new SyntaxError("[sprintf] unexpected fromat")}
}
return parse_tree
}
var vsprintf = function(fmt, argv, _argv) {
_argv = (argv || []).slice(0)
_argv.splice(0, 0, fmt)
return sprintf.apply(null, _argv)
}
/**
* helpers
*/
function get_type(variable) {
return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase()
}
function str_repeat(input, multiplier) {
return Array(multiplier + 1).join(input)
}
/**
* export to either browser or node.js
*/
if (typeof exports !== "undefined") {
exports.sprintf = sprintf
exports.vsprintf = vsprintf
}
else {
window.sprintf = sprintf
window.vsprintf = vsprintf
if (typeof define === "function" && define.amd) {
define(function() {
return {
sprintf: sprintf,
vsprintf: vsprintf
}
})
}
}
})(typeof window === "undefined" ? this : window);
};
BundleModuleCode['/home/sbosse/proj/jam/js/top/jamsh.js']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 1-3-18 by sbosse.
** $VERSION: 1.2.1
**
** $INFO:
**
** JAM Shell Interpreter (Front end)
**
** $ENDOFINFO
*/
/* Soem hacks */
process.noDeprecation = true
var Comp = Require('com/compat');
var Io = Require('com/io');
var doc = Require('doc/doc');
var table = Require('doc/table');
var readline = Require('com/readline');
var readlineSync = Require('term/readlineSync');
var renderer = doc.Renderer({lazy:true});
var http = Require('http');
var httpserv = Require('http/https'); // a file server!
try { var https = require('https'); } catch (e) {}; if (!https.request) https=undefined;
var geoip = Require('geoip/geoip'); // a geo location database server
var util = Require('util')
var sip = Require('top/rendezvous');
var db = Require('db/db');
// JAM Shell Interpreter (Back end)
var JamShell = Require('shell/shell');
var nlp = Require('nlp/nlp');
var ml = Require('ml/ml')
var nn = Require('nn/nn')
var csp = Require('csp/csp')
var sat = Require('logic/sat')
var logic = Require('logic/prolog')
var csv = Require('parser/papaparse');
var ampCOM = Require('jam/ampCOM');
var numerics = Require('numerics/numerics')
var osutils = Require('os/osutils');
var UI = Require('ui/app/app');
var Des48 = Require('dos/des48');
var p;
var options= {
args:[],
echo: true,
modules : {
csp : csp,
csv : csv,
db : db,
des48 : Des48,
doc : doc,
geoip : geoip,
http : http,
httpserv : httpserv,
https : https,
logic:logic,
ml:ml,
nlp:nlp,
nn:nn,
numerics:numerics,
os:osutils,
readline : readline,
readlineSync : readlineSync,
sat : sat,
sip : sip,
table : table,
UI: UI,
},
extensions : {
url2addr: ampCOM.url2addr,
sleep: process.watchdog&&process.watchdog.sleep?
function (milli) {
process.watchdog.sleep(milli)
}:undefined
},
nameopts : {length:8, memorable:true, lowercase:true},
Nameopts : {length:8, memorable:true, uppercase:true},
output : null,
renderer : renderer,
server : false,
verbose : 0,
}
process.on('uncaughtException', function (err) {
console.error(err.stack);
console.log("jamsh not exiting...");
});
if ((p=process.argv.indexOf('--'))>0) {
options.args=process.argv.slice(p+1,process.argv.length);
process.argv=process.argv.slice(0,p);
}
if (process.argv[1].match(/jamsh$/)||process.argv[1].match(/jamsh\.debug$/)) {
var ind;
if (process.argv.indexOf('-h')>0) return print('usage: jamsh [-v -s] [script] [-e "shell commands"]');
if ((ind=process.argv.indexOf('-e'))>0) {
options.server=true;
options.output=console.log;
options.exec=process.argv[ind+1];
} else if (process.argv.length>2) {
var script = process.argv.filter(function (arg,ind) {
return ind>1 && arg.indexOf(':') == -1 && arg.indexOf('-') != 0;
});
if (script.length == 1) {
options.script=script[0];
options.output=console.log;
options.server=true;
}
}
process.argv.forEach(function (arg) {
switch (arg) {
case '-v': options.verbose++; break;
case '-s': options.server=false; break;
}
})
if (!options.server && options.verbose==0) options.verbose=1;
JamShell(options).init();
}
};
BundleModuleCode['com/compat']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2021 bLAB
** $CREATED: 30-3-15 by sbosse.
** $VERSION: 1.24.1
**
** $INFO:
**
** JavaScript-OCaML Compatibility Module
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Path = Require('com/path');
var Sprintf = Require('com/sprintf');
/*******************************
** Some global special "values"
********************************/
/** A matching template pattern matching any value
*
* @type {undefined}
*/
global.any = undefined;
/** A matching template pattern matching any value
*
* @type {undefined}
*/
global._ = undefined;
/**
*
* @type {null}
*/
global.none = null;
/**
*
* @type {null}
*/
global.empty = null;
global.NL = '\n';
global.int = function (v) {return v|0};
global.div = function (a,b) {return a/b|0};
if (!Object.prototype.forEach) {
Object.defineProperties(Object.prototype, {
'forEach': {
value: function (callback) {
if (this == null) {
throw new TypeError('Not an object');
}
var obj = this;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
callback.call(obj, obj[key], key, obj);
}
}
},
writable: true
}
});
}
/** Just transfer parent prototypes to child
*
*/
function inherit(child,parent) {
for(var p in parent.prototype) {
if (p == '__proto__') continue;
child.prototype[p]=parent.prototype[p];
}
}
/** Portable class inheritance and instanceOf polyfill
*
*/
// SomeObject.prototype.__proto__=SomeObject2.prototype;
// Child class inherits prototype from parent using __proto__
function inheritPrototype(child,parent) {
var __proto__=child.__proto__;
child.prototype.__proto__=parent.prototype;
if (!__proto__) for(var p in parent.prototype) {
if (p == '__proto__') continue;
child.prototype[p]=parent.prototype[p];
}
}
// Polyfill fir o instanceof c with inheritance check (checking __proto__)
function instanceOf(obj,cla) {
var p=obj.__proto__;
if (obj instanceof cla) return true;
while (p) {
if (p === cla.prototype) return true;
p=p.__proto__
}
return false;
}
// Polyfill for __defineGetter__ / __defineSetter__
function defineGetter(cla,prop,fun) {
Object.defineProperty(cla.prototype,prop,{
configurable:true,
get:fun
});
}
function defineSetter(cla,prop,fun) {
Object.defineProperty(cla.prototype,prop,{
configurable:true,
set:fun
});
}
Object.addProperty = function (obj,name,fun) {
if (obj.prototype[name]) return;
obj.prototype[name]=fun;
Object.defineProperty(obj.prototype, name, {enumerable: false});
};
Object.updateProperty = function (obj,name,fun) {
obj.prototype[name]=fun;
Object.defineProperty(obj.prototype, name, {enumerable: false});
};
Object.addProperty(Array,'contains',function (el) { return this.indexOf(el)!=-1 });
Object.addProperty(Array,'last',function () { return this[this.length-1] });
global.inherit = inherit;
global.inheritPrototype = inheritPrototype;
global.instanceOf = instanceOf;
global.defineGetter = defineGetter;
global.defineSetter = defineSetter;
/**
*
*/
var assert = function(condmsg) {
if (condmsg != true) {
Io.out('** Assertion failed: '+condmsg+' **');
Io.stacktrace();
throw Error(condmsg);
}
};
global.assert=assert;
function forof(obj,f) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = obj[Symbol.iterator](), _step;
!(_iteratorNormalCompletion = (_step = _iterator.next()).done);
_iteratorNormalCompletion = true) {
element = _step.value;
f(element);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
global.forof=forof;
/** OBJ
*
*/
var obj = {
/** Compact an object:
* [{a:b},[c:d},..] -> {a:b,c:d,..}
* {a:[b]} -> {a:b}
*
*/
compact: function (o) {
var a;
if (obj.isArray(o)) {
if (o.length==1 && obj.isObject(o[0])) return obj.compact(o[0]);
else return o;
} else if (obj.isObject(o)) for (a in o) {
var elem=o[a];
o[a]=obj.compact(elem);
}
return o;
},
copy: function (o) {
if (o === null || typeof o !== 'object') {
return o;
}
var temp = (o instanceof Array) ? [] : {};
for (var key in o) {
temp[key] = obj.copy(o[key]);
}
return temp;
},
equal: function (o1,o2) {
if (!o1 || !o2) return false;
for(var i in o1) if (o1[i]!=o2[i]) return false;
for(var i in o2) if (o1[i]!=o2[i]) return false;
return true;
},
extend: function (o1,o2) {
for(var i in o2) o1[i]=o2[i];
return o1;
},
find: function(obj,fun) {
var p;
for(p in obj) {
if (fun(obj[p],p)) return obj[p];
}
},
hasProperty: function (o,p) {
return o[p]!=undefined || (p in o);
},
head:function(o) {
for (var p in o) return p;
return undefined;
},
// transfer src attributes to dst recusively (no object overwrite)
inherit: function (dst,src) {
for(var i in src) {
if (typeof dst[i] == 'object' && typeof src[i] == 'object')
inherit(dst[i],src[i]);
else if (typeof dst[i] == 'undefined')
dst[i]=src[i];
}
return dst;
},
isArray:function (o) {
if (o==_ || o ==null) return false;
else return typeof o == "array" || (typeof o == "object" && o.constructor === Array);
},
isMatrix:function (o) {
if (o==_ || o ==null) return false;
else return obj.isArray(o) &&
obj.isArray(o[0]);
},
isEmpty: function (o) {
for(var prop in o) {
if (o[prop]!=undefined) return false;
}
return true;
},
isError: function (o) {
return o instanceof Error
},
isFunction: function (o) {
return typeof o == "function";
},
isObj:function (o) {
return typeof o == "object";
},
isObject:function (o) {
return typeof o == "object";
},
isRegex: function (o) {
return o instanceof RegExp;
},
isString: function (o) {
return typeof o == "string" || (typeof o == "object" && o.constructor === String);
},
isNumber: function (o) {
return typeof o == "number" || (typeof o == "object" && o.constructor === Number);
},
iter: function(obj,fun) {
var p;
for(p in obj) {
fun(obj[p],p)
}
}
};
/** ARRAY
*
*/
var array = {
/** Evaluate a function returning a boolean value for each member of the array and
* compute the boolean conjunction.
*
* @param {* []} array
* @param {function(*,number)} fun
*/
and: function(array,fun) {
var res=true;
var i=0;
var len=array.length;
for(i=0;i<len;i++) {
var element=array[i];
res=res&&fun(element,i)
}
return res;
},
/** Append one element at the end of the array.
*
* @param {* []} array
* @param {*} element
* @returns {* []}
*/
append : function(array,element) {
array.push(element);
return array;
},
/**
*
* @param {* []} array
* @param {function(*,number)} fun
*/
call: function(array,args) {
var i=0;
var len=array.length;
for(i=0;i<len;i++) {
var element=array[i];
element()
}
},
/** Check for an elenment in the array by using a check function.
*
* @param array
* @param fun
* @returns {boolean}
*/
check: function(array,fun) {
var i,exist;
exist=false;
loop: for(i in array) {
var element=array[i];
if (fun(element,i)) {
exist=true;
break loop;
}
}
return exist;
},
/** Append array2 at the end of array inplace. The extended array is returned.
* Source array (1) will be modified.
*
* @param {*[]} array
* @param {*[]} array2
* @returns {*[]}
*/
concat : function(array,array2) {
for(var i in array2) {
array.push(array2[i]);
}
return array;
},
/** Create the conjunction set of two arrays
*
*/
conjunction :function (set1,set2,fun) {
return array.union(set1,set2,fun);
},
/**
*
* @param {*[]} array
* @param {number|string|*|*[]} elements
* @param {function} [fun] Optional equality test function
* @returns {boolean}
*/
contains : function(array,elements,fun) {
var i = array.length;
if (!fun) fun=function(o1,o2) {return o1===o2};
if (obj.isArray(elements)) {
while (i--) {
var j = elements.length;
while (j--) {
if (fun(array[i],elements[j])) {
return true;
}
}
}
}
else while (i--) {
if (fun(array[i],elements)) {
return true;
}
}
return false;
},
/** Return a fresh copy of the source array or copy src array to dst.
*
* @param array
* @returns {Array.<T>|string|Blob|ArrayBuffer}
*/
copy: function(src,dst) {
var i;
if (dst) {
for(i in src) dst[i]=src[i];
} else return src.slice();
},
/** Create a new array with initial element values.
*
* @param length
* @param init
* @returns {Array}
*/
create : function(length,init) {
var arr = [], i = length;
while (i--) {
arr[i] = init;
}
return arr;
},
/** Create a matrix (array of array) with initial element values.
*
*/
create_matrix : function(rows,cols,init) {
var m = [];
var r = [];
var i,j;
for (i = 0; i < rows; i++) {
r=[];
for(j=0;j<cols;j++) r.push(init);
m.push(r);
}
return m;
},
/** Create the (inclusive) disjunction set of two arrays.
* Source arrays will not be modified.
*
*/
disjunction :function (set1,set2,fun) {
return array.merge(set1,set2);
},
/**
*
* @param array
* @returns {boolean}
*/
empty : function (array) {
return (array==undefined ||
array.length==0)
},
/** Test for equality
*/
equal: function (a1,a2) {
if (a1.length != a2.length) return false;
for(var i in a1) if (a1[i]!=a2[i]) return false;
return true;
},
/** Create the (exclusive) disjunction set of two arrays.
* Source arrays will not be modified.
*
*/
exclusive :function (set1,set2,fun) {
var i,j,found,res = [];
for (i in set1) {
found=false;
loop1: for (j in set2) {
if (fun != undefined && fun(set1[i],set2[j])) {found=true; break loop1;}
else if (fun == undefined && set1[i]==set2[j]) {found=true; break loop1;};
}
if (!found) res.push(set1[i]);
}
for (i in set2) {
found=false;
loop2: for (j in set1) {
if (fun != undefined && fun(set2[i],set1[j])) {found=true; break loop2;}
else if (fun == undefined && set2[i]==set1[j]) {found=true; break loop2;};
}
if (!found) res.push(set2[i]);
}
return res;
},
/** Find an element in an array and return it (or none);
*
* @param array
* @param fun
* @returns {undefined|*}
*/
find: function(array,fun) {
var i;
for(i in array) {
if (fun(array[i],i)) return array[i];
}
return none;
},
/** Search and map an element of an array using a test&map function.
*
* @param array
* @param {function(*,number):*} fun
* @returns {undefined|*}
*/
findmap: function(array,fun) {
var i,found;
for(i in array) {
found=fun(array[i],i);
if (found) return found;
}
return none;
},
/** Filter out elements using a test function.
*
* @param {* []} array
* @param {function(*,number):boolean} fun
* @returns {* []}
*/
filter: function(array,fun) {
if (array.filter) return array.filter(fun);
else {
var res=[],
len=array.length,
element,i;
for(i=0;i<len;i++) {
element=array[i];
if (fun(element,i)) res.push(element);
}
return res;
}
},
/** Filter out and map elements using a test&map function.
*
* @param {* []} array
* @param {function(*,number):*|undefined} fun
* @returns {* []}
*/
filtermap: function(array,fun) {
var res=[],
len=array.length,
element,mapped,i;
for(i=0;i<len;i++) {
element=array[i];
mapped=fun(element,i);
if (mapped!=undefined) res.push(mapped);
}
return res;
},
/** Flattens an array consting of arrays (and elements)
*
* @param array
* @returns {Array}
*/
flatten: function (array) {
var res=[];
var len=array.length;
var i;
for(i=0;i<len;i++) {
var element=array[i];
if (!obj.isArray(element)) res.push(element);
else {
var j;
var len2=element.length;
for(j=0;j<len2;j++) {
var element2=element[j];
res.push(element2);
}
}
}
return res;
},
/**
*
* @param array
* @returns {*}
*/
head : function(array) {
return array[0];
},
/**
*
* @param length
* @param fun
* @returns {Array}
*/
init : function(length,fun) {
var arr = [], i = length;
while (i--) {
arr[i] = fun(i);
}
return arr;
},
/**
*
* @param {* []} array
* @param {function(*,number)} fun
*/
iter: function(array,fun) {
/*
var i=0;
var len=array.length;
for(i=0;i<len;i++) {
fun(array[i],i)
}
*/
array.forEach(fun);
},
/**
*
* @param {* []} array1
* @param {* []} array2
* @param {function(*,*,number)} fun
*/
iter2: function(array1,array2,fun) {
var i=0;
assert((array1.length == array2.length)||('Array.iter2: arrays of different lengths'));
/*
var len=array1.length;
for(i=0;i<len;i++) {
fun(array1[i],array2[i],i)
}
*/
array1.forEach(function (e1,i) { fun(e1,array2[i],i) });
},
/**
*
* @param {* []} array
* @param {function(*,number)} fun Returning a true value leaves iteration loop
*/
iter_break: function(array,fun) {
var i=0;
var len=array.length;
for(i=0;i<len;i++) {
var element=array[i];
if (fun(element,i)) return;
}
},
/**
*
* @param {* []} array
* @param {function(*,number)} fun
*/
iter_rev: function(array,fun) {
var i;
var len=array.length;
for(i=len-1;i>=0;i--) {
fun(array[i],i)
}
},
/** Return last element of array.
*
*/
last : function(array) {
var len=array.length;
if (len==0) return none;
else return array[len-1];
},
length : function(array) {
return array.length;
},
/**
*
* @param {* []} array1
* @param {* []} array2
* @param {function(*,*,number)} fun
* @returns {* []}
*/
map2: function(array1,array2,fun) {
var i=0;
assert((array1.length == array2.length)||('Array.map2: arrays of different lengths'));
var len=array1.length;
var res=[];
for(i=0;i<len;i++) {
res.push(fun(array1[i],array2[i],i));
}
return res;
},
/**
*
* @param {* []} array
* @param {function(*,number)} fun
* @returns {* []}
*/
map: function(array,fun) {
var i=0;
var len=array.length;
var res=[];
for(i=0;i<len;i++) {
var element=array[i];
res.push(fun(element,i));
}
return res;
},
/**
*
* @param {* []} array
* @param {Function} fun_hdtl - function(hd,tl)
* @param {Function} [fun_empty] - function()
*/
match: function(array,fun_hdtl,fun_empty) {
if (array.length == 0) {
if (fun_empty) fun_empty();
} else if (array.length>1) {
var hd = this.head(array);
var tl = this.tail(array);
fun_hdtl(hd,tl);
} else fun_hdtl(this.head(array),[]);
},
/**
*
* @param {* []} array
* @param {Function} fun_hd1hd2 - function(hd1,hd2)
* @param {Function} [fun_hdtl] - function(hd,tl)
* @param {Function} [fun_empty] - function()
*/
match2: function(array,fun_hd1hd2,fun_hdtl,fun_empty) {
if (array.length == 0 && fun_empty)
fun_empty();
else if (array.length == 2) {
var hd1 = this.head(array);
var hd2 = this.second(array);
fun_hd1hd2(hd1,hd2);
}
else if (array.length>1 && fun_hdtl) {
var hd = this.head(array);
var tl = this.tail(array);
fun_hdtl(hd,tl);
} else if (fun_hdtl) fun_hdtl(this.head(array),[]);
},
/** Return the maximum element of an array applying
* an optional mapping function.
*
* @param {* []} array
* @param [fun]
* @returns {number|undefined}
*/
max : function (array,fun) {
var res,max,num;
for(var i in array) {
if (fun) num=fun(array[i]); else num=array[i];
if (max==undefined) { max=num; res=array[i] }
else if (num > max) { max=num; res=array[i] }
}
return res;
},
/** Return the minimum element of an array applying
* an optional mapping function.
*
* @param {* []} array
* @param [fun]
* @returns {number|undefined}
*/
min : function (array,fun) {
var res,min,num;
for(var i in array) {
if (fun) num=fun(array[i]); else num=array[i];
if (min==undefined) { min=num; res=array[i] }
else if (num < min) { min=num; res=array[i] }
}
return res;
},
/** Check for an element in the array.
*
* @param {(number|string|boolean) []} array
* @param {number|string|boolean} element
* @returns {boolean}
*/
member: function(array,element) {
var i,exist;
var len=array.length;
exist=false;
loop: for(i=0;i<len;i++) {
var _element=array[i];
if (_element==element) {
exist=true;
break loop;
}
}
return exist;
},
/** Merge all arrays and return a new array.
*
* @param {Array} array1
* @param {Array} array2
* @param {Array} [array3]
* @param {Array} [array4]
* @returns {Array}
*/
merge: function(array1,array2,array3,array4) {
var arraynew=array1.slice();
arraynew=arraynew.concat(array2);
if (array3!=undefined) arraynew=arraynew.concat(array3);
if (array4!=undefined) arraynew=arraynew.concat(array4);
return arraynew;
},
/** Return the next element from array after val (next element after last is first!)
* @param {Array} array
* @param {number|string} val
* @returns {number|string}
*/
next: function(array,val) {
var i;
var len=array.length;
if (obj.isString(val))
for(i=0;i<len;i++) {
if (string.equal(array[i],val)) {
if (i==len-1) return array[0];
else return array[i+1];
}
}
else
for(i=0;i<len;i++) {
if (array[i]==val) {
if (i==len-1) return array[0];
else return array[i+1];
}
}
return none;
},
/** Evaluate a function returning a boolean value for each member of the array and
* compute the boolean disjunction.
*
* @param {* []} array
* @param {function(*,number)} fun
*/
or: function(array,fun) {
var res=false;
var i=0;
var len=array.length;
for(i=0;i<len;i++) {
var element=array[i];
res=res||fun(element,i)
}
return res;
},
/**
* Gets the property value of `key` from all elements in `collection`.
*
* var users = [
* { 'user': 'barney', 'age': 36 },
* { 'user': 'fred', 'age': 40 }
* ];
*
* pluck(users, 'user');
* // => ['barney', 'fred']
*/
pluck: function(collection, key) {
return collection.map(function(object) {
return object == null ? undefined : object[key];
});
},
/*
** Push/pop head elements (Stack behaviour)
*/
/** Remove and return top element of array.
*
* @param array
* @returns {*}
*/
pop : function(array) {
var element=array[0];
array.shift();
return element;
},
print: function(array) {
var i;
var len=array.length;
var str='[';
for(i=0;i<len;i++) {
var cell=array[i];
str=str+cell;
}
return str+']';
},
/** Add new element at top of array.
*
* @param array
* @param element
*/
push : function(array,element) {
array.unshift(element);
},
/** Create an ordered array of numbers {a,a+1,..b}
*
* @param a
* @param b
* @returns {Array}
*/
range : function(a,b) {
var i;
var array=[];
for(i=a;i<=b;i++) array.push(i);
return array;
},
/** Remove elements from an array.
* [1,2,3,4,5,6] (begin=2,end=4) => [1,2,6]
* @param {* []} array
* @returns {* []}
*/
remove: function(array,begin,end) {
var i,a;
if (end==undefined) end=begin+1;
if (begin<0 || end >= array.length) return [];
a=array.slice(0,begin);
for(i=end;i<array.length;i++) a.push(array[i]);
return a;
},
second : function(array) {
return array[1];
},
/**
*
* @param {* []} array
* @param {function(*,*):number} fun (1:a gt. b by the ordering criterion,-1: a lt. b, 0: a eq. b)
* @returns {* []}
*/
sort: function(array,fun) {
var array2=array.slice();
array2.sort(fun);
return array2;
},
/** Split an array at position 'pos', i.e., remove 'len' (1) elements starting at
* position 'pos'.
* ==> use remove!!! split should return two arrays!!
*
* @param array
* @param pos
* @param [len]
* @param element
*/
split: function(array,pos,len) {
if (pos==0) return array.slice((len||1));
else {
var a1=array.slice(0,pos);
var a2=array.slice(pos+(len||1));
return a1.concat(a2);
}
},
/** Return the sum number of an array applying
* an optional mapping function.
*
* @param {* []} array
* @param [fun]
* @returns {number|undefined}
*/
sum : function (array,fun) {
var res=0;
for(var i in array) {
var num=0;
if (fun) num=fun(array[i]); else num=array[i];
if (!obj.isNumber(num)) return undefined;
res += num;
}
return res;
},
/** Return a new array w/o the head element (or optional
* w/o the first top elements).
*
*/
tail : function(array,top) {
var array2=array.slice();
array2.shift();
if (top) for(;top>1;top--) array2.shift();
return array2;
},
/** Return union of two sets (== conjunction set)
*
* @param {* []} set1
* @param {* []} set2
* @param {function} [fun] Equality test
* @returns {* []}
*/
union : function(set1,set2,fun) {
var i,j,res = [];
for (i in set1) {
for (j in set2) {
if (fun != undefined && fun(set1[i],set2[j])) res.push(set1[i]);
else if (fun == undefined && set1[i]==set2[j]) res.push(set1[i]);
}
}
return res;
},
/**
* Creates a duplicate-free version of an array
*/
unique: function(array) {
var length = array ? array.length : 0;
function baseUniq(array) {
var index = -1,
length = array.length,
seen,
result = [];
seen = result;
outer:
while (++index < length) {
var value = array[index];
var seenIndex = seen.length;
while (seenIndex--) {
if (seen[seenIndex] === value) {
continue outer;
}
}
result.push(value);
}
return result;
}
if (!length) {
return [];
}
return baseUniq(array);
},
/**
* Creates an array excluding all provided values
* without([1, 2, 1, 3], 1, 2);
* // => [3]
*/
without: function () {
var array,
values=[];
for(var i in arguments) {
if (i==0) array=arguments[0];
else values.push(arguments[i]);
}
return array.filter(function (e) {
return values.indexOf(e) == -1;
});
},
/** Test for zero elements {0, '', false, undefined, ..}
*/
zero: function (array) {
for(var i in array) if (!!array[i]) return false;
return true;
},
};
/** STRING
*
*/
var string = {
/** Is pattern conatined in template?
*
*/
contains: function (template,pattern) {
return template.indexOf(pattern)>-1;
},
copy: function(src) {
var i;
var dst='';
for(i=0;i<src.length;i++) dst=dst+src.charAt(i);
return dst;
},
/**
*
* @param {number} size
* @returns {string} filled with spaces
*/
create: function(size)
{
var i;
var s='';
var init=' ';
for(i=0;i<size;i++) s=s+init;
return s;
},
endsWith : function (str,tail) {
return str.indexOf(tail)==(str.length-tail.length);
},
empty: function (str) {
return this.equal(str,'');
},
equal: function(str1,str2) {
var i;
var eq=true;
if (str1.length != str2.length) return false;
for(i=0;i<str1.length;i++) { if (string.get(str1,i)!=string.get(str2,i)) eq=false;}
return eq;
},
find: function (search,str) {
return str.indexOf(search);
},
format_hex: function (n,len) {
// format a hexadecimal number with 'len' figures.
switch (len) {
case 2: return (((n>>4) & 0xf).toString(16))+
((n&0xf).toString(16));
case 4: return (((n>>12) & 0xf).toString(16)+
((n>>8) & 0xf).toString(16)+
((n>>4) & 0xf).toString(16)+
(n&0xf).toString(16));
case 6: return (((n>>20) & 0xf).toString(16)+
((n>>16) & 0xf).toString(16)+
((n>>12) & 0xf).toString(16)+
((n>>8) & 0xf).toString(16)+
((n>>4) & 0xf).toString(16)+
(n&0xf).toString(16));
case 8: return (((n>>28) & 0xf).toString(16)+
((n>>24) & 0xf).toString(16)+
((n>>20) & 0xf).toString(16)+
((n>>16) & 0xf).toString(16)+
((n>>12) & 0xf).toString(16)+
((n>>8) & 0xf).toString(16)+
((n>>4) & 0xf).toString(16)+
(n&0xf).toString(16));
default: return 'format_hex??';
}
},
/**
*
* @param {string} str
* @param {number} index
* @returns {string}
*/
get: function (str,index) {
assert((str != undefined && index < str.length && index >= 0)||('string.get ('+str.length+')'));
return str.charAt(index);
},
isBoolean: function (str) {
return (str=='true' || str=='false')
},
isNumeric: function (str) {
return !isNaN(parseFloat(str)) && isFinite(str);
},
isText: function (s) {
var is_text=true;
string.iter(s,function (ch,i) {
string.match(ch,[
['a','z',function () {}],
['A','Z',function () {}],
['0','9',function () {if (i==0) is_text=false;}],
function () {is_text=false;}
]);
});
return is_text;
},
/**
*
* @param {string} str
* @param {function(string,number)} fun
*/
iter: function(str,fun) {
var i;
var len=str.length;
for (i = 0; i < len; i++) {
var c = str.charAt(i);
fun(c,i);
}
},
/**
*
* @param str
* @returns {*}
*/
length: function(str) {
if (str!=undefined) return str.length;
else return 0;
},
/**
*
* @param str
* @returns {string}
*/
lowercase : function (str) {
return str.toLowerCase();
},
/**
*
* @param {number} size
* @param {string} init
* @returns {string}
*/
make: function(size,init)
{
var i;
var s='';
for(i=0;i<size;i++) s=s+init;
return s;
},
/** Map a string with a set of (test,reuslt) transformation rules.
*
* @param {string} str
* @param {* [] []} case - ([string,string] | fun) []
*/
map: function(str,mapping) {
var i;
var map;
for(i in mapping) {
map=mapping[i];
if (obj.isFunction(map)) return map(str);
else if (this.equal(str,map[0])) return map[1];
}
},
/** Match a string with different patterns and apply a matching function.
*
* @param {string} str
* @param {* [] []} cases - ([string,fun] | [string [<case1>,<case2>,..],fun] | [<range1>:string,<range2>:string,fun] | fun) []
*/
match: function(str,cases) {
var i,j;
var cas,cex,cv;
for(i in cases) {
cas=cases[i];
if (obj.isArray(cas)) {
switch (cas.length) {
case 2:
// Multi-value-case
cex=cas[0];
if (!obj.isArray(cex)) {
if (this.equal(str,cex)) {
cas[1]();
return;
}
} else {
for(j in cex) {
cv=cex[j];
if (this.equal(str,cv)) {
cas[1]();
return;
}
}
}
break;
case 3:
// Character range check
try {
j=pervasives.int_of_char(str);
if (j>= pervasives.int_of_char(cas[0]) && j<=pervasives.int_of_char(cas[1])) {
cas[2](str);
return;
}
} catch(e) {
return
};
break;
case 1:
cas[0](str); // Default case - obsolete
return;
default:
throw 'String.match #args';
}
} else if (obj.isFunction(cas)) {
// Default case
cas(str);
return;
}
}
},
/** Pad a string on the left (pre-str.length) if pre>0,
* right (post-str.length) if post>0, or centered (pre>0&post>0).
*
*/
pad: function (str,pre,post,char) {
var len = str.length;
if (pre>0 && post==0) return string.make(len-pre,char||' ')+str;
else if (post>0 && pre==0) return str+string.make(post-len,char||' ');
else return string.make(len-pre/2,char||' ')+str+string.make(len-post/2,char||' ');
},
/**
*
* @param str
* @param pos
* @param len
* @returns {Number}
*/
parse_hex: function (str,pos,len) {
// parse a hexadecimal number in string 'str' starting at position 'pos' with 'len' figures.
return parseInt(this.sub(str,pos,len),16);
},
/** Return the sub-string after a point in the source string ('.' or optional point string).
* If there is no splitting point, the original string is returned.
*
* @param str
* @param [point]
* @returns {string}
*/
postfix: function (str,point) {
var n = str.indexOf(point||'.');
if (n <= 0) return str;
else return str.substr(n+1);
},
/** Return the sub-string before a point in the source string ('.' or optional point string)
* If there is no splitting point, the original string is returned.
*
* @param str
* @param [point]
* @returns {string}
*/
prefix: function (str,point) {
var n = str.indexOf(point||'.');
if (n <= 0) return str;
else return str.substr(0,n);
},
replace_first: function (pat,repl,str) {
return str.replace(pat,repl);
},
replace_all: function (pat,repl,str) {
return str.replace('/'+pat+'/g',repl);
},
/**
*
* @param str
* @param index
* @param char
* @returns {string}
*/
set: function (str,index,char) {
assert((str != undefined && index < str.length && index >= 0)||'string.get');
return str.substr(0, index) + char + str.substr(index+1)
},
/**
*
* @param delim
* @param str
* @returns {*|Array}
*/
split: function (delim,str) {
return str.split(delim);
},
startsWith : function (str,head) {
return !str.indexOf(head);
},
/** Return a sub-string.
*
* @param str
* @param off
* @param [len] If not give, return a sub-string from off to end
* @returns {string}
*/
sub: function (str,off,len) {
if (len)
return str.substr(off,len);
else
return str.substr(off);
},
/** Remove leading and trailing characters from string
*
* @param str
* @param {number} pref number of head characters to remove
* @param {number} post number of tail characters to remove
* @returns {*}
*/
trim: function (str,pref,post) {
if (str.length==0 ||
pref>str.length ||
post>str.length ||
pref < 0 || post < 0 ||
(pref==0 && post==0)
) return str;
return str.substr(pref,str.length-pref-post);
},
/** Return a string with all characters converted to uppercase letters.
*
* @param str
* @returns {string}
*/
uppercase : function (str) {
return str.toUpperCase();
},
/** Return a string with first character converted to uppercase letter.
*
* @param str
* @returns {string}
*/
Uppercase : function (str) {
var len = str.length;
if (len > 1) {
var head = str.substr(0,1);
var tail = str.substr(1,len-1);
return head.toUpperCase()+tail.toLowerCase()
} if (len==1) return str.toUpperCase();
else return '';
}
};
/** RANDOM
*
*/
var rnd = Math.random;
/* Antti Syk\E4ri's algorithm adapted from Wikipedia MWC
** Returns a random generator function [0.0,1.0| with seed initialization
*/
var seeder = function(s) {
var m_w = s;
var m_z = 987654321;
var mask = 0xffffffff;
return function() {
m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;
var result = ((m_z << 16) + m_w) & mask;
result /= 4294967296;
return result + 0.5;
}
}
var random = {
float: function(max) {
return rnd()*max
},
int: function(max) {
return Math.floor(rnd()*max+0)
},
// integer
interval: function(min,max) {
return Math.round(min+rnd()*(max-min))
},
// float
range: function(min,max) {
return min+rnd()*(max-min)
},
seed: function (s) {
// Create a new initialized random generator
rnd=seeder(s);
}
};
/** PRINTF
*
*/
var printf = {
/** Trim string(s).
*
* @param str
* @param indent
* @param [width]
* @param {string} [tab]
* @returns {string}
*/
align: function (str,indent,width,tab) {
var lines = string.split('\n',str);
var form = '';
var sp = printf.spaces(indent);
var spbreak = sp;
array.iter(lines,function(line){
var rest;
function breakit(spbreak,str) {
if (width < (str.length + spbreak.length)) {
return spbreak+string.sub(str,0,width-spbreak.length)+'\n'+
breakit(spbreak,string.sub(str,width-spbreak.length,str.length-width+spbreak.length));
} else return spbreak+str+'\n';
}
if (width && width < (line.length + indent)) {
if (tab) {
var pos = string.find(tab,line);
if (pos > 0 && pos < width) spbreak=printf.spaces(pos+indent+1);
else spbreak=sp;
}
form=form+sp+string.sub(line,0,width-indent)+'\n';
rest=string.sub(line,width-indent,line.length-width+indent);
form=form+breakit(spbreak,rest);
}
else
form=form+sp+line+'\n';
});
return form;
},
/** Format a list of array elements using the (optional) mapping
* function <fun> and the separator <sep> (optional, too, default is ',').
*
*/
list: function (array,fun,sep) {
var i, str='';
if (sep==undefined) sep=',';
if (fun==undefined) fun=function (s) {return s;};
if (!obj.isArray(array)) array=[array];
for (i in array) {
if (str==='') str=fun(array[i]);
else str=str+sep+fun(array[i]);
}
return str;
},
/**
*
* @param n
* @returns {string}
*/
spaces: function (n){
return string.make(n,' ');
},
/** Formatted printer (simplified)
*
* @param {* []} args (['%format',arg]|string) [] format=%s,%d,%f,%c,%x,%#d,%#s,..
* @returns {string}
*/
sprintf2: function(args) {
var str='';
array.iter(args,function(fmtarg) {
var len, n,fs;
if (obj.isArray(fmtarg)) {
if (fmtarg.length==2) {
var fmt=fmtarg[0];
var arg=fmtarg[1];
var fc='';
var fn=0;
string.iter(fmt,function(c) {
if (c=='s' || c=='d' || c=='f' || c=='x') {
fc=c;
} else if (c!='%') {
fn=fn*10;
n=parseInt(c);
if (!isNaN(n)) fn=fn+n;
}
});
if (fc=='s' && obj.isString(arg)) {
str=str+arg;
if (fn!=0) {
len=arg.length;
if (len<fn) str=str+string.create(fn-len);
}
} else if (fc=='d' && obj.isNumber(arg)) {
fs = pervasives.string_of_int(arg);
if (fn!=0) {
len = fs.length;
if (len < fn) {
str=str+string.create(fn-len);
}
}
str=str+fs;
} else if (fc=='x' && obj.isNumber(arg)) {
fs = string.format_hex(arg,fn||8);
str=str+fs;
}
}
} else if (obj.isString(fmtarg)) {
str = str + fmtarg;
}
});
return str;
},
sprintf:Sprintf.sprintf
};
/** FILENAME
*
*/
var filename = {
/**
*
* @param path
* @returns {string}
*/
basename : function (path) {
return Path.basename(path);
},
/**
*
* @param path
* @returns {string}
*/
dirname : function (path) {
return Path.dirname(path);
},
/**
*
* @param path
* @returns {string}
*/
extname : function (path) {
return Path.extname(path)
},
/**
*
* @param path
* @returns {boolean}
*/
is_relative: function(path) {
return !(path.length > 0 && path[0] == '/');
},
/**
*
* @param pathl
* @param absolute
* @returns {string}
*/
join: function (pathl,absolute) {
var path=(absolute?'/':'');
array.iter(pathl,function (name,index) {
if (index>0) {
path=path+'/'+name;
}
else {
path=path+name;
}
});
return path;
},
/**
*
* @param path
* @returns {string}
*/
normalize : function (path) {
return Path.normalize(path)
},
/**
*
* @param path
* @returns {*}
*/
path_absolute: function (path) {
if (this.is_relative(path)) {
var workdir = Io.workdir();
return this.path_normalize(workdir + '/' + path);
} else return this.path_normalize(path);
},
/** Duplicate of Path.normalize!?
*
* @param path
* @returns {string}
*/
path_normalize: function (path) {
var i;
if (string.equal(path, '')) path = '/';
var relpath = !(string.get(path, 0) == '/');
var pathlist = path.split('/');
var pathlist2 = pathlist.filter(function (s) {
return (!string.equal(s, '') && !string.equal(s, '.'))
});
var pathlist3 = [];
array.iter(pathlist2, function (pe) {
if (!string.equal(pe, '..')) {
array.push(pathlist3, pe)
} else {
if (pathlist3.length == 0) return '';
else
pathlist3 = array.tail(pathlist3);
}
});
var path2 = '';
i = 0;
array.iter(pathlist3, function (pe) {
var sep;
if (i == 0) sep = ''; else sep = '/';
path2 = pe + sep + path2;
i++;
});
if (relpath) return path2; else return '/' + path2;
},
removeext: function (path) {
return path.substr(0, path.lastIndexOf('.'));
}
};
/** PERVASIVES
*
*
*/
var pervasives = {
assert:assert,
char_of_int: function (i) {return String.fromCharCode(i)},
div: function(a,b) {return a/b|0;},
failwith: function(msg) {Io.err(msg);},
float_of_string: function(s) {var num=parseFloat(s); if (isNaN(num)) throw 'NaN'; else return num;},
int_of_char: function(c) {return c.charCodeAt()},
int_of_float: function(f) {return f|0;},
int_of_string: function(s) {
var num=parseInt(s); if (isNaN(num)) throw 'NaN'; else return num;
},
/** Try to find a value in a search list and return a mapping value.
*
* @param {*} value
* @param {* []} mapping [testval,mapval] []
* @returns {*}
*/
map: function(value,mapping) {
function eq(v1,v2) {
if (v1==v2) return true;
if (obj.isString(v1) && obj.isString(v2)) return string.equal(v1,v2);
return false;
}
if (!array.empty(mapping)) {
var hd=array.head(mapping);
var tl=array.tail(mapping);
if (eq(hd[0],value)) return hd[1];
else return pervasives.map(value,tl);
} else return undefined;
},
/** Apply a matcher function to a list of cases with case handler functions.
* A case is matched if the matcher function returns a value/object.
*
* The result of the matcher function is passed as an argument ot the case handler function.
* The return value of the case handler fucntion is finally returned by this match function
* or undefined if there was no matching case.
*
* @param {function(*,*):*} matcher function(expr,pat)
* @param {*} expr
* @param {*[]} cases (pattern,handler function | handler function) []
* @returns {*|undefined}
*/
match: function (matcher,expr,cases) {
var ret = undefined;
array.iter_break(cases, function (match) {
var quit, succ, pat, fun;
if (match.length == 2) {
/*
** Pattern, Function
*/
pat = match[0];
fun = match[1];
succ = matcher(expr, pat);
if (succ) ret = fun(succ);
quit = succ!=undefined;
} else if (match.length == 1) {
/*
** Default case, Function
*/
fun = match[0];
ret = fun();
quit= true;
}
return quit;
});
return ret;
},
mtime: function () {var time = new Date(); return time.getTime();},
min: function(a,b) { return (a<b)?a:b},
max: function(a,b) { return (a>b)?a:b},
string_of_float: function(f) {return f.toString()},
string_of_int: function(i) {return i.toString()},
string_of_int64: function(i) {return i.toString()},
time: function () {var time = new Date(); return (time.getTime()/1000)|0;}
};
/** BIT
*
*/
var bit = {
get: function (v,b) {return (v >> b) && 1;},
isSet: function (v,b) {return ((v >> b) && 1)==1;},
set: function (v,b) {return v & (1 << b);}
};
/** ARGS
*
*/
var args = {
/** Parse process or command line arguments (array argv). The first offset [1] arguments are
** ignored. The numarg pattern '*' consumes all remaining arguments.
*
* @param {string []} argv
* @param {*[]} map [<argname>,<numargs:0..3|'*'>,<handler(up to 3 arguments|[])>]|[<defhandler(val)>] []
* @param {number} [offset]
*/
parse: function(argv,map,offset) {
var shift=undefined,
in_shift=0,
shift_args=[],
names,
mapfun,
numarg,
len=argv.length;
if (offset==undefined) offset=1;
argv.forEach(function (val, index) {
var last=index==(len-1);
if(index>=offset) {
if (in_shift==0) {
array.check(map,function (onemap) {
assert(onemap!=undefined||'map');
if (onemap.length==3) {
names = onemap[0];
numarg = onemap[1];
mapfun = onemap[2];
if (!obj.isArray(names)) names=[names];
var found = array.find(names,function (name) {
if (string.equal(val, name)) return name; else _;
});
if (found) {
if (numarg==0) mapfun(found);
else {
in_shift=numarg;
shift_args=[];
shift=mapfun;
}
return true;
}
} else if (obj.isFunction(onemap)) {
onemap(val);
return true;
} else if (onemap.length==1) {
mapfun = onemap[0];
mapfun(val);
return true;
}
return false;
});
} else {
shift_args.push(val);
if (in_shift!='*') in_shift--;
if (in_shift==0 && shift!=undefined) {
numarg=shift_args.length;
switch (numarg) {
case 0: shift(val);break;
case 1: shift(shift_args[0],val); break;
case 2: shift(shift_args[0],shift_args[1],val); break;
case 3: shift(shift_args[0],shift_args[1],shift_args[2],val); break;
default: break;
}
shift=undefined;
} else if (in_shift=='*' && last) shift(shift_args);
}
}
});
}
};
/** HASHTBL
*
*/
var hashtbl = {
add: function(hash,key,data) {
hash[key]=data;
},
create: function(initial) {
return [];
},
empty: function(hash) {
for (var key in hash) return false;
return true;
},
find: function(hash,key) {
return hash[key];
},
invalidate: function(hash,key) {
hash[key]=undefined;
},
iter: function(hash,fun) {
for (var key in hash) {
if (hash[key]!=undefined) fun(key,hash[key]);
}
},
mem: function(hash,key) {
return hash[key] != undefined;
},
remove: function(hash,key) {
// TODO: check, its wrong!
if (!hash.hasOwnProperty(key))
return;
if (isNaN(parseInt(key)) || !(hash instanceof Array))
delete hash[key];
else
hash.splice(key, 1)
}
};
var types = [];
/**
*
* @param name
* @returns {number}
*/
function register_type(name) {
var typoff = 1000+types.length*1000;
if (array.member(types,name)) throw('[COMP] register_type: type '+name+' exists already.');
types.push(name);
return typoff;
}
/**
*
* @typedef {{v1:*, v2:*, v3:*, v4:*, v5:*, v6:*, v7:*, v8:*, v9:* }} tuple
*/
/**
*
* @typedef {{t:number, v1:*, v2:*, v3:*, v4:*, v5:*, v6:*, v7:*, v8:*, v9:* }} tagged_tuple
*/
module.exports = {
args:args,
assert: assert,
array:array,
bit:bit,
copy:obj.copy,
div:pervasives.div,
filename:filename,
hashtbl:hashtbl,
isNodeJS: function () {
return (typeof global !== "undefined" &&
{}.toString.call(global) == '[object global]');
},
obj:obj,
pervasives:pervasives,
printf:printf,
random:random,
string:string,
isArray: obj.isArray,
isString: obj.isString,
isNumber: obj.isNumber,
register_type:register_type,
/**
*
* @param tag
* @param [val1]
* @param [val2]
* @param [val3]
* @returns {(tagged_tuple)}
*/
Tuple: function (tag,val1,val2,val3) {
if(val3) return {t:tag,v1:val1,v2:val2,v3:val3};
else if (val2) return {t:tag,v1:val1,v2:val2};
else if (val1) return {t:tag,v1:val1};
else return {t:tag};
}
};
};
BundleModuleCode['doc/doc']=function (module,exports){
/** Markdown renderer for highlighted and formatted terminal output
* by embedding (escaped) terminal control sequences
*
*/
var Marked = Require('doc/marked');
var Colors = Require('doc/colors');
var List = Require('doc/list');
var Table = Require('doc/cli-table');
var NL = '\n';
function id (x) {return x}
// default css
var css = {
bold:Colors.black.bold,
italic:Colors.underline,
h1:Colors.bold,
h2:Colors.blue.bold,
h3:Colors.red.bold,
ol:{
label:['1','a','i'],
},
ul:{
label:['*','-','+'],
},
}
// Increment numeric/alphanumeric list label
function incr(label,start) {
switch (label) {
case '1': return start.toString();
}
return label;
}
function B (text) { return css.bold(text) }
function I (text) { return css.italic(text) }
function P (text) { return text+'\n' }
function H (text, level) {
var color,
escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
switch (level) {
case 1: color=css.h1; break;
case 2: color=css.h2; break;
case 3: color=css.h3; break;
default: color=id;
}
return color(text+'\n');
};
function CD(text) {
return text+'\n';
}
function DL(body) {
var item;
list=new List({type:'dl',tab:2});
while (this._data.stack.length && this._data.stack[0].dt != undefined) {
item=this._data.stack.shift();
//print(item)
list.unshift({dt:css.bold(item.dt),dd:item.dd});
}
return list.toString()+NL;
}
function DT(body) {
this._data.stack.unshift({dt:body});
}
function DD(body) {
if (this._data.stack.length && this._data.stack[0].dt!=undefined)
this._data.stack[0].dd=body;
}
function L(body, ordered, start) {
var list,label;
if (ordered) label=incr(css.ol.label[this._data.olist],start);
else label=css.ul.label[this._data.ulist];
list=new List({type:label});
if (ordered) this._data.olist++; else this._data.ulist++;
while (this._data.stack.length && this._data.stack[0].item != undefined) {
list.unshift(this._data.stack.shift().item);
}
if (ordered) this._data.olist--; else this._data.ulist--;
return list.toString()+NL;
}
function LI(text) {
this._data.stack.unshift({item:text});
}
function text(text) {
return text.replace(/&quot;/g,'"').
replace(/&gt;/g,'>').
replace(/&lt;/g,'<');
}
// Terminal MarkDown Renderer
function Renderer (options) {
var marked = Marked(),
renderer = new marked.Renderer();
renderer.heading = H.bind(renderer);
renderer.list = L.bind(renderer);
renderer.listitem = LI.bind(renderer);
renderer.paragraph = P.bind(renderer);
renderer.strong = B.bind(renderer);
renderer.em = I.bind(renderer);
renderer._data={stack:[],ulist:0,olist:0};
renderer.dt = DT.bind(renderer);
renderer.dd = DD.bind(renderer);
renderer.dl = DL.bind(renderer);
renderer.code = CD.bind(renderer);
renderer.text = text;
marked.setOptions({
renderer: renderer,
highlight: function(code) {
return require('highlight.js').highlightAuto(code).value;
},
pedantic: false,
gfm: true,
tables: true,
breaks: false,
sanitize: false,
smartLists: true,
smartypants: false,
xhtml: false
});
if (options.lazy) return function (text) { try { return marked(text) } catch (e) { return text }};
else return marked;
}
module.exports = {
Colors:Colors,
List:List,
Marked:Marked,
Renderer:Renderer,
Table:Table
}
};
BundleModuleCode['doc/marked']=function (module,exports){
/**
* marked - a markdown parser
* Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
* 2016-2019 (c) Dr. Stefan Bosse
* https://github.com/markedjs/marked
*
* Version 1.2.2
*/
module.exports = function() {
'use strict';
/**
* Block-Level Grammar
*/
var block = {
newline: /^\n+/,
code: /^( {4}[^\n]+\n*)+/,
fences: noop,
hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
nptable: noop,
blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
html: '^ {0,3}(?:' // optional indentation
+ '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
+ '|comment[^\\n]*(\\n+|$)' // (2)
+ '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
+ '|<![A-Z][\\s\\S]*?>\\n*' // (4)
+ '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
+ '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
+ '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
+ '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
+ ')',
def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
dl: /^ *(dt)\n: *(dd)/,
table: noop,
lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)+)/,
text: /^[^\n]+/
};
block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
block.def = edit(block.def)
.replace('label', block._label)
.replace('title', block._title)
.getRegex();
block.bullet = /(?:[*+-]|\d+\.)/;
block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
block.item = edit(block.item, 'gm')
.replace(/bull/g, block.bullet)
.getRegex();
block.list = edit(block.list)
.replace(/bull/g, block.bullet)
.replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
.replace('def', '\\n+(?=' + block.def.source + ')')
.getRegex();
block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
+ '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
+ '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
+ '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
+ '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
+ '|track|ul';
block._comment = /<!--(?!-?>)[\s\S]*?-->/;
block.html = edit(block.html, 'i')
.replace('comment', block._comment)
.replace('tag', block._tag)
.replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
.getRegex();
block.paragraph = edit(block.paragraph)
.replace('hr', block.hr)
.replace('heading', block.heading)
.replace('lheading', block.lheading)
.replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
.getRegex();
block.blockquote = edit(block.blockquote)
.replace('paragraph', block.paragraph)
.getRegex();
block.dl = edit(block.dl)
.replace('dt', block.text)
.replace('dd', block.text)
.getRegex();
/**
* Normal Block Grammar
*/
block.normal = merge({}, block);
/**
* GFM Block Grammar
*/
block.gfm = merge({}, block.normal, {
fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\n? *\1 *(?:\n+|$)/,
paragraph: /^/,
heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
});
block.gfm.paragraph = edit(block.paragraph)
.replace('(?!', '(?!'
+ block.gfm.fences.source.replace('\\1', '\\2') + '|'
+ block.list.source.replace('\\1', '\\3') + '|')
.getRegex();
/**
* GFM + Tables Block Grammar
*/
block.tables = merge({}, block.gfm, {
nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
});
/**
* Pedantic grammar
*/
block.pedantic = merge({}, block.normal, {
html: edit(
'^ *(?:comment *(?:\\n|\\s*$)'
+ '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
+ '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
.replace('comment', block._comment)
.replace(/tag/g, '(?!(?:'
+ 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
+ '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
+ '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
.getRegex(),
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/
});
/**
* Block Lexer
*/
function Lexer(options) {
this.tokens = [];
this.tokens.links = {};
this.options = options || marked.defaults;
this.rules = block.normal;
if (this.options.pedantic) {
this.rules = block.pedantic;
} else if (this.options.gfm) {
if (this.options.tables) {
this.rules = block.tables;
} else {
this.rules = block.gfm;
}
}
}
/**
* Expose Block Rules
*/
Lexer.rules = block;
/**
* Static Lex Method
*/
Lexer.lex = function(src, options) {
var lexer = new Lexer(options);
return lexer.lex(src);
};
/**
* Preprocessing
*/
Lexer.prototype.lex = function(src) {
src = src
.replace(/\r\n|\r/g, '\n')
.replace(/\t/g, ' ')
.replace(/\u00a0/g, ' ')
.replace(/\u2424/g, '\n');
return this.token(src, true);
};
/**
* Lexing
*/
Lexer.prototype.token = function(src, top) {
src = src.replace(/^ +$/gm, '');
var next,
loose,
cap,
bull,
b,
item,
space,
i,
tag,
l,
isordered;
while (src) {
// newline
if (cap = this.rules.newline.exec(src)) {
src = src.substring(cap[0].length);
if (cap[0].length > 1) {
this.tokens.push({
type: 'space'
});
}
}
// code
if (cap = this.rules.code.exec(src)) {
src = src.substring(cap[0].length);
cap = cap[0].replace(/^ {4}/gm, '');
this.tokens.push({
type: 'code',
text: !this.options.pedantic
? cap.replace(/\n+$/, '')
: cap
});
continue;
}
// fences (gfm)
if (cap = this.rules.fences.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
type: 'code',
lang: cap[2],
text: cap[3] || ''
});
continue;
}
// heading
if (cap = this.rules.heading.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
type: 'heading',
depth: cap[1].length,
text: cap[2]
});
continue;
}
// table no leading pipe (gfm)
if (top && (cap = this.rules.nptable.exec(src))) {
src = src.substring(cap[0].length);
item = {
type: 'table',
header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
cells: cap[3].replace(/\n$/, '').split('\n')
};
for (i = 0; i < item.align.length; i++) {
if (/^ *-+: *$/.test(item.align[i])) {
item.align[i] = 'right';
} else if (/^ *:-+: *$/.test(item.align[i])) {
item.align[i] = 'center';
} else if (/^ *:-+ *$/.test(item.align[i])) {
item.align[i] = 'left';
} else {
item.align[i] = null;
}
}
for (i = 0; i < item.cells.length; i++) {
item.cells[i] = splitCells(item.cells[i]);
}
this.tokens.push(item);
continue;
}
// hr
if (cap = this.rules.hr.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
type: 'hr'
});
continue;
}
// blockquote
if (cap = this.rules.blockquote.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
type: 'blockquote_start'
});
cap = cap[0].replace(/^ *> ?/gm, '');
// Pass `top` to keep the current
// "toplevel" state. This is exactly
// how markdown.pl works.
this.token(cap, top);
this.tokens.push({
type: 'blockquote_end'
});
continue;
}
// list
if (cap = this.rules.list.exec(src)) {
src = src.substring(cap[0].length);
bull = cap[2];
isordered = bull.length > 1;
this.tokens.push({
type: 'list_start',
ordered: isordered,
start: isordered ? +bull : ''
});
// Get each top-level item.
cap = cap[0].match(this.rules.item);
next = false;
l = cap.length;
i = 0;
for (; i < l; i++) {
item = cap[i];
// Remove the list item's bullet
// so it is seen as the next token.
space = item.length;
item = item.replace(/^ *([*+-]|\d+\.) +/, '');
// Outdent whatever the
// list item contains. Hacky.
if (~item.indexOf('\n ')) {
space -= item.length;
item = !this.options.pedantic
? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
: item.replace(/^ {1,4}/gm, '');
}
// Determine whether the next list item belongs here.
// Backpedal if it does not belong in this list.
if (this.options.smartLists && i !== l - 1) {
b = block.bullet.exec(cap[i + 1])[0];
if (bull !== b && !(bull.length > 1 && b.length > 1)) {
src = cap.slice(i + 1).join('\n') + src;
i = l - 1;
}
}
// Determine whether item is loose or not.
// Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
// for discount behavior.
loose = next || /\n\n(?!\s*$)/.test(item);
if (i !== l - 1) {
next = item.charAt(item.length - 1) === '\n';
if (!loose) loose = next;
}
this.tokens.push({
type: loose
? 'loose_item_start'
: 'list_item_start'
});
// Recurse.
this.token(item, false);
this.tokens.push({
type: 'list_item_end'
});
}
this.tokens.push({
type: 'list_end'
});
continue;
}
// dl
if (cap = this.rules.dl.exec(src)) {
// TODO
this.tokens.push({
type: 'dl_start',
});
this.tokens.push({
type: 'dt_start',
});
this.tokens.push({
type: 'text',
text: cap[1]
});
this.tokens.push({
type: 'dt_end',
});
this.tokens.push({
type: 'dd_start',
});
this.tokens.push({
type: 'text',
text: cap[2]
});
this.tokens.push({
type: 'dd_end',
});
src = src.substring(cap[0].length);
this.tokens.push({
type: 'dl_end'
});
continue;
}
// html
if (cap = this.rules.html.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
type: this.options.sanitize
? 'paragraph'
: 'html',
pre: !this.options.sanitizer
&& (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
text: cap[0]
});
continue;
}
// def
if (top && (cap = this.rules.def.exec(src))) {
src = src.substring(cap[0].length);
if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
if (!this.tokens.links[tag]) {
this.tokens.links[tag] = {
href: cap[2],
title: cap[3]
};
}
continue;
}
// table (gfm)
if (top && (cap = this.rules.table.exec(src))) {
src = src.substring(cap[0].length);
item = {
type: 'table',
header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
};
for (i = 0; i < item.align.length; i++) {
if (/^ *-+: *$/.test(item.align[i])) {
item.align[i] = 'right';
} else if (/^ *:-+: *$/.test(item.align[i])) {
item.align[i] = 'center';
} else if (/^ *:-+ *$/.test(item.align[i])) {
item.align[i] = 'left';
} else {
item.align[i] = null;
}
}
for (i = 0; i < item.cells.length; i++) {
item.cells[i] = splitCells(
item.cells[i].replace(/^ *\| *| *\| *$/g, ''));
}
this.tokens.push(item);
continue;
}
// lheading
if (cap = this.rules.lheading.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
type: 'heading',
depth: cap[2] === '=' ? 1 : 2,
text: cap[1]
});
continue;
}
// top-level paragraph
if (top && (cap = this.rules.paragraph.exec(src))) {
src = src.substring(cap[0].length);
this.tokens.push({
type: 'paragraph',
text: cap[1].charAt(cap[1].length - 1) === '\n'
? cap[1].slice(0, -1)
: cap[1]
});
continue;
}
// text
if (cap = this.rules.text.exec(src)) {
// Top-level should never reach here.
src = src.substring(cap[0].length);
this.tokens.push({
type: 'text',
text: cap[0]
});
continue;
}
if (src) {
throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
}
}
return this.tokens;
};
/**
* Inline-Level Grammar
*/
var inline = {
escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
url: noop,
tag: '^comment'
+ '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
+ '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
+ '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
+ '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
+ '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
// fix *x* **x** one character em/strong formatters
// strong: /^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)|^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)/,
// em: /^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*][\s\S]*?[^\s])\*(?!\*)|^_([^\s_])_(?!_)|^\*([^\s*])\*(?!\*)/,
strong: /^__[^\s_\*]__|^\*\*[^\s]\*\*|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)|^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)/,
em: /^_[^\s_\*]_|^\*[^\s_\*]\*|^_([^\s][\s\S]*?[^\s_])_(?!_)|^_([^\s_][\s\S]*?[^\s])_(?!_)|^\*([^\s][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*][\s\S]*?[^\s])\*(?!\*)|^_([^\s_])_(?!_)|^\*([^\s*])\*(?!\*)/,
code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/,
br: /^ {2,}\n(?!\s*$)/,
del: noop,
text: /^[\s\S]+?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
};
inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
inline.autolink = edit(inline.autolink)
.replace('scheme', inline._scheme)
.replace('email', inline._email)
.getRegex();
inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
inline.tag = edit(inline.tag)
.replace('comment', block._comment)
.replace('attribute', inline._attribute)
.getRegex();
inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?)/;
inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
inline.link = edit(inline.link)
.replace('label', inline._label)
.replace('href', inline._href)
.replace('title', inline._title)
.getRegex();
inline.reflink = edit(inline.reflink)
.replace('label', inline._label)
.getRegex();
/**
* Normal Inline Grammar
*/
inline.normal = merge({}, inline);
/**
* Pedantic Inline Grammar
*/
inline.pedantic = merge({}, inline.normal, {
strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
link: edit(/^!?\[(label)\]\((.*?)\)/)
.replace('label', inline._label)
.getRegex(),
reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
.replace('label', inline._label)
.getRegex()
});
/**
* GFM Inline Grammar
*/
inline.gfm = merge({}, inline.normal, {
escape: edit(inline.escape).replace('])', '~|])').getRegex(),
url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/)
.replace('email', inline._email)
.getRegex(),
_backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
del: /^~~(?=\S)([\s\S]*?\S)~~/,
text: edit(inline.text)
.replace(']|', '~]|')
.replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|')
.getRegex()
});
/**
* GFM + Line Breaks Inline Grammar
*/
inline.breaks = merge({}, inline.gfm, {
br: edit(inline.br).replace('{2,}', '*').getRegex(),
text: edit(inline.gfm.text).replace('{2,}', '*').getRegex()
});
/**
* Inline Lexer & Compiler
*/
function InlineLexer(links, options) {
this.options = options || marked.defaults;
this.links = links;
this.rules = inline.normal;
this.renderer = this.options.renderer || new Renderer();
this.renderer.options = this.options;
if (!this.links) {
throw new Error('Tokens array requires a `links` property.');
}
if (this.options.pedantic) {
this.rules = inline.pedantic;
} else if (this.options.gfm) {
if (this.options.breaks) {
this.rules = inline.breaks;
} else {
this.rules = inline.gfm;
}
}
}
/**
* Expose Inline Rules
*/
InlineLexer.rules = inline;
/**
* Static Lexing/Compiling Method
*/
InlineLexer.output = function(src, links, options) {
var inline = new InlineLexer(links, options);
return inline.output(src);
};
/**
* Lexing/Compiling
*/
InlineLexer.prototype.output = function(src) {
var out = '',
link,
text,
href,
title,
cap;
while (src) {
// escape
if (cap = this.rules.escape.exec(src)) {
src = src.substring(cap[0].length);
out += cap[1];
continue;
}
// autolink
if (cap = this.rules.autolink.exec(src)) {
src = src.substring(cap[0].length);
if (cap[2] === '@') {
text = escape(this.mangle(cap[1]));
href = 'mailto:' + text;
} else {
text = escape(cap[1]);
href = text;
}
out += this.renderer.link(href, null, text);
continue;
}
// url (gfm)
if (!this.inLink && (cap = this.rules.url.exec(src))) {
cap[0] = this.rules._backpedal.exec(cap[0])[0];
src = src.substring(cap[0].length);
if (cap[2] === '@') {
text = escape(cap[0]);
href = 'mailto:' + text;
} else {
text = escape(cap[0]);
if (cap[1] === 'www.') {
href = 'http://' + text;
} else {
href = text;
}
}
out += this.renderer.link(href, null, text);
continue;
}
// tag
if (cap = this.rules.tag.exec(src)) {
if (!this.inLink && /^<a /i.test(cap[0])) {
this.inLink = true;
} else if (this.inLink && /^<\/a>/i.test(cap[0])) {
this.inLink = false;
}
src = src.substring(cap[0].length);
out += this.options.sanitize
? this.options.sanitizer
? this.options.sanitizer(cap[0])
: escape(cap[0])
: cap[0]
continue;
}
// link
if (cap = this.rules.link.exec(src)) {
src = src.substring(cap[0].length);
this.inLink = true;
href = cap[2];
if (this.options.pedantic) {
link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
if (link) {
href = link[1];
title = link[3];
} else {
title = '';
}
} else {
title = cap[3] ? cap[3].slice(1, -1) : '';
}
href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
out += this.outputLink(cap, {
href: InlineLexer.escapes(href),
title: InlineLexer.escapes(title)
});
this.inLink = false;
continue;
}
// reflink, nolink
if ((cap = this.rules.reflink.exec(src))
|| (cap = this.rules.nolink.exec(src))) {
src = src.substring(cap[0].length);
link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
link = this.links[link.toLowerCase()];
if (!link || !link.href) {
out += cap[0].charAt(0);
src = cap[0].substring(1) + src;
continue;
}
this.inLink = true;
out += this.outputLink(cap, link);
this.inLink = false;
continue;
}
// strong
if (cap = this.rules.strong.exec(src)) {
src = src.substring(cap[0].length);
out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));
continue;
}
// em
if (cap = this.rules.em.exec(src)) {
src = src.substring(cap[0].length);
out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));
continue;
}
// code
if (cap = this.rules.code.exec(src)) {
src = src.substring(cap[0].length);
out += this.renderer.codespan(escape(cap[2].trim(), true));
continue;
}
// br
if (cap = this.rules.br.exec(src)) {
src = src.substring(cap[0].length);
out += this.renderer.br();
continue;
}
// del (gfm)
if (cap = this.rules.del.exec(src)) {
src = src.substring(cap[0].length);
out += this.renderer.del(this.output(cap[1]));
continue;
}
// text
if (cap = this.rules.text.exec(src)) {
src = src.substring(cap[0].length);
out += this.renderer.text(escape(this.smartypants(cap[0])));
continue;
}
if (src) {
throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
}
}
return out;
};
InlineLexer.escapes = function(text) {
return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
}
/**
* Compile Link
*/
InlineLexer.prototype.outputLink = function(cap, link) {
var href = link.href,
title = link.title ? escape(link.title) : null;
return cap[0].charAt(0) !== '!'
? this.renderer.link(href, title, this.output(cap[1]))
: this.renderer.image(href, title, escape(cap[1]));
};
/**
* Smartypants Transformations
*/
InlineLexer.prototype.smartypants = function(text) {
if (!this.options.smartypants) return text;
return text
// em-dashes
.replace(/---/g, '\u2014')
// en-dashes
.replace(/--/g, '\u2013')
// opening singles
.replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
// closing singles & apostrophes
.replace(/'/g, '\u2019')
// opening doubles
.replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
// closing doubles
.replace(/"/g, '\u201d')
// ellipses
.replace(/\.{3}/g, '\u2026');
};
/**
* Mangle Links
*/
InlineLexer.prototype.mangle = function(text) {
if (!this.options.mangle) return text;
var out = '',
l = text.length,
i = 0,
ch;
for (; i < l; i++) {
ch = text.charCodeAt(i);
if (Math.random() > 0.5) {
ch = 'x' + ch.toString(16);
}
out += '&#' + ch + ';';
}
return out;
};
/**
* Renderer
*/
function Renderer(options) {
this.options = options || marked.defaults;
}
Renderer.prototype.code = function(code, lang, escaped) {
if (this.options.highlight) {
var out = this.options.highlight(code, lang);
if (out != null && out !== code) {
escaped = true;
code = out;
}
}
if (!lang) {
return '<pre><code>'
+ (escaped ? code : escape(code, true))
+ '\n</code></pre>';
}
return '<pre><code class="'
+ this.options.langPrefix
+ escape(lang, true)
+ '">'
+ (escaped ? code : escape(code, true))
+ '\n</code></pre>\n';
};
Renderer.prototype.blockquote = function(quote) {
return '<blockquote>\n' + quote + '</blockquote>\n';
};
Renderer.prototype.html = function(html) {
return html;
};
Renderer.prototype.heading = function(text, level, raw) {
if (this.options.headerIds) {
return '<h'
+ level
+ ' id="'
+ this.options.headerPrefix
+ raw.toLowerCase().replace(/[^\w]+/g, '-')
+ '">'
+ text
+ '</h'
+ level
+ '>\n';
}
// ignore IDs
return '<h' + level + '>' + text + '</h' + level + '>\n';
};
Renderer.prototype.hr = function() {
return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
};
Renderer.prototype.list = function(body, ordered, start) {
var type = ordered ? 'ol' : 'ul',
startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
};
Renderer.prototype.listitem = function(text) {
return '<li>' + text + '</li>\n';
};
Renderer.prototype.dl = function(body) {
return '<dl>\n' + body + '</dl>\n';
};
Renderer.prototype.dt = function(body) {
return '<dt>' + body + '</dt>\n';
};
Renderer.prototype.dd = function(body) {
return '<dd>' + body + '</dd>\n';
};
Renderer.prototype.paragraph = function(text) {
return '<p>' + text + '</p>\n';
};
Renderer.prototype.table = function(header, body) {
return '<table>\n'
+ '<thead>\n'
+ header
+ '</thead>\n'
+ '<tbody>\n'
+ body
+ '</tbody>\n'
+ '</table>\n';
};
Renderer.prototype.tablerow = function(content) {
return '<tr>\n' + content + '</tr>\n';
};
Renderer.prototype.tablecell = function(content, flags) {
var type = flags.header ? 'th' : 'td';
var tag = flags.align
? '<' + type + ' style="text-align:' + flags.align + '">'
: '<' + type + '>';
return tag + content + '</' + type + '>\n';
};
// span level renderer
Renderer.prototype.strong = function(text) {
return '<strong>' + text + '</strong>';
};
Renderer.prototype.em = function(text) {
return '<em>' + text + '</em>';
};
Renderer.prototype.codespan = function(text) {
return '<code>' + text + '</code>';
};
Renderer.prototype.br = function() {
return this.options.xhtml ? '<br/>' : '<br>';
};
Renderer.prototype.del = function(text) {
return '<del>' + text + '</del>';
};
Renderer.prototype.link = function(href, title, text) {
if (this.options.sanitize) {
try {
var prot = decodeURIComponent(unescape(href))
.replace(/[^\w:]/g, '')
.toLowerCase();
} catch (e) {
return text;
}
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
return text;
}
}
if (this.options.baseUrl && !originIndependentUrl.test(href)) {
href = resolveUrl(this.options.baseUrl, href);
}
try {
href = encodeURI(href).replace(/%25/g, '%');
} catch (e) {
return text;
}
var out = '<a href="' + escape(href) + '"';
if (title) {
out += ' title="' + title + '"';
}
out += '>' + text + '</a>';
return out;
};
Renderer.prototype.image = function(href, title, text) {
if (this.options.baseUrl && !originIndependentUrl.test(href)) {
href = resolveUrl(this.options.baseUrl, href);
}
var out = '<img src="' + href + '" alt="' + text + '"';
if (title) {
out += ' title="' + title + '"';
}
out += this.options.xhtml ? '/>' : '>';
return out;
};
Renderer.prototype.text = function(text) {
return text;
};
/**
* TextRenderer
* returns only the textual part of the token
*/
function TextRenderer() {}
// no need for block level renderers
TextRenderer.prototype.strong =
TextRenderer.prototype.em =
TextRenderer.prototype.codespan =
TextRenderer.prototype.del =
TextRenderer.prototype.text = function (text) {
return text;
}
TextRenderer.prototype.link =
TextRenderer.prototype.image = function(href, title, text) {
return '' + text;
}
TextRenderer.prototype.br = function() {
return '';
}
/**
* Parsing & Compiling
*/
function Parser(options) {
this.tokens = [];
this.token = null;
this.options = options || marked.defaults;
this.options.renderer = this.options.renderer || new Renderer();
this.renderer = this.options.renderer;
this.renderer.options = this.options;
}
/**
* Static Parse Method
*/
Parser.parse = function(src, options) {
var parser = new Parser(options);
return parser.parse(src);
};
/**
* Parse Loop
*/
Parser.prototype.parse = function(src) {
this.inline = new InlineLexer(src.links, this.options);
// use an InlineLexer with a TextRenderer to extract pure text
this.inlineText = new InlineLexer(
src.links,
merge({}, this.options, {renderer: new TextRenderer()})
);
this.tokens = src.reverse();
var out = '';
while (this.next()) {
out += this.tok();
}
return out;
};
/**
* Next Token
*/
Parser.prototype.next = function() {
return this.token = this.tokens.pop();
};
/**
* Preview Next Token
*/
Parser.prototype.peek = function() {
return this.tokens[this.tokens.length - 1] || 0;
};
/**
* Parse Text Tokens
*/
Parser.prototype.parseText = function() {
var body = this.token.text;
while (this.peek().type === 'text') {
body += '\n' + this.next().text;
}
return this.inline.output(body);
};
/**
* Parse Current Token
*/
Parser.prototype.tok = function() {
switch (this.token.type) {
case 'space': {
return '';
}
case 'hr': {
return this.renderer.hr();
}
case 'heading': {
return this.renderer.heading(
this.inline.output(this.token.text),
this.token.depth,
unescape(this.inlineText.output(this.token.text)));
}
case 'code': {
return this.renderer.code(this.token.text,
this.token.lang,
this.token.escaped);
}
case 'table': {
var header = '',
body = '',
i,
row,
cell,
j;
// header
cell = '';
for (i = 0; i < this.token.header.length; i++) {
cell += this.renderer.tablecell(
this.inline.output(this.token.header[i]),
{ header: true, align: this.token.align[i] }
);
}
header += this.renderer.tablerow(cell);
for (i = 0; i < this.token.cells.length; i++) {
row = this.token.cells[i];
cell = '';
for (j = 0; j < row.length; j++) {
cell += this.renderer.tablecell(
this.inline.output(row[j]),
{ header: false, align: this.token.align[j] }
);
}
body += this.renderer.tablerow(cell);
}
return this.renderer.table(header, body);
}
case 'blockquote_start': {
body = '';
while (this.next().type !== 'blockquote_end') {
body += this.tok();
}
return this.renderer.blockquote(body);
}
case 'list_start': {
body = '';
var ordered = this.token.ordered,
start = this.token.start;
while (this.next().type !== 'list_end') {
body += this.tok();
}
return this.renderer.list(body, ordered, start);
}
case 'list_item_start': {
body = '';
while (this.next().type !== 'list_item_end') {
body += this.token.type === 'text'
? this.parseText()
: this.tok();
}
return this.renderer.listitem(body);
}
case 'loose_item_start': {
body = '';
while (this.next().type !== 'list_item_end') {
body += this.tok();
}
return this.renderer.listitem(body);
}
case 'dl_start': {
body = '';
while (this.next().type !== 'dl_end') {
body += this.tok();
}
return this.renderer.dl(body);
}
case 'dt_start': {
body = '';
while (this.next().type !== 'dt_end') {
body += this.parseText();
}
return this.renderer.dt(body);
}
case 'dd_start': {
body = '';
while (this.next().type !== 'dd_end') {
body += this.parseText();
}
return this.renderer.dd(body);
}
case 'html': {
// TODO parse inline content if parameter markdown=1
return this.renderer.html(this.token.text);
}
case 'paragraph': {
return this.renderer.paragraph(this.inline.output(this.token.text));
}
case 'text': {
return this.renderer.paragraph(this.parseText());
}
}
};
/**
* Helpers
*/
function escape(html, encode) {
return html
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function unescape(html) {
// explicitly match decimal, hex, and named HTML entities
return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
n = n.toLowerCase();
if (n === 'colon') return ':';
if (n.charAt(0) === '#') {
return n.charAt(1) === 'x'
? String.fromCharCode(parseInt(n.substring(2), 16))
: String.fromCharCode(+n.substring(1));
}
return '';
});
}
function edit(regex, opt) {
regex = regex.source || regex;
opt = opt || '';
return {
replace: function(name, val) {
val = val.source || val;
val = val.replace(/(^|[^\[])\^/g, '$1');
regex = regex.replace(name, val);
return this;
},
getRegex: function() {
return new RegExp(regex, opt);
}
};
}
function resolveUrl(base, href) {
if (!baseUrls[' ' + base]) {
// we can ignore everything in base after the last slash of its path component,
// but we might need to add _that_
// https://tools.ietf.org/html/rfc3986#section-3
if (/^[^:]+:\/*[^/]*$/.test(base)) {
baseUrls[' ' + base] = base + '/';
} else {
baseUrls[' ' + base] = base.replace(/[^/]*$/, '');
}
}
base = baseUrls[' ' + base];
if (href.slice(0, 2) === '//') {
return base.replace(/:[\s\S]*/, ':') + href;
} else if (href.charAt(0) === '/') {
return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href;
} else {
return base + href;
}
}
var baseUrls = {};
var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
function noop() {}
noop.exec = noop;
function merge(obj) {
var i = 1,
target,
key;
for (; i < arguments.length; i++) {
target = arguments[i];
for (key in target) {
if (Object.prototype.hasOwnProperty.call(target, key)) {
obj[key] = target[key];
}
}
}
return obj;
}
function splitCells(tableRow) {
var cells = tableRow.replace(/([^\\])\|/g, '$1 |').split(/ +\| */),
i = 0;
for (; i < cells.length; i++) {
cells[i] = cells[i].replace(/\\\|/g, '|');
}
return cells;
}
/**
* Marked
*/
function marked(src, opt, callback) {
// throw error in case of non string input
if (typeof src === 'undefined' || src === null) {
throw new Error('marked(): input parameter is undefined or null');
}
if (typeof src !== 'string') {
throw new Error('marked(): input parameter is of type '
+ Object.prototype.toString.call(src) + ', string expected');
}
if (callback || typeof opt === 'function') {
if (!callback) {
callback = opt;
opt = null;
}
opt = merge({}, marked.defaults, opt || {});
var highlight = opt.highlight,
tokens,
pending,
i = 0;
try {
tokens = Lexer.lex(src, opt)
} catch (e) {
return callback(e);
}
pending = tokens.length;
var done = function(err) {
if (err) {
opt.highlight = highlight;
return callback(err);
}
var out;
try {
out = Parser.parse(tokens, opt);
} catch (e) {
err = e;
}
opt.highlight = highlight;
return err
? callback(err)
: callback(null, out);
};
if (!highlight || highlight.length < 3) {
return done();
}
delete opt.highlight;
if (!pending) return done();
for (; i < tokens.length; i++) {
(function(token) {
if (token.type !== 'code') {
return --pending || done();
}
return highlight(token.text, token.lang, function(err, code) {
if (err) return done(err);
if (code == null || code === token.text) {
return --pending || done();
}
token.text = code;
token.escaped = true;
--pending || done();
});
})(tokens[i]);
}
return;
}
try {
if (opt) opt = merge({}, marked.defaults, opt);
return Parser.parse(Lexer.lex(src, opt), opt);
} catch (e) {
e.message += '\nPlease report this to https://github.com/markedjs/marked.';
if ((opt || marked.defaults).silent) {
return '<p>An error occurred:</p><pre>'
+ escape(e.message + '', true)
+ '</pre>';
}
throw e;
}
}
/**
* Options
*/
marked.options =
marked.setOptions = function(opt) {
merge(marked.defaults, opt);
return marked;
};
marked.getDefaults = function () {
return {
baseUrl: null,
breaks: false,
gfm: true,
headerIds: true,
headerPrefix: '',
highlight: null,
langPrefix: 'lang-',
mangle: true,
pedantic: false,
renderer: new Renderer(),
sanitize: false,
sanitizer: null,
silent: false,
smartLists: false,
smartypants: false,
tables: true,
xhtml: false
};
}
marked.defaults = marked.getDefaults();
/**
* Expose
*/
marked.Parser = Parser;
marked.parser = Parser.parse;
marked.Renderer = Renderer;
marked.TextRenderer = TextRenderer;
marked.Lexer = Lexer;
marked.lexer = Lexer.lex;
marked.InlineLexer = InlineLexer;
marked.inlineLexer = InlineLexer.output;
marked.parse = marked;
return marked;
}
};
BundleModuleCode['doc/colors']=function (module,exports){
/*
The MIT License (MIT)
Original Library
- Copyright (c) Marak Squires
Additional functionality
- Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
var colors = {};
module['exports'] = colors;
colors.themes = {};
var ansiStyles = colors.styles = Require('doc/styles');
var defineProps = Object.defineProperties;
colors.supportsColor = Require('doc/system/supports-colors').supportsColor;
if (typeof colors.enabled === "undefined") {
colors.enabled = colors.supportsColor() !== false;
}
colors.stripColors = colors.strip = function(str){
return ("" + str).replace(/\x1B\[\d+m/g, '');
};
var stylize = colors.stylize = function stylize (str, style) {
if (!colors.enabled) {
return str+'';
}
return ansiStyles[style].open + str + ansiStyles[style].close;
}
var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
var escapeStringRegexp = function (str) {
if (typeof str !== 'string') {
throw new TypeError('Expected a string');
}
return str.replace(matchOperatorsRe, '\\$&');
}
function build(_styles) {
var builder = function builder() {
return applyStyle.apply(builder, arguments);
};
builder._styles = _styles;
// __proto__ is used because we must return a function, but there is
// no way to create a function with a different prototype.
builder.__proto__ = proto;
return builder;
}
var styles = (function () {
var ret = {};
ansiStyles.grey = ansiStyles.gray;
Object.keys(ansiStyles).forEach(function (key) {
ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g');
ret[key] = {
get: function () {
return build(this._styles.concat(key));
}
};
});
return ret;
})();
var proto = defineProps(function colors() {}, styles);
function applyStyle() {
var args = arguments;
var argsLen = args.length;
var str = argsLen !== 0 && String(arguments[0]);
if (argsLen > 1) {
for (var a = 1; a < argsLen; a++) {
str += ' ' + args[a];
}
}
if (!colors.enabled || !str) {
return str;
}
var nestedStyles = this._styles;
var i = nestedStyles.length;
while (i--) {
var code = ansiStyles[nestedStyles[i]];
str = code.open + str.replace(code.closeRe, code.open) + code.close;
}
return str;
}
colors.setTheme = function (theme) {
if (typeof theme === 'string') {
console.log('colors.setTheme now only accepts an object, not a string. ' +
'If you are trying to set a theme from a file, it is now your (the caller\'s) responsibility to require the file. ' +
'The old syntax looked like colors.setTheme(__dirname + \'/../themes/generic-logging.js\'); ' +
'The new syntax looks like colors.setTheme(require(__dirname + \'/../themes/generic-logging.js\'));');
return;
}
for (var style in theme) {
(function(style){
colors[style] = function(str){
if (typeof theme[style] === 'object'){
var out = str;
for (var i in theme[style]){
out = colors[theme[style][i]](out);
}
return out;
}
return colors[theme[style]](str);
};
})(style)
}
}
function init() {
var ret = {};
Object.keys(styles).forEach(function (name) {
ret[name] = {
get: function () {
return build([name]);
}
};
});
return ret;
}
var sequencer = function sequencer (map, str) {
var exploded = str.split(""), i = 0;
exploded = exploded.map(map);
return exploded.join("");
};
// custom formatter methods
colors.trap = Require('doc/custom/trap');
colors.zalgo = Require('doc/custom/zalgo');
// maps
colors.maps = {};
colors.maps.america = (function() {
return function (letter, i, exploded) {
if(letter === " ") return letter;
switch(i%3) {
case 0: return colors.red(letter);
case 1: return colors.white(letter)
case 2: return colors.blue(letter)
}
}
})();
colors.maps.zebra = function (letter, i, exploded) {
return i % 2 === 0 ? letter : colors.inverse(letter);
}
colors.maps.rainbow = (function () {
var rainbowColors = ['red', 'yellow', 'green', 'blue', 'magenta']; //RoY G BiV
return function (letter, i, exploded) {
if (letter === " ") {
return letter;
} else {
return colors[rainbowColors[i++ % rainbowColors.length]](letter);
}
};
})();
colors.maps.random = (function () {
var available = ['underline', 'inverse', 'grey', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta'];
return function(letter, i, exploded) {
return letter === " " ? letter : colors[available[Math.round(Math.random() * (available.length - 1))]](letter);
};
})();
for (var map in colors.maps) {
(function(map){
colors[map] = function (str) {
return sequencer(colors.maps[map], str);
}
})(map)
}
defineProps(colors, init());
};
BundleModuleCode['doc/styles']=function (module,exports){
/*
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
var styles = {};
module['exports'] = styles;
var codes = {
reset: [0, 0],
bold: [1, 22],
dim: [2, 22],
italic: [3, 23],
underline: [4, 24],
inverse: [7, 27],
hidden: [8, 28],
strikethrough: [9, 29],
black: [30, 39],
red: [31, 39],
green: [32, 39],
yellow: [33, 39],
blue: [34, 39],
magenta: [35, 39],
cyan: [36, 39],
white: [37, 39],
gray: [90, 39],
grey: [90, 39],
bgBlack: [40, 49],
bgRed: [41, 49],
bgGreen: [42, 49],
bgYellow: [43, 49],
bgBlue: [44, 49],
bgMagenta: [45, 49],
bgCyan: [46, 49],
bgWhite: [47, 49],
// legacy styles for colors pre v1.0.0
blackBG: [40, 49],
redBG: [41, 49],
greenBG: [42, 49],
yellowBG: [43, 49],
blueBG: [44, 49],
magentaBG: [45, 49],
cyanBG: [46, 49],
whiteBG: [47, 49]
};
Object.keys(codes).forEach(function (key) {
var val = codes[key];
var style = styles[key] = [];
style.open = '\u001b[' + val[0] + 'm';
style.close = '\u001b[' + val[1] + 'm';
});};
BundleModuleCode['doc/system/supports-colors']=function (module,exports){
/*
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
'use strict';
var os = Require('os');
var hasFlag = Require('doc/system/has-flag.js');
var env = process.env;
var forceColor = void 0;
if (hasFlag('no-color') || hasFlag('no-colors') || hasFlag('color=false')) {
forceColor = false;
} else if (hasFlag('color') || hasFlag('colors') || hasFlag('color=true') || hasFlag('color=always')) {
forceColor = true;
}
if ('FORCE_COLOR' in env) {
forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0;
}
function translateLevel(level) {
if (level === 0) {
return false;
}
return {
level: level,
hasBasic: true,
has256: level >= 2,
has16m: level >= 3
};
}
function supportsColor(stream) {
if (forceColor === false) {
return 0;
}
if (hasFlag('color=16m') || hasFlag('color=full') || hasFlag('color=truecolor')) {
return 3;
}
if (hasFlag('color=256')) {
return 2;
}
if (stream && !stream.isTTY && forceColor !== true) {
return 0;
}
var min = forceColor ? 1 : 0;
if (process.platform === 'win32') {
// Node.js 7.5.0 is the first version of Node.js to include a patch to
// libuv that enables 256 color output on Windows. Anything earlier and it
// won't work. However, here we target Node.js 8 at minimum as it is an LTS
// release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows
// release that supports 256 colors. Windows 10 build 14931 is the first release
// that supports 16m/TrueColor.
var osRelease = os.release().split('.');
if (Number(process.versions.node.split('.')[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
return Number(osRelease[2]) >= 14931 ? 3 : 2;
}
return 1;
}
if ('CI' in env) {
if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(function (sign) {
return sign in env;
}) || env.CI_NAME === 'codeship') {
return 1;
}
return min;
}
if ('TEAMCITY_VERSION' in env) {
return (/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0
);
}
if ('TERM_PROGRAM' in env) {
var version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);
switch (env.TERM_PROGRAM) {
case 'iTerm.app':
return version >= 3 ? 3 : 2;
case 'Hyper':
return 3;
case 'Apple_Terminal':
return 2;
// No default
}
}
if (/-256(color)?$/i.test(env.TERM)) {
return 2;
}
if (/^screen|^xterm|^vt100|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
return 1;
}
if ('COLORTERM' in env) {
return 1;
}
if (env.TERM === 'dumb') {
return min;
}
return min;
}
function getSupportLevel(stream) {
var level = supportsColor(stream);
return translateLevel(level);
}
module.exports = {
supportsColor: getSupportLevel,
stdout: getSupportLevel(process.stdout),
stderr: getSupportLevel(process.stderr)
};
};
BundleModuleCode['doc/system/has-flag.js']=function (module,exports){
/*
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
module.exports = function (flag, argv) {
argv = argv || process.argv;
var terminatorPos = argv.indexOf('--');
var prefix = /^-{1,2}/.test(flag) ? '' : '--';
var pos = argv.indexOf(prefix + flag);
return pos !== -1 && (terminatorPos === -1 ? true : pos < terminatorPos);
};
};
BundleModuleCode['doc/custom/trap']=function (module,exports){
module['exports'] = function runTheTrap (text, options) {
var result = "";
text = text || "Run the trap, drop the bass";
text = text.split('');
var trap = {
a: ["\u0040", "\u0104", "\u023a", "\u0245", "\u0394", "\u039b", "\u0414"],
b: ["\u00df", "\u0181", "\u0243", "\u026e", "\u03b2", "\u0e3f"],
c: ["\u00a9", "\u023b", "\u03fe"],
d: ["\u00d0", "\u018a", "\u0500" , "\u0501" ,"\u0502", "\u0503"],
e: ["\u00cb", "\u0115", "\u018e", "\u0258", "\u03a3", "\u03be", "\u04bc", "\u0a6c"],
f: ["\u04fa"],
g: ["\u0262"],
h: ["\u0126", "\u0195", "\u04a2", "\u04ba", "\u04c7", "\u050a"],
i: ["\u0f0f"],
j: ["\u0134"],
k: ["\u0138", "\u04a0", "\u04c3", "\u051e"],
l: ["\u0139"],
m: ["\u028d", "\u04cd", "\u04ce", "\u0520", "\u0521", "\u0d69"],
n: ["\u00d1", "\u014b", "\u019d", "\u0376", "\u03a0", "\u048a"],
o: ["\u00d8", "\u00f5", "\u00f8", "\u01fe", "\u0298", "\u047a", "\u05dd", "\u06dd", "\u0e4f"],
p: ["\u01f7", "\u048e"],
q: ["\u09cd"],
r: ["\u00ae", "\u01a6", "\u0210", "\u024c", "\u0280", "\u042f"],
s: ["\u00a7", "\u03de", "\u03df", "\u03e8"],
t: ["\u0141", "\u0166", "\u0373"],
u: ["\u01b1", "\u054d"],
v: ["\u05d8"],
w: ["\u0428", "\u0460", "\u047c", "\u0d70"],
x: ["\u04b2", "\u04fe", "\u04fc", "\u04fd"],
y: ["\u00a5", "\u04b0", "\u04cb"],
z: ["\u01b5", "\u0240"]
}
text.forEach(function(c){
c = c.toLowerCase();
var chars = trap[c] || [" "];
var rand = Math.floor(Math.random() * chars.length);
if (typeof trap[c] !== "undefined") {
result += trap[c][rand];
} else {
result += c;
}
});
return result;
}
};
BundleModuleCode['doc/custom/zalgo']=function (module,exports){
// please no
module['exports'] = function zalgo(text, options) {
text = text || " he is here ";
var soul = {
"up" : [
'̍', '̎', '̄', '̅',
'̿', '̑', '̆', '̐',
'͒', '͗', '͑', '̇',
'̈', '̊', '͂', '̓',
'̈', '͊', '͋', '͌',
'̃', '̂', '̌', '͐',
'̀', '́', '̋', '̏',
'̒', '̓', '̔', '̽',
'̉', 'ͣ', 'ͤ', 'ͥ',
'ͦ', 'ͧ', 'ͨ', 'ͩ',
'ͪ', 'ͫ', 'ͬ', 'ͭ',
'ͮ', 'ͯ', '̾', '͛',
'͆', '̚'
],
"down" : [
'̖', '̗', '̘', '̙',
'̜', '̝', '̞', '̟',
'̠', '̤', '̥', '̦',
'̩', '̪', '̫', '̬',
'̭', '̮', '̯', '̰',
'̱', '̲', '̳', '̹',
'̺', '̻', '̼', 'ͅ',
'͇', '͈', '͉', '͍',
'͎', '͓', '͔', '͕',
'͖', '͙', '͚', '̣'
],
"mid" : [
'̕', '̛', '̀', '́',
'͘', '̡', '̢', '̧',
'̨', '̴', '̵', '̶',
'͜', '͝', '͞',
'͟', '͠', '͢', '̸',
'̷', '͡', ' ҉'
]
},
all = [].concat(soul.up, soul.down, soul.mid),
zalgo = {};
function randomNumber(range) {
var r = Math.floor(Math.random() * range);
return r;
}
function is_char(character) {
var bool = false;
all.filter(function (i) {
bool = (i === character);
});
return bool;
}
function heComes(text, options) {
var result = '', counts, l;
options = options || {};
options["up"] = typeof options["up"] !== 'undefined' ? options["up"] : true;
options["mid"] = typeof options["mid"] !== 'undefined' ? options["mid"] : true;
options["down"] = typeof options["down"] !== 'undefined' ? options["down"] : true;
options["size"] = typeof options["size"] !== 'undefined' ? options["size"] : "maxi";
text = text.split('');
for (l in text) {
if (is_char(l)) {
continue;
}
result = result + text[l];
counts = {"up" : 0, "down" : 0, "mid" : 0};
switch (options.size) {
case 'mini':
counts.up = randomNumber(8);
counts.mid = randomNumber(2);
counts.down = randomNumber(8);
break;
case 'maxi':
counts.up = randomNumber(16) + 3;
counts.mid = randomNumber(4) + 1;
counts.down = randomNumber(64) + 3;
break;
default:
counts.up = randomNumber(8) + 1;
counts.mid = randomNumber(6) / 2;
counts.down = randomNumber(8) + 1;
break;
}
var arr = ["up", "mid", "down"];
for (var d in arr) {
var index = arr[d];
for (var i = 0 ; i <= counts[index]; i++) {
if (options[index]) {
result = result + soul[index][randomNumber(soul[index].length)];
}
}
}
}
return result;
}
// don't summon him
return heComes(text, options);
}
};
BundleModuleCode['doc/list']=function (module,exports){
var NL='\n',SP=' ';
function spaces(n) {var s=''; while(n) s+=SP,n--; return s;}
/** List Constructor
* typeof @options = {
* type:string=' '|'*'|'-'|'+'|'1'|'2'|..|'a'|'b'|..|'dl',
* }
*/
function List (options) {
this.type = options.type|| '*';
this.margin = options.margin || {left:0,right:0,top:0,bottom:0};
this.width = options.width || 80;
this.tab = options.tab || 2;
this.tab2 = options.tab || 4;
}
/**
* Inherit from Array. Each item of the list is one array element.
*/
List.prototype.__proto__ = Array.prototype;
/** List formatter
*
*/
List.prototype.render
List.prototype.toString = function (){
var i,self=this,ret='',line='',lines=[],label,
tokens,
textwidth=this.width-this.margin.left-this.margin.right-this.tab;
for(i=0;i<this.margin.top;i++) lines.push([]);
this.forEach(function (item,index) {
label=self.type;
line = spaces (self.margin.left);
switch (label) {
case '*':
case '+':
case '-':
line += label+SP; break;
case ' ': break;
case '1': line += (index+1).toString()+'.'+SP; break;
case '2': line += (index+2).toString()+'.'+SP; break;
case '3': line += (index+3).toString()+'.'+SP; break;
case '4': line += (index+4).toString()+'.'+SP; break;
case '5': line += (index+5).toString()+'.'+SP; break;
case '6': line += (index+6).toString()+'.'+SP; break;
case '7': line += (index+7).toString()+'.'+SP; break;
case '8': line += (index+8).toString()+'.'+SP; break;
case '9': line += (index+9).toString()+'.'+SP; break;
case 'dl':
line += item.dt+NL; label=undefined; item=item.dd; break;
default:
break;
}
line += label?spaces(self.tab-label.length-1):spaces(self.tab);
if (item.length < textwidth) {
line += item;
lines.push(line);
} else {
tokens=item.split(SP); // TODO: preserve nbsp?
tokens.forEach(function (token) {
if ((line.length+token.length+1)<textwidth)
line += (token+SP);
else {
lines.push(line);
line = spaces(self.margin.left+self.tab)+token+SP;
}
});
lines.push(line);
}
});
for(i=0;i<this.margin.bottom;i++) lines.push([]);
return lines.join(NL);
}
module.exports = List;
};
BundleModuleCode['doc/cli-table']=function (module,exports){
/**
* Module dependencies.
*/
var colors = Require('doc/colors')
, utils = Require('doc/cli-utils')
, repeat = utils.repeat
, truncate = utils.truncate
, pad = utils.pad;
/**
* Table constructor
*
* @param {Object} options
* @api public
*/
function Table (options){
this.options = utils.options({
chars: {
'top': '─'
, 'top-mid': '┬'
, 'top-left': '┌'
, 'top-right': '┐'
, 'bottom': '─'
, 'bottom-mid': '┴'
, 'bottom-left': '└'
, 'bottom-right': '┘'
, 'left': '│'
, 'left-mid': '├'
, 'mid': '─'
, 'mid-mid': '┼'
, 'right': '│'
, 'right-mid': '┤'
, 'middle': '│'
}
, truncate: '…'
, colWidths: []
, colAligns: []
, style: {
'padding-left': 1
, 'padding-right': 1
, head: ['red']
, border: ['grey']
, compact : false
}
, head: []
}, options);
};
/**
* Inherit from Array.
*/
Table.prototype.__proto__ = Array.prototype;
/**
* Width getter
*
* @return {Number} width
* @api public
*/
/* Depricated
Table.prototype.__defineGetter__('width', function (){
var str = this.toString().split("\n");
if (str.length) return str[0].length;
return 0;
});
*/
Object.defineProperty(Table.prototype,'width',{
get: function() {
var str = this.toString().split("\n");
if (str.length) return str[0].length;
return 0;
}
});
/**
* Render to a string.
*
* @return {String} table representation
* @api public
*/
Table.prototype.render
Table.prototype.toString = function (){
var ret = ''
, options = this.options
, style = options.style
, head = options.head
, chars = options.chars
, truncater = options.truncate
, colWidths = options.colWidths || new Array(this.head.length)
, totalWidth = 0;
if (!head.length && !this.length) return '';
if (!colWidths.length){
var all_rows = this.slice(0);
if (head.length) { all_rows = all_rows.concat([head]) };
all_rows.forEach(function(cells){
// horizontal (arrays)
if (typeof cells === 'object' && cells.length) {
extractColumnWidths(cells);
// vertical (objects)
} else {
var header_cell = Object.keys(cells)[0]
, value_cell = cells[header_cell];
colWidths[0] = Math.max(colWidths[0] || 0, get_width(header_cell) || 0);
// cross (objects w/ array values)
if (typeof value_cell === 'object' && value_cell.length) {
extractColumnWidths(value_cell, 1);
} else {
colWidths[1] = Math.max(colWidths[1] || 0, get_width(value_cell) || 0);
}
}
});
};
totalWidth = (colWidths.length == 1 ? colWidths[0] : colWidths.reduce(
function (a, b){
return a + b
})) + colWidths.length + 1;
function extractColumnWidths(arr, offset) {
var offset = offset || 0;
arr.forEach(function(cell, i){
colWidths[i + offset] = Math.max(colWidths[i + offset] || 0, get_width(cell) || 0);
});
};
function get_width(obj) {
return typeof obj == 'object' && obj && obj.width != undefined
? obj.width
: ((typeof obj == 'object' && obj !== null ? utils.strlen(obj.text) : utils.strlen(obj)) + (style['padding-left'] || 0) + (style['padding-right'] || 0))
}
// draws a line
function line (line, left, right, intersection){
var width = 0
, line =
left
+ repeat(line, totalWidth - 2)
+ right;
colWidths.forEach(function (w, i){
if (i == colWidths.length - 1) return;
width += w + 1;
line = line.substr(0, width) + intersection + line.substr(width + 1);
});
return applyStyles(options.style.border, line);
};
// draws the top line
function lineTop (){
var l = line(chars.top
, chars['top-left'] || chars.top
, chars['top-right'] || chars.top
, chars['top-mid']);
if (l)
ret += l + "\n";
};
function generateRow (items, style) {
var cells = []
, max_height = 0;
// prepare vertical and cross table data
if (!Array.isArray(items) && typeof items === "object") {
var key = Object.keys(items)[0]
, value = items[key]
, first_cell_head = true;
if (Array.isArray(value)) {
items = value;
items.unshift(key);
} else {
items = [key, value];
}
}
// transform array of item strings into structure of cells
items.forEach(function (item, i) {
var contents = (item == null ? '' : item).toString().split("\n").reduce(function (memo, l) {
memo.push(string(l, i));
return memo;
}, [])
var height = contents.length;
if (height > max_height) { max_height = height };
cells.push({ contents: contents , height: height });
});
// transform vertical cells into horizontal lines
var lines = new Array(max_height);
cells.forEach(function (cell, i) {
cell.contents.forEach(function (line, j) {
if (!lines[j]) { lines[j] = [] };
if (style || (first_cell_head && i === 0 && options.style.head)) {
line = applyStyles(options.style.head, line)
}
lines[j].push(line);
});
// populate empty lines in cell
for (var j = cell.height, l = max_height; j < l; j++) {
if (!lines[j]) { lines[j] = [] };
lines[j].push(string('', i));
}
});
var ret = "";
lines.forEach(function (line, index) {
if (ret.length > 0) {
ret += "\n" + applyStyles(options.style.border, chars.left);
}
ret += line.join(applyStyles(options.style.border, chars.middle)) + applyStyles(options.style.border, chars.right);
});
return applyStyles(options.style.border, chars.left) + ret;
};
function applyStyles(styles, subject) {
if (!subject)
return '';
styles.forEach(function(style) {
subject = colors[style](subject);
});
return subject;
};
// renders a string, by padding it or truncating it
function string (str, index){
var str = String(typeof str == 'object' && str.text ? str.text : str)
, length = utils.strlen(str)
, width = colWidths[index]
- (style['padding-left'] || 0)
- (style['padding-right'] || 0)
, align = options.colAligns[index] || 'left';
return repeat(' ', style['padding-left'] || 0)
+ (length == width ? str :
(length < width
? pad(str, ( width + (str.length - length) ), ' ', align == 'left' ? 'right' :
(align == 'middle' ? 'both' : 'left'))
: (truncater ? truncate(str, width, truncater) : str))
)
+ repeat(' ', style['padding-right'] || 0);
};
if (head.length){
lineTop();
ret += generateRow(head, style.head) + "\n"
}
if (this.length)
this.forEach(function (cells, i){
if (!head.length && i == 0)
lineTop();
else {
if (!style.compact || i<(!!head.length) ?1:0 || cells.length == 0){
var l = line(chars.mid
, chars['left-mid']
, chars['right-mid']
, chars['mid-mid']);
if (l)
ret += l + "\n"
}
}
if (cells.hasOwnProperty("length") && !cells.length) {
return
} else {
ret += generateRow(cells) + "\n";
};
});
var l = line(chars.bottom
, chars['bottom-left'] || chars.bottom
, chars['bottom-right'] || chars.bottom
, chars['bottom-mid']);
if (l)
ret += l;
else
// trim the last '\n' if we didn't add the bottom decoration
ret = ret.slice(0, -1);
return ret;
};
/**
* Module exports.
*/
module.exports = Table;
module.exports.version = '0.0.1';
};
BundleModuleCode['doc/cli-utils']=function (module,exports){
/**
* Repeats a string.
*
* @param {String} char(s)
* @param {Number} number of times
* @return {String} repeated string
*/
exports.repeat = function (str, times){
return Array(times + 1).join(str);
};
/**
* Pads a string
*
* @api public
*/
exports.pad = function (str, len, pad, dir) {
if (len + 1 >= str.length)
switch (dir){
case 'left':
str = Array(len + 1 - str.length).join(pad) + str;
break;
case 'both':
var right = Math.ceil((padlen = len - str.length) / 2);
var left = padlen - right;
str = Array(left + 1).join(pad) + str + Array(right + 1).join(pad);
break;
default:
str = str + Array(len + 1 - str.length).join(pad);
};
return str;
};
/**
* Truncates a string
*
* @api public
*/
exports.truncate = function (str, length, chr){
chr = chr || '…';
return str.length >= length ? str.substr(0, length - chr.length) + chr : str;
};
/**
* Copies and merges options with defaults.
*
* @param {Object} defaults
* @param {Object} supplied options
* @return {Object} new (merged) object
*/
function options(defaults, opts) {
for (var p in opts) {
if (opts[p] && opts[p].constructor && opts[p].constructor === Object) {
defaults[p] = defaults[p] || {};
options(defaults[p], opts[p]);
} else {
defaults[p] = opts[p];
}
}
return defaults;
};
exports.options = options;
//
// For consideration of terminal "color" programs like colors.js,
// which can add ANSI escape color codes to strings,
// we destyle the ANSI color escape codes for padding calculations.
//
// see: http://en.wikipedia.org/wiki/ANSI_escape_code
//
exports.strlen = function(str){
var code = /\u001b\[(?:\d*;){0,5}\d*m/g;
var stripped = ("" + (str != null ? str : '')).replace(code,'');
var split = stripped.split("\n");
return split.reduce(function (memo, s) { return (s.length > memo) ? s.length : memo }, 0);
}
};
BundleModuleCode['doc/table']=function (module,exports){
var doc = Require('doc/doc');
var com = Require('com/compat');
function Table (data,options) {
var totalWidth=(process.stdout.columns)-2;
if (com.obj.isArray(options)) options={head:options};
options=options||{};
var head=options.head,table;
if (com.obj.isMatrix(data)) {
} else if (com.obj.isArray(data) && com.obj.isObject(data[0])) {
options.head=true;
head=Object.keys(data[0]);
data=data.map(function (row) {
return head.map(function (key) { return row[key] })
});
} else return new Error('Table: Inavlid data');
if (!options.colWidths) {
totalWidth-= ((head||data[0]).length-1);
options.colWidths=(head||data[0]).map(function (x,i) {
return Math.max(4,Math.floor(totalWidth/(head||data[0]).length));
});
}
if (head)
table = new doc.Table({
head : head,
colWidths :options.colWidths,
});
else
table = new doc.Table({
colWidths : options.colWidths,
});
data.forEach(function (row,rowi) {
table.push(row);
});
print(table.toString());
}
module.exports = Table;
};
BundleModuleCode['com/readline']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Joyent, Inc. and other Node contributors, Stefan Bosse
** $INITIAL: (C) 2006-2018 bLAB
** $VERSION: 1.2.5
**
** $INFO:
**
// Inspiration for this code comes from Salvatore Sanfilippo's linenoise.
// https://github.com/antirez/linenoise
// Reference:
// * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
// * http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
*/
var kHistorySize = 30;
var util = Require('util');
var Buffer = Require('buffer').Buffer;
var inherits = Require('util').inherits;
var EventEmitter = Require('events').EventEmitter;
var StringDecoder = Require('string_decoder').StringDecoder;
// listenerCount isn't in node 0.10, so here's a basic polyfill
EventEmitter._listenerCount = EventEmitter._listenerCount || function (ee, event) {
var listeners = ee && ee._events && ee._events[event]
if (Array.isArray(listeners)) {
return listeners.length
} else if (typeof listeners === 'function') {
return 1
} else {
return 0
}
}
exports.createInterface = function(input, output, completer, terminal) {
var rl;
if (arguments.length === 1) {
rl = new Interface(input);
} else {
rl = new Interface(input, output, completer, terminal);
}
return rl;
};
function Interface(input, output, completer, terminal) {
if (!(this instanceof Interface)) {
return new Interface(input, output, completer, terminal);
}
this._sawReturn = false;
EventEmitter.call(this);
if (arguments.length === 1) {
// an options object was given
output = input.output;
completer = input.completer;
terminal = input.terminal;
input = input.input;
}
completer = completer || function() { return []; };
if (!util.isFunction(completer)) {
throw new TypeError('Argument \'completer\' must be a function');
}
// backwards compat; check the isTTY prop of the output stream
// when `terminal` was not specified
if (util.isUndefined(terminal) && !util.isNullOrUndefined(output)) {
terminal = !!output.isTTY;
}
var self = this;
this.output = output;
this.input = input;
// Check arity, 2 - for async, 1 for sync
this.completer = completer.length === 2 ? completer : function(v, callback) {
callback(null, completer(v));
};
this.setPrompt('> ');
this.terminal = !!terminal;
function ondata(data) {
self._normalWrite(data);
}
function onend() {
if (util.isString(self._line_buffer) && self._line_buffer.length > 0) {
self.emit('line', self._line_buffer);
}
self.close();
}
function ontermend() {
if (util.isString(self.line) && self.line.length > 0) {
self.emit('line', self.line);
}
self.close();
}
function onkeypress(s, key) {
self._ttyWrite(s, key);
}
function onresize() {
self._refreshLine();
}
if (!this.terminal) {
input.on('data', ondata);
input.on('end', onend);
self.once('close', function() {
input.removeListener('data', ondata);
input.removeListener('end', onend);
});
this._decoder = new StringDecoder('utf8');
} else {
exports.emitKeypressEvents(input);
// input usually refers to stdin
input.on('keypress', onkeypress);
input.on('end', ontermend);
// Current line
this.line = '';
this._setRawMode(true);
this.terminal = true;
// Cursor position on the line.
this.cursor = 0;
this.history = [];
this.historyIndex = -1;
if (!util.isNullOrUndefined(output))
output.on('resize', onresize);
self.once('close', function() {
input.removeListener('keypress', onkeypress);
input.removeListener('end', ontermend);
if (!util.isNullOrUndefined(output)) {
output.removeListener('resize', onresize);
}
});
}
input.resume();
}
inherits(Interface, EventEmitter);
Object.defineProperty(Interface.prototype,'columns',{
get: function() {
var columns = Infinity;
if (this.output && this.output.columns)
columns = this.output.columns;
return columns;
}
});
/* Depricated
Interface.prototype.__defineGetter__('columns', function() {
var columns = Infinity;
if (this.output && this.output.columns)
columns = this.output.columns;
return columns;
});
*/
Interface.prototype.setPrompt = function(prompt) {
this._prompt = prompt;
};
Interface.prototype._setRawMode = function(mode) {
if (util.isFunction(this.input.setRawMode)) {
return this.input.setRawMode(mode);
}
};
Interface.prototype.prompt = function(preserveCursor) {
if (this.paused) this.resume();
if (this.terminal) {
if (!preserveCursor) this.cursor = 0;
this._refreshLine();
} else {
this._writeToOutput(this._prompt);
}
};
Interface.prototype.question = function(query, cb) {
if (util.isFunction(cb)) {
if (this._questionCallback) {
this.prompt();
} else {
this._oldPrompt = this._prompt;
this.setPrompt(query);
this._questionCallback = cb;
this.prompt();
}
}
};
Interface.prototype._onLine = function(line) {
if (this._questionCallback) {
var cb = this._questionCallback;
this._questionCallback = null;
this.setPrompt(this._oldPrompt);
cb(line);
} else {
this.emit('line', line);
}
};
Interface.prototype._writeToOutput = function _writeToOutput(stringToWrite) {
if (!util.isString(stringToWrite))
throw new TypeError('stringToWrite must be a string');
if (!util.isNullOrUndefined(this.output))
this.output.write(stringToWrite);
};
Interface.prototype._addHistory = function() {
if (this.line.length === 0) return '';
if (this.history.length === 0 || this.history[0] !== this.line) {
this.history.unshift(this.line);
// Only store so many
if (this.history.length > kHistorySize) this.history.pop();
}
this.historyIndex = -1;
return this.history[0];
};
Interface.prototype._refreshLine = function() {
// line length
var line = this._prompt + this.line;
var dispPos = this._getDisplayPos(line);
var lineCols = dispPos.cols;
var lineRows = dispPos.rows;
// cursor position
var cursorPos = this._getCursorPos();
// first move to the bottom of the current line, based on cursor pos
var prevRows = this.prevRows || 0;
if (prevRows > 0) {
exports.moveCursor(this.output, 0, -prevRows);
}
// Cursor to left edge.
exports.cursorTo(this.output, 0);
// erase data
exports.clearScreenDown(this.output);
// Write the prompt and the current buffer content.
this._writeToOutput(line);
// Force terminal to allocate a new line
if (lineCols === 0) {
this._writeToOutput(' ');
}
// Move cursor to original position.
exports.cursorTo(this.output, cursorPos.cols);
var diff = lineRows - cursorPos.rows;
if (diff > 0) {
exports.moveCursor(this.output, 0, -diff);
}
this.prevRows = cursorPos.rows;
};
Interface.prototype.close = function() {
if (this.closed) return;
this.pause();
if (this.terminal) {
this._setRawMode(false);
}
this.closed = true;
this.emit('close');
};
Interface.prototype.pause = function() {
if (this.paused) return;
this.input.pause();
this.paused = true;
this.emit('pause');
return this;
};
Interface.prototype.resume = function() {
if (!this.paused) return;
this.input.resume();
this.paused = false;
this.emit('resume');
return this;
};
Interface.prototype.write = function(d, key) {
if (this.paused) this.resume();
this.terminal ? this._ttyWrite(d, key) : this._normalWrite(d);
};
// \r\n, \n, or \r followed by something other than \n
var lineEnding = /\r?\n|\r(?!\n)/;
Interface.prototype._normalWrite = function(b) {
if (util.isUndefined(b)) {
return;
}
var string = this._decoder.write(b);
if (this._sawReturn) {
string = string.replace(/^\n/, '');
this._sawReturn = false;
}
// Run test() on the new string chunk, not on the entire line buffer.
var newPartContainsEnding = lineEnding.test(string);
if (this._line_buffer) {
string = this._line_buffer + string;
this._line_buffer = null;
}
if (newPartContainsEnding) {
this._sawReturn = /\r$/.test(string);
// got one or more newlines; process into "line" events
var lines = string.split(lineEnding);
// either '' or (concievably) the unfinished portion of the next line
string = lines.pop();
this._line_buffer = string;
lines.forEach(function(line) {
this._onLine(line);
}, this);
} else if (string) {
// no newlines this time, save what we have for next time
this._line_buffer = string;
}
};
Interface.prototype._insertString = function(c) {
//BUG: Problem when adding tabs with following content.
// Perhaps the bug is in _refreshLine(). Not sure.
// A hack would be to insert spaces instead of literal '\t'.
if (this.cursor < this.line.length) {
var beg = this.line.slice(0, this.cursor);
var end = this.line.slice(this.cursor, this.line.length);
this.line = beg + c + end;
this.cursor += c.length;
this._refreshLine();
} else {
this.line += c;
this.cursor += c.length;
if (this._getCursorPos().cols === 0) {
this._refreshLine();
} else {
this._writeToOutput(c);
}
// a hack to get the line refreshed if it's needed
this._moveCursor(0);
}
};
Interface.prototype._tabComplete = function() {
var self = this;
self.pause();
self.completer(self.line.slice(0, self.cursor), function(err, rv) {
self.resume();
if (err) {
// XXX Log it somewhere?
return;
}
var completions = rv[0],
completeOn = rv[1]; // the text that was completed
if (completions && completions.length) {
// Apply/show completions.
if (completions.length === 1) {
self._insertString(completions[0].slice(completeOn.length));
} else {
self._writeToOutput('\r\n');
var width = completions.reduce(function(a, b) {
return a.length > b.length ? a : b;
}).length + 2; // 2 space padding
var maxColumns = Math.floor(self.columns / width) || 1;
var group = [], c;
for (var i = 0, compLen = completions.length; i < compLen; i++) {
c = completions[i];
if (c === '') {
handleGroup(self, group, width, maxColumns);
group = [];
} else {
group.push(c);
}
}
handleGroup(self, group, width, maxColumns);
// If there is a common prefix to all matches, then apply that
// portion.
var f = completions.filter(function(e) { if (e) return e; });
var prefix = commonPrefix(f);
if (prefix.length > completeOn.length) {
self._insertString(prefix.slice(completeOn.length));
}
}
self._refreshLine();
}
});
};
// this = Interface instance
function handleGroup(self, group, width, maxColumns) {
if (group.length == 0) {
return;
}
var minRows = Math.ceil(group.length / maxColumns);
for (var row = 0; row < minRows; row++) {
for (var col = 0; col < maxColumns; col++) {
var idx = row * maxColumns + col;
if (idx >= group.length) {
break;
}
var item = group[idx];
self._writeToOutput(item);
if (col < maxColumns - 1) {
for (var s = 0, itemLen = item.length; s < width - itemLen;
s++) {
self._writeToOutput(' ');
}
}
}
self._writeToOutput('\r\n');
}
self._writeToOutput('\r\n');
}
function commonPrefix(strings) {
if (!strings || strings.length == 0) {
return '';
}
var sorted = strings.slice().sort();
var min = sorted[0];
var max = sorted[sorted.length - 1];
for (var i = 0, len = min.length; i < len; i++) {
if (min[i] != max[i]) {
return min.slice(0, i);
}
}
return min;
}
Interface.prototype._wordLeft = function() {
if (this.cursor > 0) {
var leading = this.line.slice(0, this.cursor);
var match = leading.match(/([^\w\s]+|\w+|)\s*$/);
this._moveCursor(-match[0].length);
}
};
Interface.prototype._wordRight = function() {
if (this.cursor < this.line.length) {
var trailing = this.line.slice(this.cursor);
var match = trailing.match(/^(\s+|\W+|\w+)\s*/);
this._moveCursor(match[0].length);
}
};
Interface.prototype._deleteLeft = function() {
if (this.cursor > 0 && this.line.length > 0) {
this.line = this.line.slice(0, this.cursor - 1) +
this.line.slice(this.cursor, this.line.length);
this.cursor--;
this._refreshLine();
}
};
Interface.prototype._deleteRight = function() {
this.line = this.line.slice(0, this.cursor) +
this.line.slice(this.cursor + 1, this.line.length);
this._refreshLine();
};
Interface.prototype._deleteWordLeft = function() {
if (this.cursor > 0) {
var leading = this.line.slice(0, this.cursor);
var match = leading.match(/([^\w\s]+|\w+|)\s*$/);
leading = leading.slice(0, leading.length - match[0].length);
this.line = leading + this.line.slice(this.cursor, this.line.length);
this.cursor = leading.length;
this._refreshLine();
}
};
Interface.prototype._deleteWordRight = function() {
if (this.cursor < this.line.length) {
var trailing = this.line.slice(this.cursor);
var match = trailing.match(/^(\s+|\W+|\w+)\s*/);
this.line = this.line.slice(0, this.cursor) +
trailing.slice(match[0].length);
this._refreshLine();
}
};
Interface.prototype._deleteLineLeft = function() {
this.line = this.line.slice(this.cursor);
this.cursor = 0;
this._refreshLine();
};
Interface.prototype._deleteLineRight = function() {
this.line = this.line.slice(0, this.cursor);
this._refreshLine();
};
Interface.prototype.clearLine = function() {
this._moveCursor(+Infinity);
this._writeToOutput('\r\n');
this.line = '';
this.cursor = 0;
this.prevRows = 0;
};
// Get current input line content
Interface.prototype.getLine = function() {
return this.line;
};
// Insert a message before actual prompt input line
Interface.prototype.insertOutput = function(msg) {
this._moveCursor(+Infinity);
this._writeToOutput('\r');
this._writeToOutput(msg+'\n');
this._writeToOutput(this._prompt+this.line);
};
Interface.prototype._line = function() {
var line = this._addHistory();
this.clearLine();
this._onLine(line);
};
Interface.prototype._historyNext = function() {
if (this.historyIndex > 0) {
this.historyIndex--;
this.line = this.history[this.historyIndex];
this.cursor = this.line.length; // set cursor to end of line.
this._refreshLine();
} else if (this.historyIndex === 0) {
this.historyIndex = -1;
this.cursor = 0;
this.line = '';
this._refreshLine();
}
};
Interface.prototype._historyPrev = function() {
if (this.historyIndex + 1 < this.history.length) {
this.historyIndex++;
this.line = this.history[this.historyIndex];
this.cursor = this.line.length; // set cursor to end of line.
this._refreshLine();
}
};
// Returns the last character's display position of the given string
Interface.prototype._getDisplayPos = function(str) {
var offset = 0;
var col = this.columns;
var row = 0;
var code;
str = stripVTControlCharacters(str);
for (var i = 0, len = str.length; i < len; i++) {
code = codePointAt(str, i);
if (code >= 0x10000) { // surrogates
i++;
}
if (code === 0x0a) { // new line \n
offset = 0;
row += 1;
continue;
}
if (isFullWidthCodePoint(code)) {
if ((offset + 1) % col === 0) {
offset++;
}
offset += 2;
} else {
offset++;
}
}
var cols = offset % col;
var rows = row + (offset - cols) / col;
return {cols: cols, rows: rows};
};
// Returns current cursor's position and line
Interface.prototype._getCursorPos = function() {
var columns = this.columns;
var strBeforeCursor = this._prompt + this.line.substring(0, this.cursor);
var dispPos = this._getDisplayPos(stripVTControlCharacters(strBeforeCursor));
var cols = dispPos.cols;
var rows = dispPos.rows;
// If the cursor is on a full-width character which steps over the line,
// move the cursor to the beginning of the next line.
if (cols + 1 === columns &&
this.cursor < this.line.length &&
isFullWidthCodePoint(codePointAt(this.line, this.cursor))) {
rows++;
cols = 0;
}
return {cols: cols, rows: rows};
};
// This function moves cursor dx places to the right
// (-dx for left) and refreshes the line if it is needed
Interface.prototype._moveCursor = function(dx) {
var oldcursor = this.cursor;
var oldPos = this._getCursorPos();
this.cursor += dx;
// bounds check
if (this.cursor < 0) this.cursor = 0;
else if (this.cursor > this.line.length) this.cursor = this.line.length;
var newPos = this._getCursorPos();
// check if cursors are in the same line
if (oldPos.rows === newPos.rows) {
var diffCursor = this.cursor - oldcursor;
var diffWidth;
if (diffCursor < 0) {
diffWidth = -getStringWidth(
this.line.substring(this.cursor, oldcursor)
);
} else if (diffCursor > 0) {
diffWidth = getStringWidth(
this.line.substring(this.cursor, oldcursor)
);
}
exports.moveCursor(this.output, diffWidth, 0);
this.prevRows = newPos.rows;
} else {
this._refreshLine();
}
};
// handle a write from the tty
Interface.prototype._ttyWrite = function(s, key) {
key = key || {};
// Ignore escape key - Fixes #2876
if (key.name == 'escape') return;
if (key.ctrl && key.shift) {
/* Control and shift pressed */
switch (key.name) {
case 'backspace':
this._deleteLineLeft();
break;
case 'delete':
this._deleteLineRight();
break;
}
} else if (key.ctrl) {
/* Control key pressed */
switch (key.name) {
case 'c':
if (EventEmitter.listenerCount(this, 'SIGINT') > 0) {
this.emit('SIGINT');
} else {
// This readline instance is finished
this.close();
}
break;
case 'h': // delete left
this._deleteLeft();
break;
case 'd': // delete right or EOF
if (this.cursor === 0 && this.line.length === 0) {
// This readline instance is finished
this.close();
} else if (this.cursor < this.line.length) {
this._deleteRight();
}
break;
case 'u': // delete the whole line
this.cursor = 0;
this.line = '';
this._refreshLine();
break;
case 'k': // delete from current to end of line
this._deleteLineRight();
break;
case 'a': // go to the start of the line
this._moveCursor(-Infinity);
break;
case 'e': // go to the end of the line
this._moveCursor(+Infinity);
break;
case 'b': // back one character
this._moveCursor(-1);
break;
case 'f': // forward one character
this._moveCursor(+1);
break;
case 'l': // clear the whole screen
exports.cursorTo(this.output, 0, 0);
exports.clearScreenDown(this.output);
this._refreshLine();
break;
case 'n': // next history item
this._historyNext();
break;
case 'p': // previous history item
this._historyPrev();
break;
case 'z':
if (process.platform == 'win32') break;
if (EventEmitter.listenerCount(this, 'SIGTSTP') > 0) {
this.emit('SIGTSTP');
} else {
process.once('SIGCONT', (function(self) {
return function() {
// Don't raise events if stream has already been abandoned.
if (!self.paused) {
// Stream must be paused and resumed after SIGCONT to catch
// SIGINT, SIGTSTP, and EOF.
self.pause();
self.emit('SIGCONT');
}
// explicitly re-enable "raw mode" and move the cursor to
// the correct position.
// See https://github.com/joyent/node/issues/3295.
self._setRawMode(true);
self._refreshLine();
};
})(this));
this._setRawMode(false);
process.kill(process.pid, 'SIGTSTP');
}
break;
case 'w': // delete backwards to a word boundary
case 'backspace':
this._deleteWordLeft();
break;
case 'delete': // delete forward to a word boundary
this._deleteWordRight();
break;
case 'left':
this._wordLeft();
break;
case 'right':
this._wordRight();
break;
}
} else if (key.meta) {
/* Meta key pressed */
switch (key.name) {
case 'b': // backward word
this._wordLeft();
break;
case 'f': // forward word
this._wordRight();
break;
case 'd': // delete forward word
case 'delete':
this._deleteWordRight();
break;
case 'backspace': // delete backwards to a word boundary
this._deleteWordLeft();
break;
}
} else {
/* No modifier keys used */
// \r bookkeeping is only relevant if a \n comes right after.
if (this._sawReturn && key.name !== 'enter')
this._sawReturn = false;
switch (key.name) {
case 'return': // carriage return, i.e. \r
this._sawReturn = true;
this._line();
break;
case 'enter':
if (this._sawReturn)
this._sawReturn = false;
else
this._line();
break;
case 'backspace':
this._deleteLeft();
break;
case 'delete':
this._deleteRight();
break;
case 'tab': // tab completion
this._tabComplete();
break;
case 'left':
this._moveCursor(-1);
break;
case 'right':
this._moveCursor(+1);
break;
case 'home':
this._moveCursor(-Infinity);
break;
case 'end':
this._moveCursor(+Infinity);
break;
case 'up':
this._historyPrev();
break;
case 'down':
this._historyNext();
break;
default:
if (util.isBuffer(s))
s = s.toString('utf-8');
if (s) {
var lines = s.split(/\r\n|\n|\r/);
for (var i = 0, len = lines.length; i < len; i++) {
if (i > 0) {
this._line();
}
this._insertString(lines[i]);
}
}
}
}
};
exports.Interface = Interface;
/**
* accepts a readable Stream instance and makes it emit "keypress" events
*/
function emitKeypressEvents(stream) {
if (stream._keypressDecoder) return;
var StringDecoder = Require('string_decoder').StringDecoder; // lazy load
stream._keypressDecoder = new StringDecoder('utf8');
function onData(b) {
if (EventEmitter.listenerCount(stream, 'keypress') > 0) {
var r = stream._keypressDecoder.write(b);
if (r) emitKeys(stream, r);
} else {
// Nobody's watching anyway
stream.removeListener('data', onData);
stream.on('newListener', onNewListener);
}
}
function onNewListener(event) {
if (event == 'keypress') {
stream.on('data', onData);
stream.removeListener('newListener', onNewListener);
}
}
if (EventEmitter.listenerCount(stream, 'keypress') > 0) {
stream.on('data', onData);
} else {
stream.on('newListener', onNewListener);
}
}
exports.emitKeypressEvents = emitKeypressEvents;
/*
Some patterns seen in terminal key escape codes, derived from combos seen
at http://www.midnight-commander.org/browser/lib/tty/key.c
ESC letter
ESC [ letter
ESC [ modifier letter
ESC [ 1 ; modifier letter
ESC [ num char
ESC [ num ; modifier char
ESC O letter
ESC O modifier letter
ESC O 1 ; modifier letter
ESC N letter
ESC [ [ num ; modifier char
ESC [ [ 1 ; modifier letter
ESC ESC [ num char
ESC ESC O letter
- char is usually ~ but $ and ^ also happen with rxvt
- modifier is 1 +
(shift * 1) +
(left_alt * 2) +
(ctrl * 4) +
(right_alt * 8)
- two leading ESCs apparently mean the same as one leading ESC
*/
// Regexes used for ansi escape code splitting
var metaKeyCodeReAnywhere = /(?:\x1b)([a-zA-Z0-9])/;
var metaKeyCodeRe = new RegExp('^' + metaKeyCodeReAnywhere.source + '$');
var functionKeyCodeReAnywhere = new RegExp('(?:\x1b+)(O|N|\\[|\\[\\[)(?:' + [
'(\\d+)(?:;(\\d+))?([~^$])',
'(?:M([@ #!a`])(.)(.))', // mouse
'(?:1;)?(\\d+)?([a-zA-Z])'
].join('|') + ')');
var functionKeyCodeRe = new RegExp('^' + functionKeyCodeReAnywhere.source);
var escapeCodeReAnywhere = new RegExp([
functionKeyCodeReAnywhere.source, metaKeyCodeReAnywhere.source, /\x1b./.source
].join('|'));
function emitKeys(stream, s) {
if (util.isBuffer(s)) {
if (s[0] > 127 && util.isUndefined(s[1])) {
s[0] -= 128;
s = '\x1b' + s.toString(stream.encoding || 'utf-8');
} else {
s = s.toString(stream.encoding || 'utf-8');
}
}
var buffer = [];
var match;
while (match = escapeCodeReAnywhere.exec(s)) {
buffer = buffer.concat(s.slice(0, match.index).split(''));
buffer.push(match[0]);
s = s.slice(match.index + match[0].length);
}
buffer = buffer.concat(s.split(''));
buffer.forEach(function(s) {
var ch,
key = {
sequence: s,
name: undefined,
ctrl: false,
meta: false,
shift: false
},
parts;
if (s === '\r') {
// carriage return
key.name = 'return';
} else if (s === '\n') {
// enter, should have been called linefeed
key.name = 'enter';
} else if (s === '\t') {
// tab
key.name = 'tab';
} else if (s === '\b' || s === '\x7f' ||
s === '\x1b\x7f' || s === '\x1b\b') {
// backspace or ctrl+h
key.name = 'backspace';
key.meta = (s.charAt(0) === '\x1b');
} else if (s === '\x1b' || s === '\x1b\x1b') {
// escape key
key.name = 'escape';
key.meta = (s.length === 2);
} else if (s === ' ' || s === '\x1b ') {
key.name = 'space';
key.meta = (s.length === 2);
} else if (s.length === 1 && s <= '\x1a') {
// ctrl+letter
key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1);
key.ctrl = true;
} else if (s.length === 1 && s >= 'a' && s <= 'z') {
// lowercase letter
key.name = s;
} else if (s.length === 1 && s >= 'A' && s <= 'Z') {
// shift+letter
key.name = s.toLowerCase();
key.shift = true;
} else if (parts = metaKeyCodeRe.exec(s)) {
// meta+character key
key.name = parts[1].toLowerCase();
key.meta = true;
key.shift = /^[A-Z]$/.test(parts[1]);
} else if (parts = functionKeyCodeRe.exec(s)) {
// ansi escape sequence
// reassemble the key code leaving out leading \x1b's,
// the modifier key bitflag and any meaningless "1;" sequence
var code = (parts[1] || '') + (parts[2] || '') +
(parts[4] || '') + (parts[9] || ''),
modifier = (parts[3] || parts[8] || 1) - 1;
// Parse the key modifier
key.ctrl = !!(modifier & 4);
key.meta = !!(modifier & 10);
key.shift = !!(modifier & 1);
key.code = code;
// Parse the key itself
switch (code) {
/* xterm/gnome ESC O letter */
case 'OP': key.name = 'f1'; break;
case 'OQ': key.name = 'f2'; break;
case 'OR': key.name = 'f3'; break;
case 'OS': key.name = 'f4'; break;
/* xterm/rxvt ESC [ number ~ */
case '[11~': key.name = 'f1'; break;
case '[12~': key.name = 'f2'; break;
case '[13~': key.name = 'f3'; break;
case '[14~': key.name = 'f4'; break;
/* from Cygwin and used in libuv */
case '[[A': key.name = 'f1'; break;
case '[[B': key.name = 'f2'; break;
case '[[C': key.name = 'f3'; break;
case '[[D': key.name = 'f4'; break;
case '[[E': key.name = 'f5'; break;
/* common */
case '[15~': key.name = 'f5'; break;
case '[17~': key.name = 'f6'; break;
case '[18~': key.name = 'f7'; break;
case '[19~': key.name = 'f8'; break;
case '[20~': key.name = 'f9'; break;
case '[21~': key.name = 'f10'; break;
case '[23~': key.name = 'f11'; break;
case '[24~': key.name = 'f12'; break;
/* xterm ESC [ letter */
case '[A': key.name = 'up'; break;
case '[B': key.name = 'down'; break;
case '[C': key.name = 'right'; break;
case '[D': key.name = 'left'; break;
case '[E': key.name = 'clear'; break;
case '[F': key.name = 'end'; break;
case '[H': key.name = 'home'; break;
/* xterm/gnome ESC O letter */
case 'OA': key.name = 'up'; break;
case 'OB': key.name = 'down'; break;
case 'OC': key.name = 'right'; break;
case 'OD': key.name = 'left'; break;
case 'OE': key.name = 'clear'; break;
case 'OF': key.name = 'end'; break;
case 'OH': key.name = 'home'; break;
/* xterm/rxvt ESC [ number ~ */
case '[1~': key.name = 'home'; break;
case '[2~': key.name = 'insert'; break;
case '[3~': key.name = 'delete'; break;
case '[4~': key.name = 'end'; break;
case '[5~': key.name = 'pageup'; break;
case '[6~': key.name = 'pagedown'; break;
/* putty */
case '[[5~': key.name = 'pageup'; break;
case '[[6~': key.name = 'pagedown'; break;
/* rxvt */
case '[7~': key.name = 'home'; break;
case '[8~': key.name = 'end'; break;
/* rxvt keys with modifiers */
case '[a': key.name = 'up'; key.shift = true; break;
case '[b': key.name = 'down'; key.shift = true; break;
case '[c': key.name = 'right'; key.shift = true; break;
case '[d': key.name = 'left'; key.shift = true; break;
case '[e': key.name = 'clear'; key.shift = true; break;
case '[2$': key.name = 'insert'; key.shift = true; break;
case '[3$': key.name = 'delete'; key.shift = true; break;
case '[5$': key.name = 'pageup'; key.shift = true; break;
case '[6$': key.name = 'pagedown'; key.shift = true; break;
case '[7$': key.name = 'home'; key.shift = true; break;
case '[8$': key.name = 'end'; key.shift = true; break;
case 'Oa': key.name = 'up'; key.ctrl = true; break;
case 'Ob': key.name = 'down'; key.ctrl = true; break;
case 'Oc': key.name = 'right'; key.ctrl = true; break;
case 'Od': key.name = 'left'; key.ctrl = true; break;
case 'Oe': key.name = 'clear'; key.ctrl = true; break;
case '[2^': key.name = 'insert'; key.ctrl = true; break;
case '[3^': key.name = 'delete'; key.ctrl = true; break;
case '[5^': key.name = 'pageup'; key.ctrl = true; break;
case '[6^': key.name = 'pagedown'; key.ctrl = true; break;
case '[7^': key.name = 'home'; key.ctrl = true; break;
case '[8^': key.name = 'end'; key.ctrl = true; break;
/* misc. */
case '[Z': key.name = 'tab'; key.shift = true; break;
default: key.name = 'undefined'; break;
}
}
// Don't emit a key if no name was found
if (util.isUndefined(key.name)) {
key = undefined;
}
if (s.length === 1) {
ch = s;
}
if (key || ch) {
stream.emit('keypress', ch, key);
}
});
}
/**
* moves the cursor to the x and y coordinate on the given stream
*/
function cursorTo(stream, x, y) {
if (util.isNullOrUndefined(stream))
return;
if (!util.isNumber(x) && !util.isNumber(y))
return;
if (!util.isNumber(x))
throw new Error("Can't set cursor row without also setting it's column");
if (!util.isNumber(y)) {
stream.write('\x1b[' + (x + 1) + 'G');
} else {
stream.write('\x1b[' + (y + 1) + ';' + (x + 1) + 'H');
}
}
exports.cursorTo = cursorTo;
/**
* moves the cursor relative to its current location
*/
function moveCursor(stream, dx, dy) {
if (util.isNullOrUndefined(stream))
return;
if (dx < 0) {
stream.write('\x1b[' + (-dx) + 'D');
} else if (dx > 0) {
stream.write('\x1b[' + dx + 'C');
}
if (dy < 0) {
stream.write('\x1b[' + (-dy) + 'A');
} else if (dy > 0) {
stream.write('\x1b[' + dy + 'B');
}
}
exports.moveCursor = moveCursor;
/**
* clears the current line the cursor is on:
* -1 for left of the cursor
* +1 for right of the cursor
* 0 for the entire line
*/
function clearLine(stream, dir) {
if (util.isNullOrUndefined(stream))
return;
if (dir < 0) {
// to the beginning
stream.write('\x1b[1K');
} else if (dir > 0) {
// to the end
stream.write('\x1b[0K');
} else {
// entire line
stream.write('\x1b[2K');
}
}
exports.clearLine = clearLine;
/**
* clears the screen from the current position of the cursor down
*/
function clearScreenDown(stream) {
if (util.isNullOrUndefined(stream))
return;
stream.write('\x1b[0J');
}
exports.clearScreenDown = clearScreenDown;
/**
* Returns the number of columns required to display the given string.
*/
function getStringWidth(str) {
var width = 0;
str = stripVTControlCharacters(str);
for (var i = 0, len = str.length; i < len; i++) {
var code = codePointAt(str, i);
if (code >= 0x10000) { // surrogates
i++;
}
if (isFullWidthCodePoint(code)) {
width += 2;
} else {
width++;
}
}
return width;
}
exports.getStringWidth = getStringWidth;
/**
* Returns true if the character represented by a given
* Unicode code point is full-width. Otherwise returns false.
*/
function isFullWidthCodePoint(code) {
if (isNaN(code)) {
return false;
}
// Code points are derived from:
// http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
if (code >= 0x1100 && (
code <= 0x115f || // Hangul Jamo
0x2329 === code || // LEFT-POINTING ANGLE BRACKET
0x232a === code || // RIGHT-POINTING ANGLE BRACKET
// CJK Radicals Supplement .. Enclosed CJK Letters and Months
(0x2e80 <= code && code <= 0x3247 && code !== 0x303f) ||
// Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
0x3250 <= code && code <= 0x4dbf ||
// CJK Unified Ideographs .. Yi Radicals
0x4e00 <= code && code <= 0xa4c6 ||
// Hangul Jamo Extended-A
0xa960 <= code && code <= 0xa97c ||
// Hangul Syllables
0xac00 <= code && code <= 0xd7a3 ||
// CJK Compatibility Ideographs
0xf900 <= code && code <= 0xfaff ||
// Vertical Forms
0xfe10 <= code && code <= 0xfe19 ||
// CJK Compatibility Forms .. Small Form Variants
0xfe30 <= code && code <= 0xfe6b ||
// Halfwidth and Fullwidth Forms
0xff01 <= code && code <= 0xff60 ||
0xffe0 <= code && code <= 0xffe6 ||
// Kana Supplement
0x1b000 <= code && code <= 0x1b001 ||
// Enclosed Ideographic Supplement
0x1f200 <= code && code <= 0x1f251 ||
// CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
0x20000 <= code && code <= 0x3fffd)) {
return true;
}
return false;
}
exports.isFullWidthCodePoint = isFullWidthCodePoint;
/**
* Returns the Unicode code point for the character at the
* given index in the given string. Similar to String.charCodeAt(),
* but this function handles surrogates (code point >= 0x10000).
*/
function codePointAt(str, index) {
var code = str.charCodeAt(index);
var low;
if (0xd800 <= code && code <= 0xdbff) { // High surrogate
low = str.charCodeAt(index + 1);
if (!isNaN(low)) {
code = 0x10000 + (code - 0xd800) * 0x400 + (low - 0xdc00);
}
}
return code;
}
exports.codePointAt = codePointAt;
/**
* Tries to remove all VT control characters. Use to estimate displayed
* string width. May be buggy due to not running a real state machine
*/
function stripVTControlCharacters(str) {
str = str.replace(new RegExp(functionKeyCodeReAnywhere.source, 'g'), '');
return str.replace(new RegExp(metaKeyCodeReAnywhere.source, 'g'), '');
}
exports.stripVTControlCharacters = stripVTControlCharacters;
};
BundleModuleCode['term/readlineSync']=function (module,exports){
/*
* readlineSync
* https://github.com/anseki/readline-sync
*
* Copyright (c) 2018 anseki
* Licensed under the MIT license.
*/
'use strict';
var
IS_WIN = process.platform === 'win32',
ALGORITHM_CIPHER = 'aes-256-cbc',
ALGORITHM_HASH = 'sha256',
DEFAULT_ERR_MSG = 'The current environment doesn\'t support interactive reading from TTY.',
fs = require('fs'),
TTY = process.binding('tty_wrap').TTY,
childProc = require('child_process'),
pathUtil = require('path'),
defaultOptions = {
/* eslint-disable key-spacing */
prompt: '> ',
hideEchoBack: false,
mask: '*',
limit: [],
limitMessage: 'Input another, please.$<( [)limit(])>',
defaultInput: '',
trueValue: [],
falseValue: [],
caseSensitive: false,
keepWhitespace: false,
encoding: 'utf8',
bufferSize: 1024,
print: void 0,
history: true,
cd: false,
phContent: void 0,
preCheck: void 0
/* eslint-enable key-spacing */
},
fdR = 'none', fdW, ttyR, isRawMode = false,
extHostPath, extHostArgs, tempdir, salt = 0,
lastInput = '', inputHistory = [], rawInput,
_DBG_useExt = false, _DBG_checkOptions = false, _DBG_checkMethod = false;
function getHostArgs(options) {
// Send any text to crazy Windows shell safely.
function encodeArg(arg) {
return arg.replace(/[^\w\u0080-\uFFFF]/g, function(chr) {
return '#' + chr.charCodeAt(0) + ';';
});
}
return extHostArgs.concat((function(conf) {
var args = [];
Object.keys(conf).forEach(function(optionName) {
if (conf[optionName] === 'boolean') {
if (options[optionName]) { args.push('--' + optionName); }
} else if (conf[optionName] === 'string') {
if (options[optionName]) {
args.push('--' + optionName, encodeArg(options[optionName]));
}
}
});
return args;
})({
/* eslint-disable key-spacing */
display: 'string',
displayOnly: 'boolean',
keyIn: 'boolean',
hideEchoBack: 'boolean',
mask: 'string',
limit: 'string',
caseSensitive: 'boolean'
/* eslint-enable key-spacing */
}));
}
// piping via files (for Node.js v0.10-)
function _execFileSync(options, execOptions) {
function getTempfile(name) {
var filepath, suffix = '', fd;
tempdir = tempdir || require('os').tmpdir();
while (true) {
filepath = pathUtil.join(tempdir, name + suffix);
try {
fd = fs.openSync(filepath, 'wx');
} catch (e) {
if (e.code === 'EEXIST') {
suffix++;
continue;
} else {
throw e;
}
}
fs.closeSync(fd);
break;
}
return filepath;
}
var hostArgs, shellPath, shellArgs, res = {}, exitCode, extMessage,
pathStdout = getTempfile('readline-sync.stdout'),
pathStderr = getTempfile('readline-sync.stderr'),
pathExit = getTempfile('readline-sync.exit'),
pathDone = getTempfile('readline-sync.done'),
crypto = require('crypto'), shasum, decipher, password;
shasum = crypto.createHash(ALGORITHM_HASH);
shasum.update('' + process.pid + (salt++) + Math.random());
password = shasum.digest('hex');
decipher = crypto.createDecipher(ALGORITHM_CIPHER, password);
hostArgs = getHostArgs(options);
if (IS_WIN) {
shellPath = process.env.ComSpec || 'cmd.exe';
process.env.Q = '"'; // The quote (") that isn't escaped.
// `()` for ignore space by echo
shellArgs = ['/V:ON', '/S', '/C',
'(%Q%' + shellPath + '%Q% /V:ON /S /C %Q%' + /* ESLint bug? */ // eslint-disable-line no-path-concat
'%Q%' + extHostPath + '%Q%' +
hostArgs.map(function(arg) { return ' %Q%' + arg + '%Q%'; }).join('') +
' & (echo !ERRORLEVEL!)>%Q%' + pathExit + '%Q%%Q%) 2>%Q%' + pathStderr + '%Q%' +
' |%Q%' + process.execPath + '%Q% %Q%' + __dirname + '\\encrypt.js%Q%' +
' %Q%' + ALGORITHM_CIPHER + '%Q% %Q%' + password + '%Q%' +
' >%Q%' + pathStdout + '%Q%' +
' & (echo 1)>%Q%' + pathDone + '%Q%'];
} else {
shellPath = '/bin/sh';
shellArgs = ['-c',
// Use `()`, not `{}` for `-c` (text param)
'("' + extHostPath + '"' + /* ESLint bug? */ // eslint-disable-line no-path-concat
hostArgs.map(function(arg) { return " '" + arg.replace(/'/g, "'\\''") + "'"; }).join('') +
'; echo $?>"' + pathExit + '") 2>"' + pathStderr + '"' +
' |"' + process.execPath + '" "' + __dirname + '/encrypt.js"' +
' "' + ALGORITHM_CIPHER + '" "' + password + '"' +
' >"' + pathStdout + '"' +
'; echo 1 >"' + pathDone + '"'];
}
if (_DBG_checkMethod) { _DBG_checkMethod('_execFileSync', hostArgs); }
try {
childProc.spawn(shellPath, shellArgs, execOptions);
} catch (e) {
res.error = new Error(e.message);
res.error.method = '_execFileSync - spawn';
res.error.program = shellPath;
res.error.args = shellArgs;
}
while (fs.readFileSync(pathDone, {encoding: options.encoding}).trim() !== '1') {} // eslint-disable-line no-empty
if ((exitCode =
fs.readFileSync(pathExit, {encoding: options.encoding}).trim()) === '0') {
res.input =
decipher.update(fs.readFileSync(pathStdout, {encoding: 'binary'}),
'hex', options.encoding) +
decipher.final(options.encoding);
} else {
extMessage = fs.readFileSync(pathStderr, {encoding: options.encoding}).trim();
res.error = new Error(DEFAULT_ERR_MSG + (extMessage ? '\n' + extMessage : ''));
res.error.method = '_execFileSync';
res.error.program = shellPath;
res.error.args = shellArgs;
res.error.extMessage = extMessage;
res.error.exitCode = +exitCode;
}
fs.unlinkSync(pathStdout);
fs.unlinkSync(pathStderr);
fs.unlinkSync(pathExit);
fs.unlinkSync(pathDone);
return res;
}
function readlineExt(options) {
var hostArgs, res = {}, extMessage,
execOptions = {env: process.env, encoding: options.encoding};
if (!extHostPath) {
if (IS_WIN) {
if (process.env.PSModulePath) { // Windows PowerShell
extHostPath = 'powershell.exe';
extHostArgs = ['-ExecutionPolicy', 'Bypass', '-File', __dirname + '\\read.ps1']; // eslint-disable-line no-path-concat
} else { // Windows Script Host
extHostPath = 'cscript.exe';
extHostArgs = ['//nologo', __dirname + '\\read.cs.js']; // eslint-disable-line no-path-concat
}
} else {
extHostPath = '/bin/sh';
extHostArgs = [__dirname + '/read.sh']; // eslint-disable-line no-path-concat
}
}
if (IS_WIN && !process.env.PSModulePath) { // Windows Script Host
// ScriptPW (Win XP and Server2003) needs TTY stream as STDIN.
// In this case, If STDIN isn't TTY, an error is thrown.
execOptions.stdio = [process.stdin];
}
if (childProc.execFileSync) {
hostArgs = getHostArgs(options);
if (_DBG_checkMethod) { _DBG_checkMethod('execFileSync', hostArgs); }
try {
res.input = childProc.execFileSync(extHostPath, hostArgs, execOptions);
} catch (e) { // non-zero exit code
extMessage = e.stderr ? (e.stderr + '').trim() : '';
res.error = new Error(DEFAULT_ERR_MSG + (extMessage ? '\n' + extMessage : ''));
res.error.method = 'execFileSync';
res.error.program = extHostPath;
res.error.args = hostArgs;
res.error.extMessage = extMessage;
res.error.exitCode = e.status;
res.error.code = e.code;
res.error.signal = e.signal;
}
} else {
res = _execFileSync(options, execOptions);
}
if (!res.error) {
res.input = res.input.replace(/^\s*'|'\s*$/g, '');
options.display = '';
}
return res;
}
/*
display: string
displayOnly: boolean
keyIn: boolean
hideEchoBack: boolean
mask: string
limit: string (pattern)
caseSensitive: boolean
keepWhitespace: boolean
encoding, bufferSize, print
*/
function _readlineSync(options) {
var input = '', displaySave = options.display,
silent = !options.display &&
options.keyIn && options.hideEchoBack && !options.mask;
function tryExt() {
var res = readlineExt(options);
if (res.error) { throw res.error; }
return res.input;
}
if (_DBG_checkOptions) { _DBG_checkOptions(options); }
(function() { // open TTY
var fsB, constants, verNum;
function getFsB() {
if (!fsB) {
fsB = process.binding('fs'); // For raw device path
constants = process.binding('constants');
}
return fsB;
}
if (typeof fdR !== 'string') { return; }
fdR = null;
if (IS_WIN) {
// iojs-v2.3.2+ input stream can't read first line. (#18)
// ** Don't get process.stdin before check! **
// Fixed v5.1.0
// Fixed v4.2.4
// It regressed again in v5.6.0, it is fixed in v6.2.0.
verNum = (function(ver) { // getVerNum
var nums = ver.replace(/^\D+/, '').split('.');
var verNum = 0;
if ((nums[0] = +nums[0])) { verNum += nums[0] * 10000; }
if ((nums[1] = +nums[1])) { verNum += nums[1] * 100; }
if ((nums[2] = +nums[2])) { verNum += nums[2]; }
return verNum;
})(process.version);
if (!(verNum >= 20302 && verNum < 40204 || verNum >= 50000 && verNum < 50100 || verNum >= 50600 && verNum < 60200) &&
process.stdin.isTTY) {
process.stdin.pause();
fdR = process.stdin.fd;
ttyR = process.stdin._handle;
} else {
try {
// The stream by fs.openSync('\\\\.\\CON', 'r') can't switch to raw mode.
// 'CONIN$' might fail on XP, 2000, 7 (x86).
fdR = getFsB().open('CONIN$', constants.O_RDWR, parseInt('0666', 8));
ttyR = new TTY(fdR, true);
} catch (e) { /* ignore */ }
}
if (process.stdout.isTTY) {
fdW = process.stdout.fd;
} else {
try {
fdW = fs.openSync('\\\\.\\CON', 'w');
} catch (e) { /* ignore */ }
if (typeof fdW !== 'number') { // Retry
try {
fdW = getFsB().open('CONOUT$', constants.O_RDWR, parseInt('0666', 8));
} catch (e) { /* ignore */ }
}
}
} else {
if (process.stdin.isTTY) {
process.stdin.pause();
try {
fdR = fs.openSync('/dev/tty', 'r'); // device file, not process.stdin
ttyR = process.stdin._handle;
} catch (e) { /* ignore */ }
} else {
// Node.js v0.12 read() fails.
try {
fdR = fs.openSync('/dev/tty', 'r');
ttyR = new TTY(fdR, false);
} catch (e) { /* ignore */ }
}
if (process.stdout.isTTY) {
fdW = process.stdout.fd;
} else {
try {
fdW = fs.openSync('/dev/tty', 'w');
} catch (e) { /* ignore */ }
}
}
})();
(function() { // try read
var atEol, limit,
isCooked = !options.hideEchoBack && !options.keyIn,
buffer, reqSize, readSize, chunk, line;
rawInput = '';
// Node.js v0.10- returns an error if same mode is set.
function setRawMode(mode) {
if (mode === isRawMode) { return true; }
if (ttyR.setRawMode(mode) !== 0) { return false; }
isRawMode = mode;
return true;
}
if (_DBG_useExt || !ttyR ||
typeof fdW !== 'number' && (options.display || !isCooked)) {
input = tryExt();
return;
}
if (options.display) {
fs.writeSync(fdW, options.display);
options.display = '';
}
if (options.displayOnly) { return; }
if (!setRawMode(!isCooked)) {
input = tryExt();
return;
}
reqSize = options.keyIn ? 1 : options.bufferSize;
// Check `allocUnsafe` to make sure of the new API.
buffer = Buffer.allocUnsafe && Buffer.alloc ? Buffer.alloc(reqSize) : new Buffer(reqSize);
if (options.keyIn && options.limit) {
limit = new RegExp('[^' + options.limit + ']',
'g' + (options.caseSensitive ? '' : 'i'));
}
while (true) {
readSize = 0;
try {
readSize = fs.readSync(fdR, buffer, 0, reqSize);
} catch (e) {
if (e.code !== 'EOF') {
setRawMode(false);
input += tryExt();
return;
}
}
if (readSize > 0) {
chunk = buffer.toString(options.encoding, 0, readSize);
rawInput += chunk;
} else {
chunk = '\n';
rawInput += String.fromCharCode(0);
}
if (chunk && typeof (line = (chunk.match(/^(.*?)[\r\n]/) || [])[1]) === 'string') {
chunk = line;
atEol = true;
}
// other ctrl-chars
// eslint-disable-next-line no-control-regex
if (chunk) { chunk = chunk.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ''); }
if (chunk && limit) { chunk = chunk.replace(limit, ''); }
if (chunk) {
if (!isCooked) {
if (!options.hideEchoBack) {
fs.writeSync(fdW, chunk);
} else if (options.mask) {
fs.writeSync(fdW, (new Array(chunk.length + 1)).join(options.mask));
}
}
input += chunk;
}
if (!options.keyIn && atEol ||
options.keyIn && input.length >= reqSize) { break; }
}
if (!isCooked && !silent) { fs.writeSync(fdW, '\n'); }
setRawMode(false);
})();
if (options.print && !silent) {
options.print(displaySave + (options.displayOnly ? '' :
(options.hideEchoBack ? (new Array(input.length + 1)).join(options.mask)
: input) + '\n'), // must at least write '\n'
options.encoding);
}
return options.displayOnly ? '' :
(lastInput = options.keepWhitespace || options.keyIn ? input : input.trim());
}
function flattenArray(array, validator) {
var flatArray = [];
function _flattenArray(array) {
if (array == null) {
return;
} else if (Array.isArray(array)) {
array.forEach(_flattenArray);
} else if (!validator || validator(array)) {
flatArray.push(array);
}
}
_flattenArray(array);
return flatArray;
}
function escapePattern(pattern) {
return pattern.replace(/[\x00-\x7f]/g, // eslint-disable-line no-control-regex
function(s) { return '\\x' + ('00' + s.charCodeAt().toString(16)).substr(-2); });
}
// margeOptions(options1, options2 ... )
// margeOptions(true, options1, options2 ... )
// arg1=true : Start from defaultOptions and pick elements of that.
function margeOptions() {
var optionsList = Array.prototype.slice.call(arguments),
optionNames, fromDefault;
if (optionsList.length && typeof optionsList[0] === 'boolean') {
fromDefault = optionsList.shift();
if (fromDefault) {
optionNames = Object.keys(defaultOptions);
optionsList.unshift(defaultOptions);
}
}
return optionsList.reduce(function(options, optionsPart) {
if (optionsPart == null) { return options; }
// ======== DEPRECATED ========
if (optionsPart.hasOwnProperty('noEchoBack') &&
!optionsPart.hasOwnProperty('hideEchoBack')) {
optionsPart.hideEchoBack = optionsPart.noEchoBack;
delete optionsPart.noEchoBack;
}
if (optionsPart.hasOwnProperty('noTrim') &&
!optionsPart.hasOwnProperty('keepWhitespace')) {
optionsPart.keepWhitespace = optionsPart.noTrim;
delete optionsPart.noTrim;
}
// ======== /DEPRECATED ========
if (!fromDefault) { optionNames = Object.keys(optionsPart); }
optionNames.forEach(function(optionName) {
var value;
if (!optionsPart.hasOwnProperty(optionName)) { return; }
value = optionsPart[optionName];
switch (optionName) {
// _readlineSync <- * * -> defaultOptions
// ================ string
case 'mask': // * *
case 'limitMessage': // *
case 'defaultInput': // *
case 'encoding': // * *
value = value != null ? value + '' : '';
if (value && optionName !== 'limitMessage') { value = value.replace(/[\r\n]/g, ''); }
options[optionName] = value;
break;
// ================ number(int)
case 'bufferSize': // * *
if (!isNaN(value = parseInt(value, 10)) && typeof value === 'number') {
options[optionName] = value; // limited updating (number is needed)
}
break;
// ================ boolean
case 'displayOnly': // *
case 'keyIn': // *
case 'hideEchoBack': // * *
case 'caseSensitive': // * *
case 'keepWhitespace': // * *
case 'history': // *
case 'cd': // *
options[optionName] = !!value;
break;
// ================ array
case 'limit': // * * to string for readlineExt
case 'trueValue': // *
case 'falseValue': // *
options[optionName] = flattenArray(value, function(value) {
var type = typeof value;
return type === 'string' || type === 'number' ||
type === 'function' || value instanceof RegExp;
}).map(function(value) {
return typeof value === 'string' ? value.replace(/[\r\n]/g, '') : value;
});
break;
// ================ function
case 'print': // * *
case 'phContent': // *
case 'preCheck': // *
options[optionName] = typeof value === 'function' ? value : void 0;
break;
// ================ other
case 'prompt': // *
case 'display': // *
options[optionName] = value != null ? value : '';
break;
// no default
}
});
return options;
}, {});
}
function isMatched(res, comps, caseSensitive) {
return comps.some(function(comp) {
var type = typeof comp;
return type === 'string' ?
(caseSensitive ? res === comp : res.toLowerCase() === comp.toLowerCase()) :
type === 'number' ? parseFloat(res) === comp :
type === 'function' ? comp(res) :
comp instanceof RegExp ? comp.test(res) : false;
});
}
function replaceHomePath(path, expand) {
var homePath = pathUtil.normalize(
IS_WIN ? (process.env.HOMEDRIVE || '') + (process.env.HOMEPATH || '') :
process.env.HOME || '').replace(/[\/\\]+$/, '');
path = pathUtil.normalize(path);
return expand ? path.replace(/^~(?=\/|\\|$)/, homePath) :
path.replace(new RegExp('^' + escapePattern(homePath) +
'(?=\\/|\\\\|$)', IS_WIN ? 'i' : ''), '~');
}
function replacePlaceholder(text, generator) {
var PTN_INNER = '(?:\\(([\\s\\S]*?)\\))?(\\w+|.-.)(?:\\(([\\s\\S]*?)\\))?',
rePlaceholder = new RegExp('(\\$)?(\\$<' + PTN_INNER + '>)', 'g'),
rePlaceholderCompat = new RegExp('(\\$)?(\\$\\{' + PTN_INNER + '\\})', 'g');
function getPlaceholderText(s, escape, placeholder, pre, param, post) {
var text;
return escape || typeof (text = generator(param)) !== 'string' ? placeholder :
text ? (pre || '') + text + (post || '') : '';
}
return text.replace(rePlaceholder, getPlaceholderText)
.replace(rePlaceholderCompat, getPlaceholderText);
}
function array2charlist(array, caseSensitive, collectSymbols) {
var values, group = [], groupClass = -1, charCode = 0, symbols = '', suppressed;
function addGroup(groups, group) {
if (group.length > 3) { // ellipsis
groups.push(group[0] + '...' + group[group.length - 1]);
suppressed = true;
} else if (group.length) {
groups = groups.concat(group);
}
return groups;
}
values = array.reduce(
function(chars, value) { return chars.concat((value + '').split('')); }, [])
.reduce(function(groups, curChar) {
var curGroupClass, curCharCode;
if (!caseSensitive) { curChar = curChar.toLowerCase(); }
curGroupClass = /^\d$/.test(curChar) ? 1 :
/^[A-Z]$/.test(curChar) ? 2 : /^[a-z]$/.test(curChar) ? 3 : 0;
if (collectSymbols && curGroupClass === 0) {
symbols += curChar;
} else {
curCharCode = curChar.charCodeAt(0);
if (curGroupClass && curGroupClass === groupClass &&
curCharCode === charCode + 1) {
group.push(curChar);
} else {
groups = addGroup(groups, group);
group = [curChar];
groupClass = curGroupClass;
}
charCode = curCharCode;
}
return groups;
}, []);
values = addGroup(values, group); // last group
if (symbols) { values.push(symbols); suppressed = true; }
return {values: values, suppressed: suppressed};
}
function joinChunks(chunks, suppressed) {
return chunks.join(chunks.length > 2 ? ', ' : suppressed ? ' / ' : '/');
}
function getPhContent(param, options) {
var text, values, resCharlist = {}, arg;
if (options.phContent) {
text = options.phContent(param, options);
}
if (typeof text !== 'string') {
switch (param) {
case 'hideEchoBack':
case 'mask':
case 'defaultInput':
case 'caseSensitive':
case 'keepWhitespace':
case 'encoding':
case 'bufferSize':
case 'history':
case 'cd':
text = !options.hasOwnProperty(param) ? '' :
typeof options[param] === 'boolean' ? (options[param] ? 'on' : 'off') :
options[param] + '';
break;
// case 'prompt':
// case 'query':
// case 'display':
// text = options.hasOwnProperty('displaySrc') ? options.displaySrc + '' : '';
// break;
case 'limit':
case 'trueValue':
case 'falseValue':
values = options[options.hasOwnProperty(param + 'Src') ? param + 'Src' : param];
if (options.keyIn) { // suppress
resCharlist = array2charlist(values, options.caseSensitive);
values = resCharlist.values;
} else {
values = values.filter(function(value) {
var type = typeof value;
return type === 'string' || type === 'number';
});
}
text = joinChunks(values, resCharlist.suppressed);
break;
case 'limitCount':
case 'limitCountNotZero':
text = options[options.hasOwnProperty('limitSrc') ?
'limitSrc' : 'limit'].length;
text = text || param !== 'limitCountNotZero' ? text + '' : '';
break;
case 'lastInput':
text = lastInput;
break;
case 'cwd':
case 'CWD':
case 'cwdHome':
text = process.cwd();
if (param === 'CWD') {
text = pathUtil.basename(text);
} else if (param === 'cwdHome') {
text = replaceHomePath(text);
}
break;
case 'date':
case 'time':
case 'localeDate':
case 'localeTime':
text = (new Date())['to' +
param.replace(/^./, function(str) { return str.toUpperCase(); }) +
'String']();
break;
default: // with arg
if (typeof (arg = (param.match(/^history_m(\d+)$/) || [])[1]) === 'string') {
text = inputHistory[inputHistory.length - arg] || '';
}
}
}
return text;
}
function getPhCharlist(param) {
var matches = /^(.)-(.)$/.exec(param), text = '', from, to, code, step;
if (!matches) { return null; }
from = matches[1].charCodeAt(0);
to = matches[2].charCodeAt(0);
step = from < to ? 1 : -1;
for (code = from; code !== to + step; code += step) { text += String.fromCharCode(code); }
return text;
}
// cmd "arg" " a r g " "" 'a"r"g' "a""rg" "arg
function parseCl(cl) {
var reToken = new RegExp(/(\s*)(?:("|')(.*?)(?:\2|$)|(\S+))/g), matches,
taken = '', args = [], part;
cl = cl.trim();
while ((matches = reToken.exec(cl))) {
part = matches[3] || matches[4] || '';
if (matches[1]) {
args.push(taken);
taken = '';
}
taken += part;
}
if (taken) { args.push(taken); }
return args;
}
function toBool(res, options) {
return (
(options.trueValue.length &&
isMatched(res, options.trueValue, options.caseSensitive)) ? true :
(options.falseValue.length &&
isMatched(res, options.falseValue, options.caseSensitive)) ? false : res);
}
function getValidLine(options) {
var res, forceNext, limitMessage,
matches, histInput, args, resCheck;
function _getPhContent(param) { return getPhContent(param, options); }
function addDisplay(text) { options.display += (/[^\r\n]$/.test(options.display) ? '\n' : '') + text; }
options.limitSrc = options.limit;
options.displaySrc = options.display;
options.limit = ''; // for readlineExt
options.display = replacePlaceholder(options.display + '', _getPhContent);
while (true) {
res = _readlineSync(options);
forceNext = false;
limitMessage = '';
if (options.defaultInput && !res) { res = options.defaultInput; }
if (options.history) {
if ((matches = /^\s*\!(?:\!|-1)(:p)?\s*$/.exec(res))) { // `!!` `!-1` +`:p`
histInput = inputHistory[0] || '';
if (matches[1]) { // only display
forceNext = true;
} else { // replace input
res = histInput;
}
// Show it even if it is empty (NL only).
addDisplay(histInput + '\n');
if (!forceNext) { // Loop may break
options.displayOnly = true;
_readlineSync(options);
options.displayOnly = false;
}
} else if (res && res !== inputHistory[inputHistory.length - 1]) {
inputHistory = [res];
}
}
if (!forceNext && options.cd && res) {
args = parseCl(res);
switch (args[0].toLowerCase()) {
case 'cd':
if (args[1]) {
try {
process.chdir(replaceHomePath(args[1], true));
} catch (e) {
addDisplay(e + '');
}
}
forceNext = true;
break;
case 'pwd':
addDisplay(process.cwd());
forceNext = true;
break;
// no default
}
}
if (!forceNext && options.preCheck) {
resCheck = options.preCheck(res, options);
res = resCheck.res;
if (resCheck.forceNext) { forceNext = true; } // Don't switch to false.
}
if (!forceNext) {
if (!options.limitSrc.length ||
isMatched(res, options.limitSrc, options.caseSensitive)) { break; }
if (options.limitMessage) {
limitMessage = replacePlaceholder(options.limitMessage, _getPhContent);
}
}
addDisplay((limitMessage ? limitMessage + '\n' : '') +
replacePlaceholder(options.displaySrc + '', _getPhContent));
}
return toBool(res, options);
}
// for dev
exports._DBG_set_useExt = function(val) { _DBG_useExt = val; };
exports._DBG_set_checkOptions = function(val) { _DBG_checkOptions = val; };
exports._DBG_set_checkMethod = function(val) { _DBG_checkMethod = val; };
exports._DBG_clearHistory = function() { lastInput = ''; inputHistory = []; };
// ------------------------------------
exports.setDefaultOptions = function(options) {
defaultOptions = margeOptions(true, options);
return margeOptions(true); // copy
};
exports.question = function(query, options) {
/* eslint-disable key-spacing */
return getValidLine(margeOptions(margeOptions(true, options), {
display: query
}));
/* eslint-enable key-spacing */
};
exports.prompt = function(options) {
var readOptions = margeOptions(true, options);
readOptions.display = readOptions.prompt;
return getValidLine(readOptions);
};
exports.keyIn = function(query, options) {
/* eslint-disable key-spacing */
var readOptions = margeOptions(margeOptions(true, options), {
display: query,
keyIn: true,
keepWhitespace: true
});
/* eslint-enable key-spacing */
// char list
readOptions.limitSrc = readOptions.limit.filter(function(value) {
var type = typeof value;
return type === 'string' || type === 'number';
})
.map(function(text) { return replacePlaceholder(text + '', getPhCharlist); });
// pattern
readOptions.limit = escapePattern(readOptions.limitSrc.join(''));
['trueValue', 'falseValue'].forEach(function(optionName) {
readOptions[optionName] = readOptions[optionName].reduce(function(comps, comp) {
var type = typeof comp;
if (type === 'string' || type === 'number') {
comps = comps.concat((comp + '').split(''));
} else { comps.push(comp); }
return comps;
}, []);
});
readOptions.display = replacePlaceholder(readOptions.display + '',
function(param) { return getPhContent(param, readOptions); });
return toBool(_readlineSync(readOptions), readOptions);
};
// ------------------------------------
exports.questionEMail = function(query, options) {
if (query == null) { query = 'Input e-mail address: '; }
/* eslint-disable key-spacing */
return exports.question(query, margeOptions({
// -------- default
hideEchoBack: false,
// http://www.w3.org/TR/html5/forms.html#valid-e-mail-address
limit: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
limitMessage: 'Input valid e-mail address, please.',
trueValue: null,
falseValue: null
}, options, {
// -------- forced
keepWhitespace: false,
cd: false
}));
/* eslint-enable key-spacing */
};
exports.questionNewPassword = function(query, options) {
/* eslint-disable key-spacing */
var resCharlist, min, max,
readOptions = margeOptions({
// -------- default
hideEchoBack: true,
mask: '*',
limitMessage: 'It can include: $<charlist>\n' +
'And the length must be: $<length>',
trueValue: null,
falseValue: null,
caseSensitive: true
}, options, {
// -------- forced
history: false,
cd: false,
// limit (by charlist etc.),
phContent: function(param) {
return param === 'charlist' ? resCharlist.text :
param === 'length' ? min + '...' + max : null;
}
}),
// added: charlist, min, max, confirmMessage, unmatchMessage
charlist, confirmMessage, unmatchMessage,
limit, limitMessage, res1, res2;
/* eslint-enable key-spacing */
options = options || {};
charlist = replacePlaceholder(
options.charlist ? options.charlist + '' : '$<!-~>', getPhCharlist);
if (isNaN(min = parseInt(options.min, 10)) || typeof min !== 'number') { min = 12; }
if (isNaN(max = parseInt(options.max, 10)) || typeof max !== 'number') { max = 24; }
limit = new RegExp('^[' + escapePattern(charlist) +
']{' + min + ',' + max + '}$');
resCharlist = array2charlist([charlist], readOptions.caseSensitive, true);
resCharlist.text = joinChunks(resCharlist.values, resCharlist.suppressed);
confirmMessage = options.confirmMessage != null ? options.confirmMessage :
'Reinput a same one to confirm it: ';
unmatchMessage = options.unmatchMessage != null ? options.unmatchMessage :
'It differs from first one.' +
' Hit only the Enter key if you want to retry from first one.';
if (query == null) { query = 'Input new password: '; }
limitMessage = readOptions.limitMessage;
while (!res2) {
readOptions.limit = limit;
readOptions.limitMessage = limitMessage;
res1 = exports.question(query, readOptions);
readOptions.limit = [res1, ''];
readOptions.limitMessage = unmatchMessage;
res2 = exports.question(confirmMessage, readOptions);
}
return res1;
};
function _questionNum(query, options, parser) {
var validValue;
function getValidValue(value) {
validValue = parser(value);
return !isNaN(validValue) && typeof validValue === 'number';
}
/* eslint-disable key-spacing */
exports.question(query, margeOptions({
// -------- default
limitMessage: 'Input valid number, please.'
}, options, {
// -------- forced
limit: getValidValue,
cd: false
// trueValue, falseValue, caseSensitive, keepWhitespace don't work.
}));
/* eslint-enable key-spacing */
return validValue;
}
exports.questionInt = function(query, options) {
return _questionNum(query, options, function(value) { return parseInt(value, 10); });
};
exports.questionFloat = function(query, options) {
return _questionNum(query, options, parseFloat);
};
exports.questionPath = function(query, options) {
/* eslint-disable key-spacing */
var validPath, error = '',
readOptions = margeOptions({
// -------- default
hideEchoBack: false,
limitMessage: '$<error(\n)>Input valid path, please.' +
'$<( Min:)min>$<( Max:)max>',
history: true,
cd: true
}, options, {
// -------- forced
keepWhitespace: false,
limit: function(value) {
var exists, stat, res;
value = replaceHomePath(value, true);
error = ''; // for validate
// mkdir -p
function mkdirParents(dirPath) {
dirPath.split(/\/|\\/).reduce(function(parents, dir) {
var path = pathUtil.resolve((parents += dir + pathUtil.sep));
if (!fs.existsSync(path)) {
fs.mkdirSync(path);
} else if (!fs.statSync(path).isDirectory()) {
throw new Error('Non directory already exists: ' + path);
}
return parents;
}, '');
}
try {
exists = fs.existsSync(value);
validPath = exists ? fs.realpathSync(value) : pathUtil.resolve(value);
// options.exists default: true, not-bool: no-check
if (!options.hasOwnProperty('exists') && !exists ||
typeof options.exists === 'boolean' && options.exists !== exists) {
error = (exists ? 'Already exists' : 'No such file or directory') +
': ' + validPath;
return false;
}
if (!exists && options.create) {
if (options.isDirectory) {
mkdirParents(validPath);
} else {
mkdirParents(pathUtil.dirname(validPath));
fs.closeSync(fs.openSync(validPath, 'w')); // touch
}
validPath = fs.realpathSync(validPath);
}
if (exists && (options.min || options.max ||
options.isFile || options.isDirectory)) {
stat = fs.statSync(validPath);
// type check first (directory has zero size)
if (options.isFile && !stat.isFile()) {
error = 'Not file: ' + validPath;
return false;
} else if (options.isDirectory && !stat.isDirectory()) {
error = 'Not directory: ' + validPath;
return false;
} else if (options.min && stat.size < +options.min ||
options.max && stat.size > +options.max) {
error = 'Size ' + stat.size + ' is out of range: ' + validPath;
return false;
}
}
if (typeof options.validate === 'function' &&
(res = options.validate(validPath)) !== true) {
if (typeof res === 'string') { error = res; }
return false;
}
} catch (e) {
error = e + '';
return false;
}
return true;
},
// trueValue, falseValue, caseSensitive don't work.
phContent: function(param) {
return param === 'error' ? error :
param !== 'min' && param !== 'max' ? null :
options.hasOwnProperty(param) ? options[param] + '' : '';
}
});
// added: exists, create, min, max, isFile, isDirectory, validate
/* eslint-enable key-spacing */
options = options || {};
if (query == null) { query = 'Input path (you can "cd" and "pwd"): '; }
exports.question(query, readOptions);
return validPath;
};
// props: preCheck, args, hRes, limit
function getClHandler(commandHandler, options) {
var clHandler = {}, hIndex = {};
if (typeof commandHandler === 'object') {
Object.keys(commandHandler).forEach(function(cmd) {
if (typeof commandHandler[cmd] === 'function') {
hIndex[options.caseSensitive ? cmd : cmd.toLowerCase()] = commandHandler[cmd];
}
});
clHandler.preCheck = function(res) {
var cmdKey;
clHandler.args = parseCl(res);
cmdKey = clHandler.args[0] || '';
if (!options.caseSensitive) { cmdKey = cmdKey.toLowerCase(); }
clHandler.hRes =
cmdKey !== '_' && hIndex.hasOwnProperty(cmdKey) ?
hIndex[cmdKey].apply(res, clHandler.args.slice(1)) :
hIndex.hasOwnProperty('_') ? hIndex._.apply(res, clHandler.args) : null;
return {res: res, forceNext: false};
};
if (!hIndex.hasOwnProperty('_')) {
clHandler.limit = function() { // It's called after preCheck.
var cmdKey = clHandler.args[0] || '';
if (!options.caseSensitive) { cmdKey = cmdKey.toLowerCase(); }
return hIndex.hasOwnProperty(cmdKey);
};
}
} else {
clHandler.preCheck = function(res) {
clHandler.args = parseCl(res);
clHandler.hRes = typeof commandHandler === 'function' ?
commandHandler.apply(res, clHandler.args) : true; // true for break loop
return {res: res, forceNext: false};
};
}
return clHandler;
}
exports.promptCL = function(commandHandler, options) {
/* eslint-disable key-spacing */
var readOptions = margeOptions({
// -------- default
hideEchoBack: false,
limitMessage: 'Requested command is not available.',
caseSensitive: false,
history: true
}, options),
// -------- forced
// trueValue, falseValue, keepWhitespace don't work.
// preCheck, limit (by clHandler)
clHandler = getClHandler(commandHandler, readOptions);
/* eslint-enable key-spacing */
readOptions.limit = clHandler.limit;
readOptions.preCheck = clHandler.preCheck;
exports.prompt(readOptions);
return clHandler.args;
};
exports.promptLoop = function(inputHandler, options) {
/* eslint-disable key-spacing */
var readOptions = margeOptions({
// -------- default
hideEchoBack: false,
trueValue: null,
falseValue: null,
caseSensitive: false,
history: true
}, options);
/* eslint-enable key-spacing */
while (true) { if (inputHandler(exports.prompt(readOptions))) { break; } }
return;
};
exports.promptCLLoop = function(commandHandler, options) {
/* eslint-disable key-spacing */
var readOptions = margeOptions({
// -------- default
hideEchoBack: false,
limitMessage: 'Requested command is not available.',
caseSensitive: false,
history: true
}, options),
// -------- forced
// trueValue, falseValue, keepWhitespace don't work.
// preCheck, limit (by clHandler)
clHandler = getClHandler(commandHandler, readOptions);
/* eslint-enable key-spacing */
readOptions.limit = clHandler.limit;
readOptions.preCheck = clHandler.preCheck;
while (true) {
exports.prompt(readOptions);
if (clHandler.hRes) { break; }
}
return;
};
exports.promptSimShell = function(options) {
/* eslint-disable key-spacing */
return exports.prompt(margeOptions({
// -------- default
hideEchoBack: false,
history: true
}, options, {
// -------- forced
prompt: (function() {
return IS_WIN ?
'$<cwd>>' :
// 'user@host:cwd$ '
(process.env.USER || '') +
(process.env.HOSTNAME ?
'@' + process.env.HOSTNAME.replace(/\..*$/, '') : '') +
':$<cwdHome>$ ';
})()
}));
/* eslint-enable key-spacing */
};
function _keyInYN(query, options, limit) {
var res;
if (query == null) { query = 'Are you sure? '; }
if ((!options || options.guide !== false) && (query += '')) {
query = query.replace(/\s*:?\s*$/, '') + ' [y/n]: ';
}
/* eslint-disable key-spacing */
res = exports.keyIn(query, margeOptions(options, {
// -------- forced
hideEchoBack: false,
limit: limit,
trueValue: 'y',
falseValue: 'n',
caseSensitive: false
// mask doesn't work.
}));
// added: guide
/* eslint-enable key-spacing */
return typeof res === 'boolean' ? res : '';
}
exports.keyInYN = function(query, options) { return _keyInYN(query, options); };
exports.keyInYNStrict = function(query, options) { return _keyInYN(query, options, 'yn'); };
exports.keyInPause = function(query, options) {
if (query == null) { query = 'Continue...'; }
if ((!options || options.guide !== false) && (query += '')) {
query = query.replace(/\s+$/, '') + ' (Hit any key)';
}
/* eslint-disable key-spacing */
exports.keyIn(query, margeOptions({
// -------- default
limit: null
}, options, {
// -------- forced
hideEchoBack: true,
mask: ''
}));
// added: guide
/* eslint-enable key-spacing */
return;
};
exports.keyInSelect = function(items, query, options) {
/* eslint-disable key-spacing */
var readOptions = margeOptions({
// -------- default
hideEchoBack: false
}, options, {
// -------- forced
trueValue: null,
falseValue: null,
caseSensitive: false,
// limit (by items),
phContent: function(param) {
return param === 'itemsCount' ? items.length + '' :
param === 'firstItem' ? (items[0] + '').trim() :
param === 'lastItem' ? (items[items.length - 1] + '').trim() : null;
}
}),
// added: guide, cancel
keylist = '', key2i = {}, charCode = 49 /* '1' */, display = '\n';
/* eslint-enable key-spacing */
if (!Array.isArray(items) || !items.length || items.length > 35) {
throw '`items` must be Array (max length: 35).';
}
items.forEach(function(item, i) {
var key = String.fromCharCode(charCode);
keylist += key;
key2i[key] = i;
display += '[' + key + '] ' + (item + '').trim() + '\n';
charCode = charCode === 57 /* '9' */ ? 97 /* 'a' */ : charCode + 1;
});
if (!options || options.cancel !== false) {
keylist += '0';
key2i['0'] = -1;
display += '[0] ' +
(options && options.cancel != null && typeof options.cancel !== 'boolean' ?
(options.cancel + '').trim() : 'CANCEL') + '\n';
}
readOptions.limit = keylist;
display += '\n';
if (query == null) { query = 'Choose one from list: '; }
if ((query += '')) {
if (!options || options.guide !== false) {
query = query.replace(/\s*:?\s*$/, '') + ' [$<limit>]: ';
}
display += query;
}
return key2i[exports.keyIn(display, readOptions).toLowerCase()];
};
exports.getRawInput = function() { return rawInput; };
// ======== DEPRECATED ========
function _setOption(optionName, args) {
var options;
if (args.length) { options = {}; options[optionName] = args[0]; }
return exports.setDefaultOptions(options)[optionName];
}
exports.setPrint = function() { return _setOption('print', arguments); };
exports.setPrompt = function() { return _setOption('prompt', arguments); };
exports.setEncoding = function() { return _setOption('encoding', arguments); };
exports.setMask = function() { return _setOption('mask', arguments); };
exports.setBufferSize = function() { return _setOption('bufferSize', arguments); };
};
BundleModuleCode['http/https']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** BSSLAB, Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 18-05-15 by sbosse.
** $RCS: $Id:$
** $VERSION: 1.3.1
**
** $INFO:
**
** HTTP(S) File Server Module.
**
*
** $ENDOFINFO
*/
"use strict";
var log = 0;
var Io = Require('com/io');
var util = Require('util');
var http = Require('http');
var https; try { https = require('https'); } catch (e) {}
var fs = Require('fs');
var Comp = Require('com/compat');
var Perv = Comp.pervasives;
var String = Comp.string;
var Array = Comp.array;
var Filename = Comp.filename;
var trace = Io.tracing;
var div = Perv.div;
var isNode = Comp.isNodeJS();
/*********************************************
** HTTP File SERVER
*********************************************/
/** Auxiliary File Server
*
* typeof @options = { sip,ipport,dir,verbose?,index?,log? }
* typeof File = constructor
*/
var HTTPSrv = function(options) {
if (!(this instanceof HTTPSrv)) return new HTTPSrv(options);
this.srv_ip = options.ip; // URL
this.srv_ipport = options.ipport; // URL:port
this.dir = options.dir; // Local file directory to be served
this.proto = options.proto||'http';
this.https=undefined;
this.verbose=options.verbose||0;
this.index=options.index||'index.html';
this.log=options.log||Io.log;
this.options=options;
};
HTTPSrv.prototype.init=function () {
var self=this,
stat='';
this.dir=Filename.path_absolute(this.dir);
function handler(request, response) {
//Io.inspect(request);
response.origin=request.headers.origin;
var path=String.prefix(request.url,'?');
String.match(request.method,[
['GET',function() {
// TODO
if (self.verbose>2) self.log('[HTTP] Get: '+path);
var data='';
try {
path=self.dir+'/'+Filename.path_normalize(path=='/'?self.index:path);
data=Io.read_file_bin(path);
stat='OK';
} catch (e) {
data='File server: failed to read file '+path+' , '+util.inspect(e);
stat=data;
}
if (data == undefined) {
stat='Failed: no data read.';
if (self.verbose>1) self.log('[HTTP] : Failed to get data for file '+path);
data='Error: Not found: '+path;
}
if (self.verbose>2) self.log('[HTTP] Get: '+request.url+' -> '+stat+' ['+(data?data.length:0)+']');
if (data!=undefined) {
//response.writeHead(200);
if (response.origin!=undefined)
response.writeHead(200,{'Access-Control-Allow-Origin': response.origin,
'Access-Control-Allow-Credentials': 'true',
});
else
// response.writeHead(200,{'Content-Type': 'text/html'});
response.writeHead(200,{'Access-Control-Allow-Origin': '*'});
response.write(data);
response.end();
}
}]
])
};
if (this.proto=='http') this.https = http.createServer(handler);
else if (this.proto=='https' && https) {
// Dummy certs, can be overriden by options
var _options={
key:"-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCj1c0IRFOg2FZt\ncDtVgdQetk0RtOmU5ukMs09xw+irPHZeHmtu0gWy11yCHfqHwaqsrYdmnC1EAJsr\nlyBgdoiOn2MJNxW52/x7/I1ZVUke6p4OPyhNGaQHcCPmp/dBzMH9yY6K/HHPDqR/\ncDR1ait3ttpsvMxFT0baHZsxm/bajKUETSkGOW5gugq32egyjAKfHYSbbSY2zm8R\n3g1kluYKGvhjt/SvTPjcGYiTMwkyKBTuvpfZqxRArkaMlQdKKKBT+X3cY47ZD4I3\n0Hy8kirTeLvPf91THeI8pcTVU8a6qPryttOB9cRruzYJF4Z0sdnAzTPmVugPjRqn\n6BFPb0v1AgMBAAECggEASTMAHV5xwt6FlvXa/LQ58tLekjezWRzmKQ+AQkMWlFM6\nS4jp1SSu+R2xrkz4n2kO+YG6ikTjEIv4yDwIcjDjiF18ISTkZxr7ruXCvZQWTGLk\n5VagifoXyF75G1gWZ+a1Ec/ZCQ4LR0iyhGG8fm1GKIGhC4468giejltF+J9HZpNT\nJTcOZ/d5+WtwFa67o1vEqp8tIZ6bA6as9Jp4brmWifXSNZpGh3oIa6eQcVAl9b32\nxnh9F1oBwAz5D5TbHZ7RfiRsoUKeEprsJ8XEfVwO5R8xd7IMc5eXqDcZIZHJEWeV\nRqY0GOGRCdBWZydrHnyIpkCcJ9TytN4nx3OD0BsCYQKBgQDRrXDM88lVWW3htatu\nZiEZQIVkJ3Lj/S9/YByeU22UBUr7UZfWAQWEF7nhDnoa3NeQULMekgeH8O4Yd7Qd\nsGHm9DwiqPiyw2MRUU2eM074GiDpgy1K+oP669YHSMe+Vq5TnW1deNDuPYm4R85V\nGqG0rpG5yN6FojMmQsn+0qTxDQKBgQDIB7E8AMLFV7g1e8mor4uMa68GyScl1bFK\ngQ3Yoq+yLUV0zziFIcR9IwGxopC81QN4qXynb1JnlQyTASEPxJT558wFIUqRwnND\nxbwfwcNL5KVN7F1yTn55mmKHuxYGURs3Au8ErwQ+cdDu3bFsQxk8eBEob3OEzAd1\nxEW1yAh8iQKBgGaU4y3yS1rtULvvhHqTjrfrABe60RPHp7g6jmXLTT3wxPllttIl\nV8yDSxZXXdfMmc3qHWfka7jPX70quz0XMR6r+MvAPURAITS0wTOXyJfLOLTlz3/y\nRiW5wdF4gviVMd6Ik5v6YsVb6Af3YXPzfo+GJJdvNabNbxbV8DsyVS31AoGAGaTy\n0fB/B/HRCfpOxjOLPntnuwT64dzdl+GntshUohEvwGP4qQjFOg3M38sppyvgAA4q\njwS0mdb//7C7XlwjhU50V4wHFVzKjjvBfIjI0ugDUVQmPstVZ52lWCViE3k+dfUI\nU59keeT5lkYRwwFvMNNrz7VKKBJIOo7pKP72J5ECgYAygg6zNNUzrcUuuZHSpyEM\nx5uy5nmoD81RwlGS0N8m5SwN8jW+c7C7ROUdGakXV69zubMXzAsz4xMJTTSQBep2\nsNTNjlV71UikZhhx/spLZqaLb0ZIgCxj4dfNZS3XRh7Wi1bYuf2III+SUf4zitG0\nuGKHIqJgcSumSzjYGiMSAA==\n-----END PRIVATE KEY-----\n",
cert:"-----BEGIN CERTIFICATE-----\nMIIDITCCAgmgAwIBAgIJAKMxU7sE4FnyMA0GCSqGSIb3DQEBCwUAMCcxCzAJBgNV\nBAYTAlVTMRgwFgYDVQQDDA9FeGFtcGxlLVJvb3QtQ0EwHhcNMjIwNjA1MTEzMDMy\nWhcNMjUwMzI1MTEzMDMyWjAnMQswCQYDVQQGEwJVUzEYMBYGA1UEAwwPRXhhbXBs\nZS1Sb290LUNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo9XNCERT\noNhWbXA7VYHUHrZNEbTplObpDLNPccPoqzx2Xh5rbtIFstdcgh36h8GqrK2HZpwt\nRACbK5cgYHaIjp9jCTcVudv8e/yNWVVJHuqeDj8oTRmkB3Aj5qf3QczB/cmOivxx\nzw6kf3A0dWord7babLzMRU9G2h2bMZv22oylBE0pBjluYLoKt9noMowCnx2Em20m\nNs5vEd4NZJbmChr4Y7f0r0z43BmIkzMJMigU7r6X2asUQK5GjJUHSiigU/l93GOO\n2Q+CN9B8vJIq03i7z3/dUx3iPKXE1VPGuqj68rbTgfXEa7s2CReGdLHZwM0z5lbo\nD40ap+gRT29L9QIDAQABo1AwTjAdBgNVHQ4EFgQU763HyX73limLTXAwJ4SwpVGv\nD/AwHwYDVR0jBBgwFoAU763HyX73limLTXAwJ4SwpVGvD/AwDAYDVR0TBAUwAwEB\n/zANBgkqhkiG9w0BAQsFAAOCAQEAaO662eNFN2wWtMsUrITX8pwUAJRKxkFvxEQI\nt0HgtfxxvZeTgYjLeTv5U0Jmv8K+6QnNnFIfoc9CD0fFaETw9Z6a+mzliMnHwzZ2\ndI+3eahIcRZ86VuvwisJsDzpW1931Jz+/arIEZprTTSfCPkJs9U790W4wfA6/7Cc\nyZ57EWiug8sP/0NcgofKNNCiixlnlNhXJIOh7/7gXw+zJVdyoKUHMJMoii1UElzN\nVTm6YKSTiuOc+rOIbC4Aw5gQqRDtUqbf/Vcr2IEdOqlL7r4vW9urH+/p3sLVF20C\n8ssjea8dmHcrb5Omu0tUMbhzMM1/eHZS3iwcauu2VWzBDOOjeQ==\n-----END CERTIFICATE-----\n"
}
if (fs.existsSync(this.options.key)) { console.log('Loading '+_this.options.key); _options.key=fs.readFileSync(this.options.key,'utf8') };
if (fs.existsSync(this.options.cert)) { console.log('Loading '+this.options.cert); _options.cert=fs.readFileSync(this.options,'utf8') };
this.https = https.createServer(_options,handler);
} else throw "ENOTSUPPORTED";
this.https.on("connection", function (socket) {
socket.setNoDelay(true);
});
this.log('[HTTP] servicing directory: ' + this.dir);
};
HTTPSrv.prototype.start=function () {
var self=this;
if (self.verbose) Io.out('[HTTP] Starting ..');
this.https.listen(this.srv_ipport, function () {
self.log('[HTTP] listen: listening on *:' + self.srv_ipport);
});
};
HTTPSrv.prototype.stop=function () {
if (this.https) this.https.close();
}
module.exports = {
/** Auxiliary File/HTML Server
*
*/
// typeof @options = {ip,ipport,dir,verbose?,index?}
HTTPSrv: HTTPSrv
}
};
BundleModuleCode['geoip/geoip']=function (module,exports){
/**
** ==============================
** OOOO O O OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: 20-10-16 by sbosse.
** $VERSION: 1.2.1
**
** $INFO:
**
** GEO IP Database Services and http/https Server
** Database source: GeoLiteCit
** Compatibility: ip-api.com/json
** Can be used as a proxy to ip-api.com to overcome ad-blockers!
**
** Look-up returns:
** {
** country: string, // country
** countryCode: string, // countryCode
** region: string, // region
** city: string, // city
** zip: string, // postal code
** lat: string, // latitude
** lon: string // longitude
** };
**
** $ENDOFINFO
*/
var fs = require("fs");
var sat = Require('dos/ext/satelize');
var out = function (msg) { console.log('[GEOIP] '+msg) };
var geoip = module.exports = {
dir : process.cwd()||__dirname,
ipblocks : [],
locations : [],
midpoints : [],
numblocks : 0,
ready : 0,
verbose : 0,
config : function (a,v) { geoip[a]=v },
// Cold start, load and compile CSV DB
init : function (cb) {
out ('Loading '+ geoip.dir + "/GeoLiteCity-Blocks.csv ..");
var block = fs.createReadStream(geoip.dir + "/GeoLiteCity-Blocks.csv");
out ('Loading '+ geoip.dir + "/GeoLiteCity-Location.csv ..");
var location = fs.createReadStream(geoip.dir + "/GeoLiteCity-Location.csv");
var buffer1 = "",buffer2 = "";
block.addListener("data", function(data) {
buffer1 += data.toString().replace(/"/g, "");
});
block.addListener("end", function() {
var entries = buffer1.split("\n");
out ('Compiling GeoLiteCity-Blocks ..');
for(var i=0; i<entries.length; i++) {
if (i<2) continue;
var entry = entries[i].split(",");
if (parseInt(entry[0])) geoip.ipblocks.push({
a: parseInt(entry[0]), // ip start
b: parseInt(entry[1]), // ip end
i: parseInt(entry[2]) // location id
});
}
geoip.ipblocks.sort(function(a, b) {
return a.a - b.a;
});
geoip.numblocks = geoip.ipblocks.length;
geoip.midpoints=[];
var n = Math.floor(geoip.numblocks / 2);
while(n >= 1) {
n = Math.floor(n / 1.5);
geoip.midpoints.push(n);
}
geoip.ready++;
if (cb && geoip.ready==2) cb();
});
location.addListener("data", function(data) {
buffer2 += data.toString().replace(/"/g, "");
});
location.addListener("end", function() {
var entries = buffer2.split("\n");
var locid=0;
out ('Compiling GeoLiteCity-Location ..');
for(var i=0; i<entries.length; i++) {
if (i<2) continue;
var entry = entries[i].split(",");
locid=parseInt(entry[0]);
geoip.locations[locid] = {
cn: entry[1], // country
re: entry[2], // region
ci: entry[3], // city
pc: entry[4], // postal code
la: entry[5], // latitude
lo: entry[6] // longitude
};
}
geoip.ready++;
if (cb && geoip.ready==2) cb();
});
},
// Load and parse JSON DB
load : function (cb) {
// Read DB
out ('Loading '+geoip.dir + "/GeoLiteCity-Blocks.json ..");
var block = fs.createReadStream(geoip.dir + "/GeoLiteCity-Blocks.json");
out ('Loading '+geoip.dir + "/GeoLiteCity-Location.json ..");
var location = fs.createReadStream(geoip.dir + "/GeoLiteCity-Location.json");
var buffer1 = "",buffer2 = "";
block.on('error', function (e) { out(e); cb(e); });
location.on('error', function (e) { out(e); cb(e); });
block.addListener("data", function(data) {
buffer1 += data.toString();
});
block.addListener("end", function() {
out ('Parsing GeoLiteCity-Blocks ..');
geoip.ipblocks = JSON.parse(buffer1);
out ('Parsing GeoLiteCity-Blocks done.');
geoip.ipblocks.sort(function(a, b) {
return a.a - b.a;
});
geoip.numblocks = geoip.ipblocks.length;
var n = Math.floor(geoip.numblocks / 2);
geoip.midpoints=[];
while(n >= 1) {
n = Math.floor(n / 1.5);
geoip.midpoints.push(n);
}
geoip.ready++;
if (cb && geoip.ready==2) cb();
});
location.addListener("data", function(data) {
buffer2 += data.toString();
});
location.addListener("end", function() {
out ('Parsing GeoLiteCity-Location ..');
geoip.locations = JSON.parse(buffer2);
out ('Parsing GeoLiteCity-Location done.');
geoip.ready++;
if (cb && geoip.ready==2) cb();
});
},
// Search a matching GEO entry
lookup: function(ip) {
if(geoip.ready<2) {
return { error: "GeoIP not ready" };
}
var ipl = iplong(ip);
if(ipl == 0) {
return { error: "Invalid ip address " + ip + " -> " + ipl + " as integer" };
}
var found = find(ipl);
if (found) {
var loc = geoip.locations[found.i];
return {
status:"success",
country: getCountryName(loc.cn),
countryCode:loc.cn,
city:loc.ci,
region:loc.re,
zip:loc.pc,
lon:loc.lo,
lat:loc.la,
}
} else return none;
},
// ip-api.com relay using satelize module!
proxy : function (options) {
options=options||{http:9999};
var http = require('http');
var https;
try { https = require('https') } catch (e) { }
if (options.http) {
var httpSrv = http.createServer(function (request,response) {
var url=request.url,body,header,sep,query,now,
remote=request.connection.remoteAddress;
if (request.url.length)
query=parseQueryString(request.remote||request.url);
else
query={}
if (url.match(/\/json\/([0-9\.]+)/)) query.ip=url.match(/\/json\/([0-9\.]+)/)[1];
if (geoip.verbose>0) print(url,query,remote);
switch (request.method) {
case 'GET':
sat.satelize({ip:query.ip||remote},function (err,info) {
if (err) {
return reply(response,JSON.stringify({error:err}))
} else {
if (request.headers && request.headers.host) info.proxy=request.headers.host;
return reply(response,JSON.stringify(info))
}
})
break;
}
});
httpSrv.on("connection", function (socket) {
// socket.setNoDelay(true);
});
httpSrv.on("error", function (err) {
out(err)
});
httpSrv.listen(options.http,function (err) {
out('HTTP server started on port '+options.http);
});
};
if (options.https && https && options.pem) {
// requires options.pem={key,cert}
var httpsSrv = https.createServer(options.pem,function (request,response) {
var url=request.url,body,header,sep,query,now,
remote=request.connection.remoteAddress;
if (request.url.length)
query=parseQueryString(request.remote||request.url);
else
query={}
if (url.match(/\/json\/([0-9\.]+)/)) query.ip=url.match(/\/json\/([0-9\.]+)/)[1];
if (geoip.verbose>0) print(url,query,remote);
switch (request.method) {
case 'GET':
sat.satelize({ip:query.ip||remote},function (err,info) {
if (err) {
return reply(response,JSON.stringify({error:err}))
} else {
if (request.headers && request.headers.host) info.proxy=request.headers.host;
return reply(response,JSON.stringify(info))
}
})
break;
}
});
httpsSrv.on("connection", function (socket) {
// socket.setNoDelay(true);
});
httpsSrv.on("error", function (err) {
out(err)
});
httpsSrv.listen(options.https,function (err) {
out('HTTPS server started on port '+options.https);
});
};
},
// Start an ip-api.com compatible web server API
server : function (options) {
options=options||{http:{address:'localhost',port:9999}};
var http = require('http');
var https;
try { https = require('https') } catch (e) { }
geoip.load(function (err) {
if (err) return;
if (options.http) {
var httpSrv = http.createServer(function (request,response) {
var url=request.url,body,header,sep,query,now,
remote=request.connection.remoteAddress;
if (request.url.length)
query=parseQueryString(request.remote||request.url);
else
query={}
if (url.match(/\/json\/([0-9\.]+)/)) query.ip=url.match(/\/json\/([0-9\.]+)/)[1];
if (geoip.verbose>0) print(url,query,remote);
switch (request.method) {
case 'GET':
reply(response,JSON.stringify(geoip.lookup(query.ip||remote)))
break;
}
})
httpSrv.on("connection", function (socket) {
// socket.setNoDelay(true);
});
httpSrv.on("error", function (err) {
out(err)
});
httpSrv.listen(options.http.port,function (err) {
out('HTTP server started on port '+options.http.port);
});
}
if (options.https && https && options.pem) {
// requires options.pem={key,cert}
var httpsSrv = https.createServer(options.pem,function (request,response) {
var url=request.url,body,header,sep,query,now,
remote=request.connection.remoteAddress;
if (request.url.length)
query=parseQueryString(request.remote||request.url);
else
query={}
if (url.match(/\/json\/([0-9\.]+)/)) query.ip=url.match(/\/json\/([0-9\.]+)/)[1];
if (geoip.verbose>0) print(url,query,remote);
switch (request.method) {
case 'GET':
reply(response,JSON.stringify(geoip.lookup(query.ip||remote)))
break;
}
})
httpsSrv.on("connection", function (socket) {
// socket.setNoDelay(true);
});
httpsSrv.on("error", function (err) {
out(err)
});
httpsSrv.listen(options.http.port,function (err) {
out('HTTPS server started on port '+options.http.port);
});
}
});
},
// Save the DB in JSON format
save : function () {
out ('Saving '+geoip.dir + "/GeoLiteCity-Blocks.json ..");
var jsblocks = JSON.stringify(geoip.ipblocks);
fs.writeFileSync(geoip.dir + "/GeoLiteCity-Blocks.json", jsblocks, 'utf8');
out ('Saving '+geoip.dir + "/GeoLiteCity-Location.json ..");
var jslocations = JSON.stringify(geoip.locations);
fs.writeFileSync(geoip.dir + "/GeoLiteCity-Location.json", jslocations, 'utf8');
},
};
function iplong(ip) {
if(!ip) {
return 0;
}
ip = ip.toString();
if(isNaN(ip) && ip.indexOf(".") == -1) {
return 0;
}
if(ip.indexOf(".") == -1) {
try {
ip = parseFloat(ip);
return ip < 0 || ip > 4294967296 ? 0 : ip;
}
catch(s) {
}
}
var parts = ip.split(".");
if(parts.length != 4) {
return 0;
}
var ipl = 0;
for(var i=0; i<4; i++) {
parts[i] = parseInt(parts[i], 10);
if(parts[i] < 0 || parts[i] > 255) {
return 0;
}
ipl += parts[3-i] * (Math.pow(256, i));
}
return ipl > 4294967296 ? 0 : ipl;
}
/**
* A qcuick little binary search
* @param ip the ip we're looking for
* @return {*}
*/
function find(ipl) {
var mpi = 0;
var n = geoip.midpoints[0];
var step;
var current;
var next;
var prev;
var nn;
var pn;
while(true) {
step = geoip.midpoints[mpi];
mpi++;
current = geoip.ipblocks[n];
nn = n + 1;
pn = n - 1;
next = nn < geoip.numblocks ? geoip.ipblocks[nn] : null;
prev = pn > -1 ? geoip.ipblocks[pn] : null;
// take another step?
if(step > 0) {
if(!next || next.a < ipl) {
n += step;
} else {
n -= step;
}
continue;
}
// we're either current, next or previous depending on which is closest to ipl
var cd = Math.abs(ipl - current.a);
var nd = next && next.a < ipl ? ipl - next.a : 1000000000;
var pd = prev && prev.a < ipl ? ipl - prev.a : 1000000000;
// current wins
if(cd < nd && cd < pd) {
return current;
}
// next wins
if(nd < cd && nd < pd) {
return next;
}
// prev wins
return prev;
}
return none;
}
// https://gist.github.com/maephisto
var isoCountries = {
'AF' : 'Afghanistan',
'AX' : 'Aland Islands',
'AL' : 'Albania',
'DZ' : 'Algeria',
'AS' : 'American Samoa',
'AD' : 'Andorra',
'AO' : 'Angola',
'AI' : 'Anguilla',
'AQ' : 'Antarctica',
'AG' : 'Antigua And Barbuda',
'AR' : 'Argentina',
'AM' : 'Armenia',
'AW' : 'Aruba',
'AU' : 'Australia',
'AT' : 'Austria',
'AZ' : 'Azerbaijan',
'BS' : 'Bahamas',
'BH' : 'Bahrain',
'BD' : 'Bangladesh',
'BB' : 'Barbados',
'BY' : 'Belarus',
'BE' : 'Belgium',
'BZ' : 'Belize',
'BJ' : 'Benin',
'BM' : 'Bermuda',
'BT' : 'Bhutan',
'BO' : 'Bolivia',
'BA' : 'Bosnia And Herzegovina',
'BW' : 'Botswana',
'BV' : 'Bouvet Island',
'BR' : 'Brazil',
'IO' : 'British Indian Ocean Territory',
'BN' : 'Brunei Darussalam',
'BG' : 'Bulgaria',
'BF' : 'Burkina Faso',
'BI' : 'Burundi',
'KH' : 'Cambodia',
'CM' : 'Cameroon',
'CA' : 'Canada',
'CV' : 'Cape Verde',
'KY' : 'Cayman Islands',
'CF' : 'Central African Republic',
'TD' : 'Chad',
'CL' : 'Chile',
'CN' : 'China',
'CX' : 'Christmas Island',
'CC' : 'Cocos (Keeling) Islands',
'CO' : 'Colombia',
'KM' : 'Comoros',
'CG' : 'Congo',
'CD' : 'Congo, Democratic Republic',
'CK' : 'Cook Islands',
'CR' : 'Costa Rica',
'CI' : 'Cote D\'Ivoire',
'HR' : 'Croatia',
'CU' : 'Cuba',
'CY' : 'Cyprus',
'CZ' : 'Czech Republic',
'DK' : 'Denmark',
'DJ' : 'Djibouti',
'DM' : 'Dominica',
'DO' : 'Dominican Republic',
'EC' : 'Ecuador',
'EG' : 'Egypt',
'SV' : 'El Salvador',
'GQ' : 'Equatorial Guinea',
'ER' : 'Eritrea',
'EE' : 'Estonia',
'ET' : 'Ethiopia',
'FK' : 'Falkland Islands (Malvinas)',
'FO' : 'Faroe Islands',
'FJ' : 'Fiji',
'FI' : 'Finland',
'FR' : 'France',
'GF' : 'French Guiana',
'PF' : 'French Polynesia',
'TF' : 'French Southern Territories',
'GA' : 'Gabon',
'GM' : 'Gambia',
'GE' : 'Georgia',
'DE' : 'Germany',
'GH' : 'Ghana',
'GI' : 'Gibraltar',
'GR' : 'Greece',
'GL' : 'Greenland',
'GD' : 'Grenada',
'GP' : 'Guadeloupe',
'GU' : 'Guam',
'GT' : 'Guatemala',
'GG' : 'Guernsey',
'GN' : 'Guinea',
'GW' : 'Guinea-Bissau',
'GY' : 'Guyana',
'HT' : 'Haiti',
'HM' : 'Heard Island & Mcdonald Islands',
'VA' : 'Holy See (Vatican City State)',
'HN' : 'Honduras',
'HK' : 'Hong Kong',
'HU' : 'Hungary',
'IS' : 'Iceland',
'IN' : 'India',
'ID' : 'Indonesia',
'IR' : 'Iran, Islamic Republic Of',
'IQ' : 'Iraq',
'IE' : 'Ireland',
'IM' : 'Isle Of Man',
'IL' : 'Israel',
'IT' : 'Italy',
'JM' : 'Jamaica',
'JP' : 'Japan',
'JE' : 'Jersey',
'JO' : 'Jordan',
'KZ' : 'Kazakhstan',
'KE' : 'Kenya',
'KI' : 'Kiribati',
'KR' : 'Korea',
'KW' : 'Kuwait',
'KG' : 'Kyrgyzstan',
'LA' : 'Lao People\'s Democratic Republic',
'LV' : 'Latvia',
'LB' : 'Lebanon',
'LS' : 'Lesotho',
'LR' : 'Liberia',
'LY' : 'Libyan Arab Jamahiriya',
'LI' : 'Liechtenstein',
'LT' : 'Lithuania',
'LU' : 'Luxembourg',
'MO' : 'Macao',
'MK' : 'Macedonia',
'MG' : 'Madagascar',
'MW' : 'Malawi',
'MY' : 'Malaysia',
'MV' : 'Maldives',
'ML' : 'Mali',
'MT' : 'Malta',
'MH' : 'Marshall Islands',
'MQ' : 'Martinique',
'MR' : 'Mauritania',
'MU' : 'Mauritius',
'YT' : 'Mayotte',
'MX' : 'Mexico',
'FM' : 'Micronesia, Federated States Of',
'MD' : 'Moldova',
'MC' : 'Monaco',
'MN' : 'Mongolia',
'ME' : 'Montenegro',
'MS' : 'Montserrat',
'MA' : 'Morocco',
'MZ' : 'Mozambique',
'MM' : 'Myanmar',
'NA' : 'Namibia',
'NR' : 'Nauru',
'NP' : 'Nepal',
'NL' : 'Netherlands',
'AN' : 'Netherlands Antilles',
'NC' : 'New Caledonia',
'NZ' : 'New Zealand',
'NI' : 'Nicaragua',
'NE' : 'Niger',
'NG' : 'Nigeria',
'NU' : 'Niue',
'NF' : 'Norfolk Island',
'MP' : 'Northern Mariana Islands',
'NO' : 'Norway',
'OM' : 'Oman',
'PK' : 'Pakistan',
'PW' : 'Palau',
'PS' : 'Palestinian Territory, Occupied',
'PA' : 'Panama',
'PG' : 'Papua New Guinea',
'PY' : 'Paraguay',
'PE' : 'Peru',
'PH' : 'Philippines',
'PN' : 'Pitcairn',
'PL' : 'Poland',
'PT' : 'Portugal',
'PR' : 'Puerto Rico',
'QA' : 'Qatar',
'RE' : 'Reunion',
'RO' : 'Romania',
'RU' : 'Russian Federation',
'RW' : 'Rwanda',
'BL' : 'Saint Barthelemy',
'SH' : 'Saint Helena',
'KN' : 'Saint Kitts And Nevis',
'LC' : 'Saint Lucia',
'MF' : 'Saint Martin',
'PM' : 'Saint Pierre And Miquelon',
'VC' : 'Saint Vincent And Grenadines',
'WS' : 'Samoa',
'SM' : 'San Marino',
'ST' : 'Sao Tome And Principe',
'SA' : 'Saudi Arabia',
'SN' : 'Senegal',
'RS' : 'Serbia',
'SC' : 'Seychelles',
'SL' : 'Sierra Leone',
'SG' : 'Singapore',
'SK' : 'Slovakia',
'SI' : 'Slovenia',
'SB' : 'Solomon Islands',
'SO' : 'Somalia',
'ZA' : 'South Africa',
'GS' : 'South Georgia And Sandwich Isl.',
'ES' : 'Spain',
'LK' : 'Sri Lanka',
'SD' : 'Sudan',
'SR' : 'Suriname',
'SJ' : 'Svalbard And Jan Mayen',
'SZ' : 'Swaziland',
'SE' : 'Sweden',
'CH' : 'Switzerland',
'SY' : 'Syrian Arab Republic',
'TW' : 'Taiwan',
'TJ' : 'Tajikistan',
'TZ' : 'Tanzania',
'TH' : 'Thailand',
'TL' : 'Timor-Leste',
'TG' : 'Togo',
'TK' : 'Tokelau',
'TO' : 'Tonga',
'TT' : 'Trinidad And Tobago',
'TN' : 'Tunisia',
'TR' : 'Turkey',
'TM' : 'Turkmenistan',
'TC' : 'Turks And Caicos Islands',
'TV' : 'Tuvalu',
'UG' : 'Uganda',
'UA' : 'Ukraine',
'AE' : 'United Arab Emirates',
'GB' : 'United Kingdom',
'US' : 'United States',
'UM' : 'United States Outlying Islands',
'UY' : 'Uruguay',
'UZ' : 'Uzbekistan',
'VU' : 'Vanuatu',
'VE' : 'Venezuela',
'VN' : 'Viet Nam',
'VG' : 'Virgin Islands, British',
'VI' : 'Virgin Islands, U.S.',
'WF' : 'Wallis And Futuna',
'EH' : 'Western Sahara',
'YE' : 'Yemen',
'ZM' : 'Zambia',
'ZW' : 'Zimbabwe'
};
function getCountryName (countryCode) {
if (isoCountries.hasOwnProperty(countryCode)) {
return isoCountries[countryCode];
} else {
return countryCode;
}
}
/*
** Parse query string '?attr=val&attr=val... and return parameter record
*/
function parseQueryString( url ) {
var queryString = url.substring( url.indexOf('?') + 1 );
if (queryString == url) return [];
var params = {}, queries, temp, i, l;
// Split into key/value pairs
queries = queryString.split("&");
// Convert the array of strings into an object
for ( i = 0, l = queries.length; i < l; i++ ) {
temp = queries[i].split('=');
if (temp[1]==undefined) temp[1]='true';
params[temp[0]] = temp[1].replace('%20',' ');
}
return params;
}
function reply(response,body,mimetype) {
header={'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true',
'Content-Type': mimetype||'text/plain'};
response.writeHead(200,header);
response.write(body);
response.end();
}
};
BundleModuleCode['dos/ext/satelize']=function (module,exports){
/*
* satelize - v0.1.3
*
* (c) 2013 Julien VALERY https://github.com/darul75/satelize, 2018-2020 modfied by bLAB Dr. Stefan Bosse
*
* Usage: satelize(ip:string|undefined,function (err,info))
*
* License: MIT
*/
var http=Require("http"),
serviceHost="ip-api.com",
servicePort=80,
servicePath="/json",
serviceJSONP="";
function Satelize(options){
this.init()
}
Satelize.prototype.init=function(options){
return this
}
Satelize.prototype.satelize=function(a,b){
var c=(a.ip?"/"+a.ip:"")+(a.JSONP?serviceJSONP:""),
d=a.timeout||1e3,
h=a.url||a.host||serviceHost,
p=a.port||servicePort,
e,
f;
if (!http) return b('ENOTSUPPORTED',null);
if (!http.xhr && http.request) {
// server
e={hostname:h,path:servicePath+c,method:"GET",port:p};
f=http.request(e,function(a){
a.setEncoding("utf8");
var c="";
a.on("data",function(a){c+=a}),
a.on("end",function(){
try {
return b(null,JSON.parse(c));
} catch (err) {
b(err.toString()+', '+e.hostname+':'+e.port);
}
})
});
return f.on("error",function(a){return b(a)}),
f.setTimeout(d,function(){return b(new Error("timeout"))}),
f.end(),this;
} else {
// Browser
e={uri:a.url?a.url:(a.proto?a.proto:'http')+'://'+h+':'+p+servicePath+c,
method:"GET",
headers:{}};
console.log(e);
http.request(e,function(err,xhr,body){
if (err) return b(err);
else try { b(null,JSON.parse(body)); } catch (err) { b(err.toString()+', '+e.uri) }
})
return this;
}
}
var sat = new Satelize
module.exports=sat;
};
BundleModuleCode['top/rendezvous']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.io
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2018 bLAB
** $CREATED: 30-11-17 by sbosse.
** $RCS: $Id: rendezvous.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.2.2
**
** $INFO:
**
** Simple public P2P rendezvous (pairing) server with associative naming service.
** Primary use: Enabling JAM2JAM connections with JAMs behind NATs
** (hosts in different private networks).
** Uses hole-punching technique to overcome router limitations occuring with NAT traversal of
** UDP streams.
**
**
** A host stores tokens in a cache, One token is removed on each pairing request or if the lifetime
** of the token has expired. There is an upper limit of tokens that are cached.
**
** $ENDOFINFO
*/
global.config={simulation:false,nonetwork:false};
var Comp = Require('com/compat');
var Io = Require('com/io');
var Chan = Require('jam/chan');
var Amp = Require('jam/amp');
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var sprintf = Comp.printf.sprintf;
var ipnet = Require('net');
var dgram = Require('dgram');
var sprintf = Comp.printf.sprintf;
var onexit=false;
var start=false;
var options = {
connport:Net.uniqport(),
http: {address:'134.102.50.219',port:80},
ip : {address:'0.0.0.0',port:10001},
verbose:1,
CACHETMO:60000,
MAXTOKENS: 4, // maximal cached register tokens from each host
TIMER:200,
TRIES:3,
version:'1.2.2'
}
var usage = function (exit) {
out('Usage: rendezvous [-h] [verbose:#] [port:#]');
if (exit) onexit=true,start=false;
}
if (process.argv[1].indexOf('ampbroker')!=-1 || process.argv[1].indexOf('rendezvous')!=-1)
start=true,process.argv.forEach(function (arg) {
var tokens=arg.split(':');
if (arg=='-h' || arg=='-help') usage(true);
if (tokens.length!=2) return;
switch (tokens[0]) {
case 'verbose': options.verbose=Number(tokens[1]); break;
case 'port': options.ip.port=Number(tokens[1]); break;
}
});
// Use remote TCP connection to get this host IP (private address if behind NAT)
function getNetworkIP(callback) {
var socket = ipnet.createConnection(options.http.port, options.http.address);
socket.on('connect', function() {
callback(undefined, socket.address().address);
socket.end();
});
socket.on('error', function(e) {
callback(e, 'error');
});
}
function timestamp() {
return Date.now();
}
// typeof @ip = { address:string, port:number }
function Broker (_options) {
var self=this;
if (!(this instanceof Broker)) return new Broker(_options);
this.options=options;
for (var p in _options) if (_options[p]!=undefined) options[p]=_options[p];
this.out = function (msg) {console.log('[RED '+Chan.addr2url(options.ip)+' '+Io.Time()+'] '+msg)};
this.udp = dgram.createSocket('udp4');
// The rendezvous cache (register tokens)
this.clients = {};
function doUntil(interval, fn, cond, arg) {
if (cond()) return;
fn(arg);
return setTimeout(function() {
doUntil(interval, fn, cond, arg);
}, interval);
}
// Compare two client db entries
function eq(client1,client2) {
var p;
if (!client1 || !client2 ||
client1.name != client2.name) return false;
for(p in client1.connections) {
if (client1.connections[p].address != client2.connections[p].address ||
client1.connections[p].port != client2.connections[p].port) return false;
}
return true;
}
// Store and lookup
function store(name,client) {
client.time=timestamp()
// Note: Old obsolete tokens of a client (changed IP/PORT) must be flushed!
if (!self.clients[name] || !eq(client,self.clients[name][0])) self.clients[name]=[client];
else if (self.clients[name].length<options.MAXTOKENS) {
self.clients[name].push(client);
self.clients[name].forEach(function (client) { client.time=timestamp() });
}
}
// TODO don't return self entry (from:public address)
function lookup(pat,from,all) {
var isRegex=pat.indexOf('*')!=-1;
if (!isRegex) {
if (!self.clients[pat]) return all?[]:undefined;
return all?self.clients[pat]:self.clients[pat].pop();
} else {
// TODO pattern search
}
}
function search(pat) {
var isRegex=pat.indexOf('*')!=-1,regex,result=[];
if (!isRegex) {
return self.clients[pat] && self.clients[pat].length?[self.clients[pat][0]]:[];
} else {
regex=RegExp(pat.replace(/\//g,'\\/').replace(/\*/g,'.+'));
for(var p in self.clients) {
if (self.clients[p] && self.clients[p].length && regex.test(p)) result.push(p);
}
return result;
}
}
function send(host, port, msg, cb) {
var buf = Buf.Buffer();
var data = JSON.stringify(msg);
Buf.buf_put_int16(buf,Amp.AMMessageType.AMMCONTROL);
Buf.buf_put_port(buf,options.connport);
Buf.buf_put_string(buf,data);
self.udp.send(buf.data, 0, Buf.length(buf), port, host, function(err, bytes) {
if (err) {
udp.close();
self.out(sprintf('# stopped due to error: %s', err));
process.exit(-1);
} else {
if (options.verbose>1) self.out('# sent '+msg.type+' to '+host+':'+port);
if (cb) cb();
}
});
}
this.udp.on('listening', function() {
var address = self.udp.address();
if (options.verbose) self.out(sprintf ('# listening [%s:%s]', address.address, address.port));
});
this.udp.on('message', function(message, rinfo) {
var buf = Buf.Buffer(),reply,
port,data,msg,obj,i,j,newreg=false;
Buf.buf_init(buf);
Buf.buf_of_str(buf,message);
msgtyp=Buf.buf_get_int16(buf);
if (msgtyp != Amp.AMMessageType.AMMCONTROL) {
if (options.verbose)
self.out(sprintf('# Invalid message from %s:%s',
rinfo.address, rinfo.port));
return;
}
port = Buf.buf_get_port(buf);
data = Buf.buf_get_string(buf);
try {
msg = JSON.parse(data);
} catch (e) {
self.out(sprintf('! Couldn\'t parse data (%s):\n%s', e, data));
return;
}
switch (msg.type) {
case 'lookup':
reply=search(msg.data);
console.log(msg.data,reply)
send(rinfo.address,rinfo.port,{type:'lookup',from:'BROKER', data:reply, path:msg.data});
break;
case 'register':
obj={
name: msg.name,
connections: {
local: msg.linfo,
public: rinfo
},
};
// copy optional attributes
for(p in msg) {
switch (p) {
case 'name':
case 'linfo':
case 'type':
continue;
default:
obj[p]=msg[p];
}
}
store(msg.name,obj);
if (self.clients[msg.name].length==1) newreg=1;
if (options.verbose && newreg)
self.out(sprintf('# Client registered: P %s@[%s:%s | L %s:%s]', msg.name,
rinfo.address, rinfo.port, msg.linfo.address, msg.linfo.port));
send(rinfo.address,rinfo.port,{type:'registered',from:'BROKER'});
break;
case 'pair':
// Pair request from one client
var couple = [lookup(msg.from,rinfo), lookup(msg.to,rinfo) ], counter=options.TRIES;
if (options.verbose>1)
self.out(sprintf('# Pair request: %s@[%s:%s] to %s [%b,%b]', msg.from,
rinfo.address, rinfo.port, msg.to, couple[0]!=undefined,couple[1]!=undefined));
else if (options.verbose && couple[0]!=undefined && couple[1]!=undefined)
self.out(sprintf('# Pairing %s@[%s:%d] and %s@[%s:%d]',
msg.from,couple[0].connections.public.address, couple[0].connections.public.port,
msg.to,couple[1].connections.public.address, couple[1].connections.public.port));
for (i=0; i<couple.length; i++) {
if (!couple[i]) {
// restore not consumed client conenction
for(j = 0; j<couple.length; j++) {
if (couple[j]) store(couple[j].name,couple[j]);
}
return self.out('Client '+(i+1)+' unknown! '+(i==0?msg.from:msg.to));
}
}
// Start pairing with punch messages on both clients
// Echo pairing to minimize deadlock possibility if pairing messages are lost
doUntil(options.TIMER,function () {
for (var i=0; i<couple.length; i++) {
send(couple[i].connections.public.address, couple[i].connections.public.port, {
type: 'pairing',
client: couple[(i+1)%couple.length],
});
}
counter--;
},function () { return counter==0});
// Only one pairing can be peformed; next time a new registering is required
break;
};
});
}
Broker.prototype.init = function () {
var self=this;
// Start GC
this.gc = setInterval(function () {
var time=timestamp();
for (var p in self.clients) {
if (self.clients[p]) self.clients[p]=self.clients[p].filter(function (conn) {
// console.log(p,time,conn.time)
return time>conn.time+self.options.CACHETMO;
});
}
},this.options.CACHETMO);
}
Broker.prototype.start = function () {
var self=this;
getNetworkIP(function (err,addr) {
if (!err) {
self.options.ip.address=addr;
self.out('# got IP '+addr);
}
});
this.udp.bind(options.ip.port,options.ip.address);
}
Broker.prototype.stop = function () {
if (this.gc) clearInterval(this.gc),this.gc=undefined;
}
if (start) {
var bs = new Broker(options);
bs.start()
}
module.exports = { Broker:Broker };
};
BundleModuleCode['jam/chan']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 09-02-16 by sbosse.
** $RCS: $Id: chan.js,v 1.4 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.16.1
**
** $INFO:
**
** JavaScript AIOS Agent Node Communication Module offering P2P communication with another nodes
**
** 1. Virtual Link: Connecting virtual (logical) nodes using buffers
**
** 2. Physical Link: Connecting physical nodes (on the same physical host or remote hosts)
** using AMP protocol and IP communication (including endpoint pairing across NAT routers
** using a rendezvous broker service)
**
** 3. Physical Link: Connecting node processes (in a cluster on the same physical host) using process streams
**
** For IP-based communication ports an internal IP router is provided offering operation
** of multiple ports and connections.
**
** Communciation link object provided by 1.-3.:
**
** type link = {
** on: method (@event,@handler) with @event={'agent'|'signal'|'class'},
** send: method (@msg) with @msg:{agent:string|object,to:dir}|{signal:string|object},to:dir},
** status: method (dir) -> boolean,
** count: method () -> number is returning number of received (phy only) and sent bytes,
** connect?:method (@to),
** disconnect?:method (@to),
** start?:method,
** stop?:method
** }
**
**
** Events, emitter: link+ link- error(err="link"|string,arg?)
**
**
** TODO:
** - Phy capability protected communication and operations
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Lz = Require('os/lz-string');
var Comp = Require('com/compat');
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var Command = Net.Command;
var Status = Net.Status;
var current=none;
var Aios=none;
var CBL = Require('com/cbl');
var Amp = Require('jam/amp');
var Sec = Require('jam/security');
var options = {
debug:{},
verbose:1,
version:'1.15.6'
}
module.exports.options=options;
var SLINK = {
INIT:'INIT',
INITED:'INITED',
RUNNING:'RUNNING'
}
/********************
* Virtual Circuit
********************
*/
var virtual= function (node1,node2,dir,options) {
var self=this;
this.node1=node1;
this.node2=node2;
this.dir=dir; // node1 -> node2
this.buffer1=[];
this.buffer2=[];
this.count1={rcv:0,snd:0};
this.count2={rcv:0,snd:0};
this.compress=options.compress;
/* NEWCOMM */
this.handler1=[];
this.handler2=[];
// External API
this.link1 = {
control : function (msg,to,callback) {
// TODO
},
on: function (event,callback) {
var data;
self.handler1[event]=callback;
if (event=='agent' && self.buffer2.length>0) {
// Agent receiver
data=Comp.array.pop(self.buffer2);
if (self.compress) data=Lz.decompress(data);
callback(data);
}
},
send: function (msg) {
var data;
if (msg.agent) {
// Agent migration
data=msg.agent;
if (self.compress) data=Lz.compress(data);
if (self.handler2.agent) self.handler2.agent(self.compress?Lz.decompress(data):data);
else self.buffer1.push(data);
if (data.length) self.count1.snd += data.length; else self.count1.snd++;
} else if (msg.signal) {
// Signal propagation - signals are not queued
data=msg.signal;
if (data.length) self.count1.snd += data.length; else self.count1.snd++;
if (self.handler2.signal) self.handler2.signal(data);
}
},
count: function () {return self.count1.snd},
status: function () {return true}, // Linked?
virtual:true
}
this.link2 = {
control : function (msg,to,callback) {
// TODO
},
on: function (event,callback) {
var data;
self.handler2[event]=callback;
if (event=='agent' && self.buffer1.length>0) {
// Agent receiver
data=Comp.array.pop(self.buffer1);
if (self.compress) data=Lz.decompress(data);
callback(data);
}
},
send: function (msg) {
var data;
if (msg.agent) {
// Agent migration
data=msg.agent;
if (self.compress) data=Lz.compress(data);
if (self.handler1.agent) self.handler1.agent(self.compress?Lz.decompress(data):data);
else self.buffer2.push(data);
if (data.length) self.count2.snd += data.length; else self.count2.snd++;
} else if (msg.signal) {
// Signal propagation - signals are not queued
data=msg.signal;
if (data.length) self.count2.snd += data.length; else self.count2.snd++;
if (self.handler1.signal) self.handler1.signal(data);
}
},
count: function () {return self.count2.snd},
status: function () {return true}, // Linked?
virtual:true
}
};
virtual.prototype.init = function () {};
virtual.prototype.start = function () {};
virtual.prototype.stop = function () {};
var Virtual = function (node1,node2,dir,options) {
var obj=new virtual(node1,node2,dir,options);
return obj;
}
module.exports.Virtual=Virtual;
module.exports.current=function (module) { current=module.current; Aios=module; Amp.current(module); };
if (global.config.nonetwork) return;
/******************************* PHYSICAL *************************************/
/*********************
** Physical Circuit
*********************
*
* Using AMP or process stream connections (TODO)
* typeof options={
* broker?:url is UDP hole punching rendezvous broker
* compress?:boolean,
* device?:string,
* name?:string is optional name of the comm. port e.g. the JAM node name,
* on?: { } is event handler object,
* oneway?:boolean,
* out?:function,
* proto?:'udp'|'tcp'|'http'|'hardware',
* rcv:url is this endpoint address,
* secure?:port string,
* snd?:url is remote endpoint address,
* stream?:boolean,
* verbose?
* ..
* }
* with type url = "<name>:<ipport>" | "<ip>:<ipport>" | "<ipport>"
* and type ipport = (1-65535) | "*"
*/
var physical= function (node,dir,options) {
var self=this;
options=checkOptions(options,{});
this.options=options;
this.ip=none;
if (options.rcv) this.ip=url2addr(options.rcv);
else this.ip={address:Amp.options.localhost,port:undefined};
if (options.proto && this.ip) this.ip.proto=options.proto;
this.node=node;
this.dir=dir; // outgoing port (node -> dst), e.g., IP
this.count=0;
this.broker=options.broker;
this.mode=this.options.compress?Amp.AMMode.AMO_COMPRESS:0;
this.state = SLINK.INIT;
this.linked = 0;
this.events = [];
this.callbacks = [];
this.out=function (msg,async) { async?Aios.logAsync(msg):Aios.log(msg) };
if (this.ip.parameter && this.ip.parameter.secure) {
this.options.secure=Sec.Port.ofString(this.ip.parameter.secure);
delete this.ip.parameter;
this.dir.ip=addr2url(this.ip);
}
this.amp= Amp.Amp({
broker:options.broker?url2addr(options.broker,this.ip.address):undefined,
dir:this.dir,
keepAlive : options.keepAlive,
mode:this.options.mode,
multicast:this.options.multicast,
name:this.options.name,
node:node,
nodeid:this.options.nodeid,
oneway:this.options.oneway,
proto:this.options.proto,
pem:this.options.pem,
rcv:this.ip,
secure:this.options.secure,
snd:options.snd?url2addr(options.snd):undefined,
sharedSocket:options.sharedSocket,
sock:options.sock,
verbose:options.verbose,
});
// External API
this.link = {
// Control RPC
// STD_STATUS/PS_STUN/...
control : function (msg,to,callback) {
var buf,data,addr=to?url2addr(to):{};
buf=Buf.Buffer();
msg.tid=Comp.random.int(65536/2);
self.callbacks[msg.tid]=callback;
Buf.buf_put_int16(buf,msg.tid);
Buf.buf_put_string(buf,JSON.stringify(msg.args||{}));
self.amp.request(msg.cmd,
buf,
self.amp.mode & Amp.AMMode.AMO_MULTICAST? addr:undefined);
},
on: function (event,callback) {
self.events[event]=callback;
},
send: function (msg,to) {
var buf,data,addr=to?url2addr(to):{};
if (msg.agent) {
data=msg.agent; // string of JSON+
buf=Buf.Buffer();
if (self.mode & Amp.AMMode.AMO_COMPRESS) data=Lz.compress(data);
Buf.buf_put_string(buf,data);
// function request(cmd:integer,msg:Buffer,snd?:address)
self.amp.request(Command.PS_MIGRATE,
buf,
self.amp.mode & Amp.AMMode.AMO_MULTICAST? addr:undefined);
} else if (msg.signal) {
data=msg.signal; // string of JSON
// Signal propagation
buf=Buf.Buffer();
if (self.mode & Amp.AMMode.AMO_COMPRESS) data=Lz.compress(data);
Buf.buf_put_string(buf,data);
// function request(cmd:integer,msg:Buffer,snd?:address)
self.amp.request(Command.PS_SIGNAL,
buf,
self.amp.mode & Amp.AMMode.AMO_MULTICAST? addr:undefined);
}
},
count: function () {return self.amp.count.rcv+self.amp.count.snd},
status : function (to) {
if (self.amp) {
switch (to) {
case '%':
// P2P link?; return remote node/ip/link id
return self.amp.status(to);
break;
default:
if (to) to=url2addr(to);
return to?self.amp.status(to.address,to.port):self.amp.status();
}
}
}, // Linked?
stats : function () {
return {
transferred:(self.amp.count.rcv+self.amp.count.snd),
linked:self.linked
}
},
ip:this.ip,
mode:this.amp.mode
}
/** Connect to remote endpoint with optional capability key protection
* typeof @to = "<url>" | "<path>" | "<ip>:<ipport>" | "<ipport>"
* typeof @key = string "[<port>](<rights>)[<protport>]"
*/
this.link.connect=function (to,key) {
// allow url2addr DNS lookup
url2addr(to,self.ip.address,function (addr) {
self.amp.link(addr,true,key);
})
};
// Disconnect remote endpoint
this.link.disconnect=function (to) {
var tokens;
if (!to){
if (self.amp.snd && self.amp.snd.address && self.amp.snd.port)
self.amp.unlink(self.amp.snd);
} else {
var addr=url2addr(to,self.ip.address);
self.amp.unlink(addr);
}
};
this.link.init=function (cb) {
if (self.state!=SLINK.INIT) return cb?cb():null;
self.state=SLINK.INITED;
return self.amp.init(cb);
}
this.link.start=function (cb) {
if (self.state!=SLINK.INITED) return cb?cb():null;
self.state=SLINK.RUNNING;
return self.amp.start(cb);
}
this.link.stop=function (cb) {
if (self.state!=SLINK.RUNNING) return cb?cb():null;
self.state=SLINK.INITED;
return self.amp.stop(cb);
}
if (this.broker) this.link.lookup = function (path,callback) {
if (self.amp.lookup) self.amp.lookup(path,callback);
else if (callback) callback([]);
}
// Install route notification propagation to router (if installed)
this.amp.on('route+',function (url,node,remote) {
if (remote) self.ip.public=remote;
if (self.router) self.router.add(url,self.link,node);
self.emit('link+',url,node);
Aios.emit('link+',url,node);
self.linked++;
});
this.amp.on('route-',function (url) {
if (self.router) self.router.delete(url,self.link);
self.emit('link-',url);
Aios.emit('link-',url);
self.linked--;
});
this.amp.on('error',function (err,arg) {
self.emit('error',err,arg);
});
if (options.on) {
for(var p in options.on) this.on(p,options.on[p]);
}
// Register message receiver handler with STD/PS RPC
this.amp.receiver(function (handler) {
var code,name,env,agentid,stat,obj,buf,status,tid;
if (!handler) return;
if (self.options.verbose>2) {
self.out('AMP: got request: '+ Io.inspect(handler),true);
};
switch (handler.cmd) {
case Command.PS_MIGRATE:
code = Buf.buf_get_string(handler.buf);
// console.log(code);
// console.log(myJam.amp.url(handler.remote))
if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code);
if (self.events.agent) self.events.agent(code,false,handler.remote);
break;
case Command.PS_CREATE:
code = Buf.buf_get_string(handler.buf);
// console.log(code);
// console.log(myJam.amp.url(handler.remote))
if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code);
if (self.events.agent) self.events.agent(code,true);
break;
case Command.PS_WRITE:
name = Buf.buf_get_string(handler.buf);
code = Buf.buf_get_string(handler.buf);
env = Buf.buf_get_string(handler.buf);
// console.log(code);
// console.log(myJam.amp.url(handler.remote))
if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code);
obj={};
try {eval("env = "+env)} catch (e) {};
obj[name]={
fun:code,
env:env
}
if (self.events['class']) self.events['class'](obj);
break;
case Command.PS_SIGNAL:
// TODO
code = Buf.buf_get_string(handler.buf);
// console.log(code);
if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code);
if (self.events.signal) self.events.signal(code,handler.remote);
break;
case Command.PS_STUN:
// Kill an agent (or all)
code = Buf.buf_get_string(handler.buf);
break;
// Control Mesages
case Command.STD_STATUS:
// Send status of requested object (node: process table..)
tid = Buf.buf_get_int16(handler.buf);
code = Buf.buf_get_string(handler.buf);
code = JSON.parse(code);
status = {};
if (typeof code == 'string') {
switch (code) {
case 'node':
case 'links':
case 'ports':
case 'agents':
status=self.node.std_status(code); break;
}
}
buf=Buf.Buffer();
Buf.buf_put_int16(buf,tid);
Buf.buf_put_string(buf,JSON.stringify(status));
self.amp.reply(Command.STD_MONITOR, // Hack: Reply to STD_STATUS request
buf,
self.amp.mode & Amp.AMMode.AMO_MULTICAST? handler.remote:undefined);
break;
case Command.STD_INFO:
// Send info about .. (agent ..)
tid = Buf.buf_get_int16(handler.buf);
code = JSON.parse(Buf.buf_get_string(handler.buf));
status = {};
if (typeof code == 'string') {
switch (code) {
case 'node' : status=self.node.std_info(); break;
}
} else if (typeof code == 'object') {
if (code.node) {
// get info of a specific node; probably not this but maybe attached?
if (code.node==self.node.id) status=self.node.std_info();
else {
// one hop is possible if destination node is connected to this node, too
var to = COM.lookupNode(self.node,code.node);
console.log(to);
// to.link.control({
// cmd:COM.Command.STD_INFO,
// args:code,
// },to.url, function (reply) {
// self.amp.reply(Command.STD_MONITOR()...
// })
}
}
if (code.agent) {
status=self.node.std_info(code);
}
}
buf=Buf.Buffer();
Buf.buf_put_int16(buf,tid);
Buf.buf_put_string(buf,JSON.stringify(status));
self.amp.reply(Command.STD_MONITOR, // Hack: Reply to STD_INFO request
buf,
self.amp.mode & Amp.AMMode.AMO_MULTICAST? handler.remote:undefined);
break;
case Command.STD_MONITOR:
// Hack: Reply to STD_STATUS/INFO request
tid = Buf.buf_get_int16(handler.buf);
code = Buf.buf_get_string(handler.buf);
code = JSON.parse(code);
if (self.callbacks[tid]) {
self.callbacks[tid](code);
delete self.callbacks[tid];
}
break;
}
});
};
physical.prototype.emit = function (event,arg,aux1,aux2) { if (this.events[event]) this.events[event](arg,aux1,aux2)};
physical.prototype.on = function (event,handler) {this.events[event]=handler};
physical.prototype.init = function (callback) { return this.link.init(callback)};
physical.prototype.start = function (callback) {return this.link.start(callback)};
physical.prototype.stop = function () {return this.link.stop()};
var Physical = function (node,dir,options) {
var obj=new physical(node,dir,options);
return obj;
}
module.exports.Physical=Physical;
/*************************
** IP UTILS
*************************/
var url2addr=Amp.url2addr;
var addr2url=Amp.addr2url;
var resolve=Amp.resolve;
/* url = "<name>:<ipport>" | "<ip>:<ipport>" | "<ipport>"
* and ipport = (1-65535) | "*"
function url2addr(url,defaultIP) {
var addr={address:defaultIP||'localhost',proto:'IP',port:undefined},
parts = url.toString().split(':');
if (parts.length==1) {
if (Comp.string.isNumeric(parts[0])) addr.port=Number(parts[0]); // port number
else if (parts[0].indexOf('-') != -1) addr.port=parts[0]; // port range p0-p1
else if (parts[0]=='*') addr.port=undefined; // any port
else addr.address=parts[0]; // ip/url
} else return {address:parts[0],port:parts[1]=='*'?undefined:Number(parts[1])||parts[1]};
return addr;
};
function addr2url(addr) {
return (addr.proto?(addr.proto+'://'):'')+addr.address+':'+(addr.port?addr.port:'*')
};
function resolve (url,defaultIP) {
var addr=url2addr(url,defaultIP);
return addr2url(addr)
}
*/
function addrequal(addr1,addr2) {
return ipequal(addr1.address,addr2.address) && addr1.port==addr2.port;
}
function ipequal(ip1,ip2) {
if (ip1==undefined || ip2==undefined) return false;
else if ((Comp.string.equal(ip1,'localhost') || Comp.string.equal(ip1,'127.0.0.1')) &&
(Comp.string.equal(ip2,'localhost') || Comp.string.equal(ip2,'127.0.0.1'))) return true;
else return ip1==ip2;
}
/***********************************************
* IP Router using AMP/UDP/TCP/HTTP links
* Entry point for move and send operations DIR.IP
***********************************************
*/
function iprouter() {
this.routingTable={};
this.nodeTable={};
this.links=[];
}
// Add route and link to be used for the route (and optional remote node id)
iprouter.prototype.add = function (to,link,node) {
to=resolve(to);
if (options.verbose) Aios.logAsync('[IP] iprouter: add route '+addr2url(link.ip)+' -> '+to+(node?'#'+node:''));
this.routingTable[to]=link;
this.nodeTable[to]=node;
}
// Add link device
iprouter.prototype.addLink = function (link) {
if (!link.ip) link.ip='*';
if (options.verbose) Aios.logAsync('[IP] iprouter: add link '+addr2url(link.ip));
this.links.push(link);
}
// Connect to a remote endpoint
iprouter.prototype.connect = function (to,key) {
var link,p,addr;
to=resolve(to);
addr=url2addr(to);
// Search for an unconnected port!?
for(p in this.links) {
if (this.links[p].status(to)) return;
if (!(this.links[p].mode&Amp.AMMode.AMO_MULTICAST) && this.links[p].status()) continue;
if (addr.proto && this.links[p].ip && this.links[p].ip.proto != addr.proto) continue;
link=this.links[p];
break;
}
if (link && link.connect) {
link.connect(to,key);
}
}
//
iprouter.prototype.count = function (dest) {
var res=0;
for(var i in this.links) {
res += this.links[i].count();
}
return res;
}
// Remove route
iprouter.prototype.delete = function (to) {
to=resolve(to);
if (this.routingTable[to]) {
if (options.verbose) Aios.logAsync('[IP] iprouter: remove route '+addr2url(this.routingTable[to].ip)+ ' -> ' + to);
delete this.routingTable[to];
delete this.nodeTable[to];
}
}
// Disconnect a remote endpoint
iprouter.prototype.disconnect = function (to) {
// Search for a connected port!
to=resolve(to);
if (this.routingTable[to] && this.routingTable[to].status(to)) {
this.routingTable[to].disconnect(to);
}
}
/** Lookup a IP:PORT address pair of a nodeid OR contact a broker to get reachable
* nodeid-IP address pairs
*
*/
iprouter.prototype.lookup = function (nodeid,callback) {
var p,result=[],n=0;
// Broker lookup with a pattern like /domain/* (DIR.PATH)
if (nodeid.indexOf('*')!=-1) {
// TODO
for (p in this.links) {
if (this.links[p].lookup) {
n++;
this.links[p].lookup(nodeid,function (_result) {
if (_result && _result.length) result=result.concat(_result);
n--;
if (n==0) callback(result);
});
}
}
} else for(p in this.nodeTable) {
if (this.nodeTable[p] == nodeid && this.routingTable[p]) return p;
}
}
/** Try to find our local IP address.
*
*/
iprouter.prototype.ip = function () {
for(var i in this.links) {
if (this.links[i].ip) return this.links[i].ip;
}
}
/** Reverse lookup: Get the nodeid from an IP:PORT address
* typeof @ip = string <ip:ipport>
*/
iprouter.prototype.reverse = function (ip) {
return this.nodeTable[ip];
}
/** Send a message
*
*/
iprouter.prototype.send = function (msg) {
msg.to=resolve(msg.to);
if (this.routingTable[msg.to]) {
this.routingTable[msg.to].send(msg,msg.to);
} else {
}
}
/** Start all attached devices
*
*/
iprouter.prototype.start = function (callback) {
var cbl=CBL(callback||function(){});
this.links.forEach(function (link) {
cbl.push(function (next) {link.start(next)});
});
cbl.start();
}
iprouter.prototype.stats = function () {
return {
links:Object.keys(this.routingTable).length
}
}
// Check status of link in given direction (or any direction dest==undefined)
// OR return all current registered routes string [] (dest=='*')!
// OR return all current connected nodes string [] (dest=='%')!
// OR return all current registered links (ip) string [] (dest=='$')!
iprouter.prototype.status = function (dest) {
var res,p;
if (dest==undefined) {
// Any registered routes?
for(p in this.routingTable) { if (this.routingTable[p]) return true }
} else if (dest=='*') {
res=[];
for(p in this.routingTable) { if (this.routingTable[p]) res.push(p) }
return res;
} else if (dest=='%') {
res=[];
for(p in this.nodeTable) {
if (this.nodeTable[p] && this.routingTable[p]) res.push(this.nodeTable[p]);
}
return res;
} else {
dest=resolve(dest);
if (this.routingTable[dest])
return this.routingTable[dest].status(dest);
else
return false;
}
return false;
}
// Stop all attached devices
iprouter.prototype.stop = function (callback) {
var cbl=CBL(callback||function(){});
this.links.forEach(function (link) {
cbl.push(function (next) {link.stop(next)});
});
cbl.start();
}
module.exports.iprouter=iprouter;
module.exports.Command=Command
module.exports.Status=Status
module.exports.url2addr=url2addr;
module.exports.addr2url=addr2url;
};
BundleModuleCode['os/lz-string']=function (module,exports){
// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
// This work is free. You can redistribute it and/or modify it
// under the terms of the WTFPL, Version 2
// For more information see LICENSE.txt or http://www.wtfpl.net/
//
// For more information, the home page:
// http://pieroxy.net/blog/pages/lz-string/testing.html
//
// LZ-based compression algorithm, version 1.4.4
var LZString = (function() {
// private property
var f = String.fromCharCode;
var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$";
var baseReverseDic = {};
function getBaseValue(alphabet, character) {
if (!baseReverseDic[alphabet]) {
baseReverseDic[alphabet] = {};
for (var i=0 ; i<alphabet.length ; i++) {
baseReverseDic[alphabet][alphabet.charAt(i)] = i;
}
}
return baseReverseDic[alphabet][character];
}
var LZString = {
compressToBase64 : function (input) {
if (input == null) return "";
var res = LZString._compress(input, 6, function(a){return keyStrBase64.charAt(a);});
switch (res.length % 4) { // To produce valid Base64
default: // When could this happen ?
case 0 : return res;
case 1 : return res+"===";
case 2 : return res+"==";
case 3 : return res+"=";
}
},
decompressFromBase64 : function (input) {
if (input == null) return "";
if (input == "") return null;
return LZString._decompress(input.length, 32, function(index) { return getBaseValue(keyStrBase64, input.charAt(index)); });
},
compressToUTF16 : function (input) {
if (input == null) return "";
return LZString._compress(input, 15, function(a){return f(a+32);}) + " ";
},
decompressFromUTF16: function (compressed) {
if (compressed == null) return "";
if (compressed == "") return null;
return LZString._decompress(compressed.length, 16384, function(index) { return compressed.charCodeAt(index) - 32; });
},
//compress into uint8array (UCS-2 big endian format)
compressToUint8Array: function (uncompressed) {
var compressed = LZString.compress(uncompressed);
var buf=new Uint8Array(compressed.length*2); // 2 bytes per character
for (var i=0, TotalLen=compressed.length; i<TotalLen; i++) {
var current_value = compressed.charCodeAt(i);
buf[i*2] = current_value >>> 8;
buf[i*2+1] = current_value % 256;
}
return buf;
},
//decompress from uint8array (UCS-2 big endian format)
decompressFromUint8Array:function (compressed) {
if (compressed===null || compressed===undefined){
return LZString.decompress(compressed);
} else {
var buf=new Array(compressed.length/2); // 2 bytes per character
for (var i=0, TotalLen=buf.length; i<TotalLen; i++) {
buf[i]=compressed[i*2]*256+compressed[i*2+1];
}
var result = [];
buf.forEach(function (c) {
result.push(f(c));
});
return LZString.decompress(result.join(''));
}
},
//compress into a string that is already URI encoded
compressToEncodedURIComponent: function (input) {
if (input == null) return "";
return LZString._compress(input, 6, function(a){return keyStrUriSafe.charAt(a);});
},
//decompress from an output of compressToEncodedURIComponent
decompressFromEncodedURIComponent:function (input) {
if (input == null) return "";
if (input == "") return null;
input = input.replace(/ /g, "+");
return LZString._decompress(input.length, 32, function(index) { return getBaseValue(keyStrUriSafe, input.charAt(index)); });
},
compress: function (uncompressed) {
return LZString._compress(uncompressed, 16, function(a){return f(a);});
},
_compress: function (uncompressed, bitsPerChar, getCharFromInt) {
if (uncompressed == null) return "";
var i, value,
context_dictionary= {},
context_dictionaryToCreate= {},
context_c="",
context_wc="",
context_w="",
context_enlargeIn= 2, // Compensate for the first entry which should not count
context_dictSize= 3,
context_numBits= 2,
context_data=[],
context_data_val=0,
context_data_position=0,
ii;
for (ii = 0; ii < uncompressed.length; ii += 1) {
context_c = uncompressed.charAt(ii);
if (!Object.prototype.hasOwnProperty.call(context_dictionary,context_c)) {
context_dictionary[context_c] = context_dictSize++;
context_dictionaryToCreate[context_c] = true;
}
context_wc = context_w + context_c;
if (Object.prototype.hasOwnProperty.call(context_dictionary,context_wc)) {
context_w = context_wc;
} else {
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {
if (context_w.charCodeAt(0)<256) {
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i=0 ; i<8 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
} else {
value = 1;
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | value;
if (context_data_position ==bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i=0 ; i<16 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
delete context_dictionaryToCreate[context_w];
} else {
value = context_dictionary[context_w];
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
// Add wc to the dictionary.
context_dictionary[context_wc] = context_dictSize++;
context_w = String(context_c);
}
}
// Output the code for w.
if (context_w !== "") {
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {
if (context_w.charCodeAt(0)<256) {
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i=0 ; i<8 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
} else {
value = 1;
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | value;
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i=0 ; i<16 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
delete context_dictionaryToCreate[context_w];
} else {
value = context_dictionary[context_w];
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
}
// Mark the end of the stream
value = 2;
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
// Flush the last char
while (true) {
context_data_val = (context_data_val << 1);
if (context_data_position == bitsPerChar-1) {
context_data.push(getCharFromInt(context_data_val));
break;
}
else context_data_position++;
}
return context_data.join('');
},
decompress: function (compressed) {
if (compressed == null) return "";
if (compressed == "") return null;
return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); });
},
_decompress: function (length, resetValue, getNextValue) {
var dictionary = [],
next,
enlargeIn = 4,
dictSize = 4,
numBits = 3,
entry = "",
result = [],
i,
w,
bits, resb, maxpower, power,
c,
data = {val:getNextValue(0), position:resetValue, index:1};
for (i = 0; i < 3; i += 1) {
dictionary[i] = i;
}
bits = 0;
maxpower = Math.pow(2,2);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
switch (next = bits) {
case 0:
bits = 0;
maxpower = Math.pow(2,8);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
c = f(bits);
break;
case 1:
bits = 0;
maxpower = Math.pow(2,16);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
c = f(bits);
break;
case 2:
return "";
}
dictionary[3] = c;
w = c;
result.push(c);
while (true) {
if (data.index > length) {
return "";
}
bits = 0;
maxpower = Math.pow(2,numBits);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
switch (c = bits) {
case 0:
bits = 0;
maxpower = Math.pow(2,8);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
dictionary[dictSize++] = f(bits);
c = dictSize-1;
enlargeIn--;
break;
case 1:
bits = 0;
maxpower = Math.pow(2,16);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
dictionary[dictSize++] = f(bits);
c = dictSize-1;
enlargeIn--;
break;
case 2:
return result.join('');
}
if (enlargeIn == 0) {
enlargeIn = Math.pow(2, numBits);
numBits++;
}
if (dictionary[c]) {
entry = dictionary[c];
} else {
if (c === dictSize) {
entry = w + w.charAt(0);
} else {
return null;
}
}
result.push(entry);
// Add w+entry[0] to the dictionary.
dictionary[dictSize++] = w + entry.charAt(0);
enlargeIn--;
w = entry;
if (enlargeIn == 0) {
enlargeIn = Math.pow(2, numBits);
numBits++;
}
}
}
};
return LZString;
})();
if (typeof define === 'function' && define.amd) {
define(function () { return LZString; });
} else if( typeof module !== 'undefined' && module != null ) {
module.exports = LZString
}
};
BundleModuleCode['dos/buf']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2018 bLAB
** $CREATED: 29-4-15 by sbosse.
** $RCS: $Id$
** $VERSION: 1.1.5
**
** $INFO:
**
** DOS: Buffer Management
**
** $ENDOFINFO
*/
"use strict";
var log = 0;
var util = Require('util');
var Io = Require('com/io');
var Comp = Require('com/compat');
var String = Comp.string;
var Array = Comp.array;
var Perv = Comp.pervasives;
var des48 = Require('dos/des48');
var Rand = Comp.random;
var Net = Require('dos/network');
var Status = Net.Status;
var Fs = Require('fs');
var SIZEOF_INT16 = 2;
var SIZEOF_INT32 = 4;
var PORT_SIZE = 6;
var PRIV_SIZE = PORT_SIZE+SIZEOF_INT32;
var CAP_SIZE = PORT_SIZE+PRIV_SIZE;
/** Generic buffer, union with rpcio object
** Argument: optional, hex-ascci string or number (size), passed to Buffer instantiation
*
*
* @param {number|string|Buffer} [data]
* @constructor
*/
var buffer = function (data) {
var size;
this.pos=0;
if (Comp.isNumber(data)) {
this.data=new Buffer(data);
} else if (Comp.isString(data)) {
this.data=new Buffer('');
buf_of_hex(this,data)
} else if (Comp.isArray(data)) {
this.data=new Buffer(data);
} else if (typeof data == "object" && data.constructor === Buffer) {
this.data=data;
} else this.data=new Buffer('');
};
/** Extend a buffer to new size (buf.pos+off).
*
* @param buf
* @param off
*/
function buf_extend(buf,off) {
if (buf.data.length<(buf.pos+off)) {
buf.data=Buffer.concat([buf.data,new Buffer(off-(buf.data.length-buf.pos))]);
}
}
/** Expand buffer to new size.
*
* @param buf
* @param size
*/
function buf_expand(buf,size) {
if (buf.data.length<size) {
buf.data=Buffer.concat([buf.data,new Buffer(size-buf.data.length)]);
}
}
/** Shrink buffer to new size.
*
* @param buf
* @param size
*/
function buf_shrink(buf,size) {
if (buf.data.length>size) {
buf.data=Buffer.slice(buf.data,size);
}
}
/*
** BUFFER encoding and decoding of native data types
** Supported objects: rpcio, buffer.
** Supported native data types: int16, int32, string, float, port, private, capability, ...
** ALL buffer data is stored in byte buffers that extends automatically (buf_put_XX).
*/
function buf_put_string (buf,str) {
buf_extend(buf,str.length+1);
for(var i=0;i<str.length;i++) {
buf.data[buf.pos]=Perv.int_of_char(String.get(str,i));
buf.pos++;
}
buf.data[buf.pos]=0;
buf.pos++;
}
function buf_get_string (buf) {
var str='';
var end=buf.data.length;
var finished=false;
while (!finished && buf.pos < end) {
if (buf.data[buf.pos]==0) finished=true; else {
str = str + Perv.char_of_int(buf.data[buf.pos]);
buf.pos++;
}
}
buf.pos++;
return str;
}
/*
** Convert byte buffer to ASCII two-digit hexadecimal text representation and vice versa
*/
function buf_to_hex (buf) {
/*
var str='';
var len=buf.data.length;
for(var i=0;i<len;i++) {
str=str+String.format_hex(buf.data[i],2);
}
return str;
*/
return buf.data.toString('hex');
}
function buf_of_hex (buf,str) {
/*
var len=str.length/2;
var pos=0;
buf.pos=0;
buf_extend(buf,len);
for(var i=0;i<len;i++) {
buf.data[i]=String.parse_hex(str, pos, 2);
pos=pos+2;
}
*/
buf.pos=0;
buf.data= new Buffer(str,'hex');
}
/*
** Convert byte buffer to strings and vice versa
*/
function buf_to_str (buf) {
var str=buf.data.toString('binary');
return str;
}
function buf_of_str (buf,str) {
buf.pos=0;
buf.data=new Buffer(str,'binary');
return buf;
}
/** Put a string to a buffer w/o EOS
*
* @param buf
* @param {string} str
*/
function buf_put_bytes (buf,str) {
buf_extend(buf,str.length);
for(var i=0;i<str.length;i++) {
var n=Perv.int_of_char(String.get(str,i));
buf.data[buf.pos]=n;
buf.pos++;
}
// No final EOS marker!
}
/** Get number of bytes from buffer and store in string (w/o EOS)
*
* @param buf
* @param size
* @returns {string}
*/
function buf_get_bytes (buf,size) {
var i=0;
var str='';
var end=buf.data.length;
var finished=false;
while (!finished && buf.pos < end) {
if (i==size) finished=true; else {
str = str + Perv.char_of_int(buf.data[buf.pos]);
buf.pos++;i++;
}
}
return str;
}
function buf_put_int16 (buf,n) {
buf_extend(buf,2);
buf.data[buf.pos]=n & 0xff;
buf.data[buf.pos+1]=(n >> 8) & 0xff;
buf.pos=buf.pos+2;
}
function buf_get_int16 (buf) {
var n=0;
var end=buf.data.length;
if (buf.pos+2 <= end) {
n = buf.data[buf.pos];
n = n | (buf.data[buf.pos+1] << 8);
buf.pos = buf.pos + 2;
if (n&0x8000) return (n-0x10000); else return (n);
} else throw Status.BUF_OVERFLOW;
}
function buf_put_int32 (buf,n) {
buf_extend(buf,4);
buf.data[buf.pos]=n & 0xff;
buf.data[buf.pos+1]=(n >> 8) & 0xff;
buf.data[buf.pos+2]=(n >> 16) & 0xff;
buf.data[buf.pos+3]=(n >> 24) & 0xff;
buf.pos=buf.pos+4;
}
function buf_get_int32 (buf) {
var n=0;
var end=buf.data.length;
if (buf.pos+4 <= end) {
n = buf.data[buf.pos];
n = n | (buf.data[buf.pos+1] << 8);
n = n | (buf.data[buf.pos+2] << 16);
n = n | (buf.data[buf.pos+3] << 24);
buf.pos = buf.pos + 4;
// TBD: Sign check???
return (n);
} else throw Status.BUF_OVERFLOW;
}
function buf_put_port (buf,port) {
buf_extend(buf,Net.PORT_SIZE);
for(var i=0;i<Net.PORT_SIZE;i++) {
var n=Perv.int_of_char(String.get(port,i));
buf.data[buf.pos]=n;
buf.pos++;
}
}
function buf_get_port (buf) {
var port='';
var end=buf.data.length;
if (buf.pos+Net.PORT_SIZE <= end) {
for (var i = 0; i < Net.PORT_SIZE; i++) {
port = port + Perv.char_of_int(buf.data[buf.pos]);
buf.pos++;
}
return port;
} else throw Status.BUF_OVERFLOW;
}
function buf_put_priv (buf,priv) {
buf_extend(buf,Net.PRIV_SIZE);
buf.data[buf.pos]=priv.prv_obj & 0xff;
buf.data[buf.pos+1]=(priv.prv_obj >> 8) & 0xff;
buf.data[buf.pos+2]=(priv.prv_obj >> 16) & 0xff;
buf.data[buf.pos+3]=priv.prv_rights & 0xff;
buf.pos=buf.pos+4;
buf_put_port(buf,priv.prv_rand);
}
function buf_get_priv (buf,priv) {
var n;
var end=buf.data.length;
if (buf.pos+(Net.PRIV_SIZE) <= end) {
if (priv == undefined) priv = Net.Private();
n = buf.data[buf.pos];
n = n | (buf.data[buf.pos+1] << 8);
n = n | (buf.data[buf.pos+2] << 16);
priv.prv_obj=n;
priv.prv_rights=buf.data[buf.pos+3];
buf.pos=buf.pos+4;
priv.prv_rand=buf_get_port(buf);
return priv;
} else throw Status.BUF_OVERFLOW;
}
function buf_put_cap (buf,cap) {
buf_put_port(buf,cap.cap_port);
buf_put_priv(buf,cap.cap_priv);
}
function buf_get_cap (buf,cap) {
var end=buf.data.length;
if (buf.pos+(Net.CAP_SIZE) <= end) {
if (cap == undefined) cap = Net.Capability();
cap.cap_port=buf_get_port(buf);
buf_get_priv(buf,cap.cap_priv);
return cap;
} else throw Status.BUF_OVERFLOW;
}
function buf_put_hdr (buf,hdr) {
buf_put_port(buf,hdr.h_port);
buf_put_priv(buf,hdr.h_priv);
buf_put_int32(buf,hdr.h_command);
buf_put_int32(buf,hdr.h_status);
}
function buf_get_hdr (buf,hdr) {
if (hdr==undefined) hdr=Net.Header();
hdr.h_port=buf_get_port(buf);
buf_get_priv(buf,hdr.h_priv);
hdr.h_command=buf_get_int32(buf);
hdr.h_status=buf_get_int32(buf);
return hdr;
}
/** TODO: buf blit
*
* @param buf
* @param bufsrc
* @param [srcoff]
* @param [len]
*/
function buf_put_buf (buf,bufsrc,srcoff,len) {
if (srcoff==undefined) srcoff=0;
if (len==undefined) len=bufsrc.data.length;
buf_extend(buf,len);
for(var i=0;i<len;i++) {
buf.data[buf.pos]=bufsrc.data[srcoff+i];
buf.pos++;
}
}
/** TODO: buf blit
*
* @param buf
* @param bufdst
* @param dstoff
* @param len
*/
function buf_get_buf (buf,bufdst,dstoff,len) {
buf_extend(bufdst,dstoff+len);
for(var i=0;i<len;i++) {
bufdst.data[dstoff+i]=buf.data[buf.pos];
buf.pos++;
}
}
function buf_pad (buf,size,byte) {
if (buf.data.length < size) buf_extend(buf,size-buf.data.length);
if (byte!=undefined) {
while (buf.pos < size) {
buf.data[buf.pos] = byte;
buf.pos++;
}
} else buf.pos=size-1;
}
function buf_set_pos (buf,off) {
if (off >= buf.data.length) buf_expand(buf,off+1);
buf.pos=off;
}
/**
* @param {file} fd
* @param {buffer} buf
* @param {number} [off] file offset
* @param {number} [len]
* @returns {number} n
*/
function buf_write (fd,buf,off,len) {
var n;
if (off==undefined) n=Io.write_buf(fd,buf.data,0,buf.data.length);
else {
if (len==undefined) len=buf.data.length;
n=Io.write_buf(fd,buf.data,0,len,off);
}
return n;
}
/**
* @param {file} fd
* @param {buffer} buf
* @param {number} off file offset
* @param {number} len
* @returns {number} n
*/
function buf_read (fd,buf,off,len) {
var n;
buf_expand(buf,len);
n=Io.read_buf(fd,buf.data,0,len,off);
buf.pos=0;
return n;
}
function buf_print(buf) {
var str='[';
for(var i=0;i<buf.data.length;i++) {
if(i>0) str=str+','+buf.data[i];
else str=str+buf.data[i];
}
return str+']'+buf.pos+':'+buf.data.length;
}
function buf_set (buf,off,byte) {
if (off >= buf.data.length) buf_expand(buf,off+1);
buf.data[off]=byte;
}
function buf_get (buf,off) {
return buf.data[off];
}
/** Reset buffer
*
* @param buf
*/
function buf_init (buf) {
buf.data=new Buffer('');
buf.pos=0;
}
function buf_copy (dst,src) {
dst.data=new Buffer(src.data);
dst.pos=0;
}
function buf_blit (dst,dstoff,src,srcoff,len) {
buf_expand(dst,dstoff+len);
src.data.copy(dst.data,dstoff,srcoff,srcoff+len);
dst.pos=0;
}
/**
*
* @type {{SIZEOF_INT16: number, SIZEOF_INT32: number, PORT_SIZE: number, PRIV_SIZE: number, CAP_SIZE: number, Buffer: Function, buf_put_string: buf_put_string, buf_put_int16: buf_put_int16, buf_put_int32: buf_put_int32, buf_put_port: buf_put_port, buf_put_priv: buf_put_priv, buf_put_cap: buf_put_cap, buf_put_hdr: buf_put_hdr, buf_put_buf: buf_put_buf, buf_put_bytes: buf_put_bytes, buf_get_string: buf_get_string, buf_get_int16: buf_get_int16, buf_get_int32: buf_get_int32, buf_get_port: buf_get_port, buf_get_priv: buf_get_priv, buf_get_cap: buf_get_cap, buf_get_hdr: buf_get_hdr, buf_get_buf: buf_get_buf, buf_get_bytes: buf_get_bytes, buf_pad: buf_pad, buf_set: buf_set, buf_get: buf_get, buf_set_pos: buf_set_pos, buf_init: buf_init, buf_blit: buf_blit, buf_copy: buf_copy, buf_extend: buf_extend, buf_expand: buf_expand, buf_shrink: buf_shrink, buf_read: buf_read, buf_write: buf_write, buf_print: buf_print, buf_to_hex: buf_to_hex, buf_of_hex: buf_of_hex, buf_to_str: buf_to_str, buf_of_str: buf_of_str}}
*/
module.exports = {
SIZEOF_INT16: SIZEOF_INT16,
SIZEOF_INT32: SIZEOF_INT32,
PORT_SIZE: PORT_SIZE,
PRIV_SIZE: PRIV_SIZE,
CAP_SIZE: CAP_SIZE,
/**
*
* @param {number|string|Buffer} [data]
* @returns {buffer}
*/
Buffer: function Buffer(data) {
var obj = new buffer(data);
Object.preventExtensions(obj);
return obj;
},
// Buffer data operations
buf_put_string:buf_put_string,
buf_put_int16:buf_put_int16,
buf_put_int32:buf_put_int32,
buf_put_port:buf_put_port,
buf_put_priv:buf_put_priv,
buf_put_cap:buf_put_cap,
buf_put_hdr:buf_put_hdr,
buf_put_buf:buf_put_buf,
buf_put_bytes:buf_put_bytes,
buf_get_string:buf_get_string,
buf_get_int16:buf_get_int16,
buf_get_int32:buf_get_int32,
buf_get_port:buf_get_port,
buf_get_priv:buf_get_priv,
buf_get_cap:buf_get_cap,
buf_get_hdr:buf_get_hdr,
buf_get_buf:buf_get_buf,
buf_get_bytes:buf_get_bytes,
buf_pad:buf_pad,
buf_set:buf_set,
buf_get:buf_get,
buf_set_pos:buf_set_pos,
buf_init:buf_init,
buf_blit:buf_blit,
buf_copy:buf_copy,
buf_extend:buf_extend,
buf_expand:buf_expand,
buf_shrink:buf_shrink,
// Buffer IO
buf_read:buf_read,
buf_write:buf_write,
buf_print:buf_print,
// Conversion
buf_to_hex:buf_to_hex,
buf_of_hex:buf_of_hex,
buf_to_str:buf_to_str,
buf_of_str:buf_of_str,
length: function(buf) {
if (buf.data==undefined) return 0;
else return buf.data.length;
}
};
};
BundleModuleCode['dos/des48']=function (module,exports){
/**
** ==================================
** OOOO OOOO OOOO O O OOOO
** O O O O O O O O O
** O O O O O O O O O
** OOOO OOOO OOOO O OOO OOOO
** O O O O O O O O O
** O O O O O O O O O
** OOOO OOOO OOOO OOOO O O OOOO
** ==================================
** BSSLAB, Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR.
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 BSSLAB
** $CREATED: 3/30/15 by sbosse.
** $VERSION: 1.1.5
**
** $INFO:
**
** DOS: Encryption 48bit
**
** $ENDOFINFO
*/
var util = Require('util');
var Io = Require('com/io');
var Comp = Require('com/compat');
var Array = Comp.array;
var assert = Comp.assert;
const des_HBS = 24;
const des_BS = des_HBS * 2;
/*
** Initial permutation,
*/
var des_IP = [
23, 27, 34, 44, 37, 17, 12, 42,
3, 32, 41, 29, 20, 2, 1, 10,
0, 28, 40, 6, 7, 11, 16, 8,
25, 30, 14, 26, 47, 38, 19, 43,
18, 5, 35, 39, 36, 21, 4, 45,
24, 22, 13, 33, 31, 9, 15, 46 ];
/*
** Final permutation, FP = IP^(-1)
*/
var des_FP = [
16, 14, 13, 8, 38, 33, 19, 20,
23, 45, 15, 21, 6, 42, 26, 46,
22, 5, 32, 30, 12, 37, 41, 0,
40, 24, 27, 1, 17, 11, 25, 44,
9, 43, 2, 34, 36, 4, 29, 35,
18, 10, 7, 31, 3, 39, 47, 28 ];
/*
** Permuted-choice 1 from the key bits
** to yield C and D.
** Note that bits 8,16... are left out:
** They are intended for a parity check.
*/
var des_PC1_C = [
57,49,41,33,25,17, 9,
1,58,50,42,34,26,18,
10, 2,59,51,43,35,27,
19,11, 3,60,52,44,36 ];
var des_PC1_D = [
63,55,47,39,31,23,15,
7,62,54,46,38,30,22,
14, 6,61,53,45,37,29,
21,13, 5,28,20,12, 4 ];
/*
** Sequence of shifts used for the key schedule.
*/
var des_shifts = [
1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1 ];
/*
** Permuted-choice 2, to pick out the bits from
** the CD array that generate the key schedule.
*/
var des_PC2_C = [
14,17,11,24, 1, 5,
3,28,15, 6,21,10,
23,19,12, 4,26, 8,
16, 7,27,20,13, 2 ];
var des_PC2_D = [
41,52,31,37,47,55,
30,40,51,45,33,48,
44,49,39,56,34,53,
46,42,50,36,29,32 ];
/*
** The C and D arrays used to calculate the key schedule.
*/
var des_C = Array.create(56,0);
// des_D = des_C[28]
var des_D_get = function (i) {return des_C[i+28]};
var des_D_set = function (i,sval) { des_C[i+28] = sval };
/*
** The key schedule.
** Generated from the key.
*/
var des_KS= Array.create_matrix(16,48,0);
var des_OWsetkey = function(key) {
var ks = [];
var t = 0;
var i,j,k;
/*
** First, generate C and D by permuting
** the key. The low order bit of each
** 8-bit char is not used, so C and D are only 28
** bits apiece.
*/
for(i = 0;i < 28;i++) {
var index1 = des_PC1_C[i] - 1;
var index2 = des_PC1_D[i] - 1;
des_C[i] = key[index1];
des_D_set(i,key[index2]);
}
/*
** To generate Ki, rotate C and D according
** to schedule and pick up a permutation
** using PC2.
*/
for (i = 0 ;i< 16;i++) {
ks = des_KS[i];
// rotate
for (k = 0; k < des_shifts[i]; k++) {
t = des_C[0];
for (j = 0; j < 27; j++) {
des_C[j] = des_C[j + 1];
}
des_C[27] = t;
t = des_D_get(0);
for (j = 0; j < 27; j++) {
des_D_set(j, des_D_get(j + 1));
}
des_D_set(27, t);
}
/*
** get Ki. Note C and D are concatenated.
*/
for (j = 0; j < 24; j++) {
ks[j] = des_C[des_PC2_C[j] - 1];
ks[j + 24] = des_D_get(des_PC2_D[j] - 28 - 1);
}
}
return { C:des_C, KS:des_KS }
};
/*
** The E bit-selection table.
*/
var des_E = [
22, 15, 12, 3, 8, 2, 23, 16,
14, 13, 9, 10, 0, 1, 21, 19,
18, 6, 11, 7, 17, 4, 20, 5,
5, 17, 11, 13, 12, 14, 8, 7,
19, 22, 18, 9, 3, 4, 1, 6,
16, 2, 20, 15, 10, 23, 0, 21 ];
/*
** The 8 selection functions.
** For some reason, they give a 0-origin
** index, unlike everything else.
*/
var des_S = [
[ 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7,
0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8,
4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0,
15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13 ],
[ 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10,
3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5,
0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15,
13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9 ],
[ 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1,
13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7,
1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12 ],
[ 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15,
13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9,
10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4,
3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14 ],
[ 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9,
14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6,
4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14,
11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3 ],
[ 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11,
10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8,
9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6,
4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13 ],
[ 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1,
13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6,
1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2,
6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12 ],
[ 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7,
1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2,
7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8,
2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11 ]
];
/*
** P is a permutation on the selected combination
** of the current L and key.
*/
var des_P = [
3, 13, 9, 12, 8, 20, 21, 7,
5, 23, 16, 1, 14, 18, 4, 15,
22, 10, 2, 0, 11, 19, 17, 6 ];
var des_L = Array.create(des_BS,0);
var des_R_get = function (i) { return des_L[(i+des_HBS)]};
var des_R_set = function (i,sval) { des_L[i+des_HBS]= sval};
var des_tempL = Array.create(des_HBS,0);
var des_f = Array.create (32,0);
/*
** Warning!!
**
** f[] used to be HBS for some years.
** 21/6/1990 cbo and sater discovered that inside the loop where f is computed
** indices are used from 0 to 31. These overlapped the preS array which is
** declared hereafter on all compilers upto that point, but only those
** values that were not used anymore. But the values of f are only used
** upto HBS. Makes you wonder about the one-way property.
** Then came ACK, and reversed the order of the arrays in the image.
**
** As a short term solution f[] was increased to 32, but in the long run
** someone should have a good look at our "oneway" function
*/
/*
** The combination of the key and the input, before selection.
*/
var des_preS = Array.create (48,0);
/*
** The payoff: encrypt a block. (Now 48 bytes, 1 bit/byte)
*/
var des_OWcrypt48 = function(block) {
var ks = [];
var t1 = 0;
var t2 = 0;
var i, j, k;
/*
** First, permute the bits in the input
*/
for (j = 0; j <= (des_BS - 1); j++) {
des_L[j] = block[des_IP[j]];
}
/*
** Perform an encryption operation 16 times.
*/
for (i = 0; i <= 15; i++) {
ks = des_KS[i];
/*
** Save the R array,
** which will be the new L.
*/
for (j = 0; j < (des_HBS - 1); j++) {
des_tempL[j] = des_R_get(j);
}
/*
** Expand R to 48 bits using the E selector;
** exclusive-or with the current key bits.
*/
for (j = 0; j <= 47; j++) {
des_preS[j] = (des_R_get(des_E[j])) ^ ks[j];
}
/*
** The pre-select bits are now considered
** in 8 groups of 6 bits each.
** The 8 selection functions map these
** 6-bit quantities into 4-bit quantities
** and the results permuted
** to make an f(R, K).
** The indexing into the selection functions
** is peculiar; it could be simplified by
** rewriting the tables.
*/
t1 = 0;
t2 = 0;
for (j = 0; j <= 7; j++) {
/*
C: for (j=0,t1=0,t2=0; j<8; j++,t1+=6,t2+=4) {
k = S[j][(preS[t1+0]<<5)+
(preS[t1+1]<<3)+
(preS[t1+2]<<2)+
(preS[t1+3]<<1)+
(preS[t1+4]<<0)+
(preS[t1+5]<<4)];
f[t2+0] = (k>>3)&01;
f[t2+1] = (k>>2)&01;
f[t2+2] = (k>>1)&01;
f[t2+3] = (k>>0)&01;
}
*/
var sind2 =
((des_preS[t1 + 0] << 5) & 0xff) +
((des_preS[t1 + 1] << 3) & 0xff) +
((des_preS[t1 + 2] << 2) & 0xff) +
((des_preS[t1 + 3] << 1) & 0xff) +
((des_preS[t1 + 4] << 0) & 0xff) +
((des_preS[t1 + 5] << 4) & 0xff);
k = des_S[j][sind2];
des_f[t2 + 0] = (k >> 3) & 0x1;
des_f[t2 + 1] = (k >> 2) & 0x1;
des_f[t2 + 2] = (k >> 1) & 0x1;
des_f[t2 + 3] = (k >> 0) & 0x1; // 3 .. 31 !!!
t1 = t1 + 6;
t2 = t2 + 4;
}
/*
** The new R is L ^ f(R, K).
** The f here has to be permuted first, though.
*/
for (j = 0; j < des_HBS; j++) {
des_R_set(j, (des_L[j] ^ des_f[des_P[j]]));
}
/*
** Finally, the new L (the original R)
** is copied back.
*/
for (j = 0; j < des_HBS; j++) {
des_L[j] = des_tempL[j];
}
}
/*
** The output L and R are reversed.
*/
for (j = 0; j < des_HBS; j++) {
t1 = des_L[j];
des_L[j] = des_R_get(j);
des_R_set(j, t1);
}
/*
** The final output
** gets the inverse permutation of the very original.
*/
for (j = 0; j < des_BS; j++) {
block[j] = des_L[des_FP[j]];
}
return block;
};
module.exports = {
des_OWsetkey:des_OWsetkey,
des_OWcrypt48:des_OWcrypt48
};
};
BundleModuleCode['dos/network']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR.
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2018 bLAB
** $CREATED: 3-5-15 by sbosse.
** $VERSION: 1.2.6
**
** $INFO:
**
** DOS: Networking, Commands, Status/Error codes, ..
**
** $ENDOFINFO
*/
"use strict";
var log = 0;
var util = Require('util');
var Io = Require('com/io');
var Comp = Require('com/compat');
var String = Comp.string;
var Array = Comp.array;
var Perv =Comp.pervasives;
var Des48 = Require('dos/des48');
var Base64 = Require('os/base64');
var Rand = Comp.random;
var Fs = Require('fs');
//var xmldoc = Require('xmldoc');
function pad(str,size) {
while (str.length < (size || 2)) {str = "0" + str;}
return str;
}
/** Direction
*
var Direction = {
NORTH:1,
WEST:2,
EAST:3,
SOUTH:4,
ORIGIN:5,
tostring:function (i) {
switch (i) {
case 1: return 'NORTH';
case 2: return 'WEST';
case 3: return 'EAST';
case 4: return 'SOUTH';
case 5: return 'ORIGIN';
default: return 'Direction?';
}
}
};
*/
// Standard Object Service
var STD_FIRST_COM = 1000;
var STD_LAST_COM = 1999;
var STD_FIRST_ERR = (-STD_FIRST_COM);
var STD_LAST_ERR = (-STD_LAST_COM);
// File Server
var AFS_FIRST_COM = 2000;
var AFS_LAST_COM = 2099;
var AFS_FIRST_ERR = (-AFS_FIRST_COM);
var AFS_LAST_ERR = (-AFS_LAST_COM);
var AFS_REQBUFSZ = 1024*32;
// Directory and Name Server
var DNS_FIRST_COM = 2100;
var DNS_LAST_COM = 2199;
var DNS_FIRST_ERR = (-DNS_FIRST_COM);
var DNS_LAST_ERR = (-DNS_LAST_COM);
var DNS_MAXCOLUMNS = 4;
// System Process Server (incl. Agent Platform Manager)
var PS_FIRST_COM = 2200;
var PS_LAST_COM = 2299;
var PS_FIRST_ERR = (-PS_FIRST_COM);
var PS_LAST_ERR = (-PS_LAST_COM);
// Broker Server
var BR_FIRST_COM = 2300;
var BR_LAST_COM = 2399;
var BR_FIRST_ERR = (-BR_FIRST_COM);
var BR_LAST_ERR = (-BR_LAST_COM);
/** RPC Status
*
* @enum {number}
*/
var Status = {
STD_OK:0,
STD_CAPBAD : STD_FIRST_ERR,
STD_COMBAD : (STD_FIRST_ERR-1),
STD_ARGBAD : (STD_FIRST_ERR-2),
STD_NOTNOW : (STD_FIRST_ERR-3),
STD_NOSPACE : (STD_FIRST_ERR-4),
STD_DENIED : (STD_FIRST_ERR-5),
STD_NOMEM : (STD_FIRST_ERR-6),
STD_EXISTS : (STD_FIRST_ERR-7),
STD_NOTFOUND : (STD_FIRST_ERR-8),
STD_SYSERR : (STD_FIRST_ERR-9),
STD_INTR : (STD_FIRST_ERR-10),
STD_OVERFLOW : (STD_FIRST_ERR-11),
STD_WRITEPROT : (STD_FIRST_ERR-12),
STD_NOMEDIUM : (STD_FIRST_ERR-13),
STD_IOERR : (STD_FIRST_ERR-14),
STD_WRONGSRV : (STD_FIRST_ERR-15),
STD_OBJBAD : (STD_FIRST_ERR-16),
STD_UNKNOWN : (STD_FIRST_ERR-17),
DNS_UNAVAIL : (DNS_FIRST_ERR -1),
DNS_NOTEMPTY : (DNS_FIRST_ERR -2),
DNS_UNREACH : (DNS_FIRST_ERR -3),
DNS_CLASH : (DNS_FIRST_ERR -4),
RPC_FAILURE : -1,
BUF_OVERFLOW : -2,
print: function(stat) {
switch(stat) {
case Status.STD_OK : return 'STD_OK';
case Status.STD_CAPBAD : return 'STD_CAPBAD';
case Status.STD_COMBAD : return 'STD_COMBAD';
case Status.STD_ARGBAD : return 'STD_ARGBAD';
case Status.STD_NOTNOW : return 'STD_NOTNOW';
case Status.STD_NOSPACE : return 'STD_NOSPACE';
case Status.STD_DENIED : return 'STD_DENIED';
case Status.STD_NOMEM : return 'STD_NOMEM';
case Status.STD_EXISTS : return 'STD_EXISTS';
case Status.STD_NOTFOUND : return 'STD_NOTFOUND';
case Status.STD_SYSERR : return 'STD_SYSERR';
case Status.STD_INTR : return 'STD_INTR';
case Status.STD_OVERFLOW : return 'STD_OVERFLOW';
case Status.STD_WRITEPROT : return 'STD_WRITEPROT';
case Status.STD_NOMEDIUM : return 'STD_NOMEDIUM';
case Status.STD_IOERR : return 'STD_IOERR';
case Status.STD_WRONGSRV : return 'STD_WRONGSRV';
case Status.STD_OBJBAD : return 'STD_OBJBAD';
case Status.STD_UNKNOWN : return 'STD_UNKNOWN';
case Status.DNS_UNAVAIL : return 'DNS_UNAVAIL';
case Status.DNS_NOTEMPTY : return 'DNS_NOTEMPTY';
case Status.DNS_UNREACH : return 'DNS_UNREACH';
case Status.DNS_CLASH : return 'DNS_CLASH';
case Status.RPC_FAILURE : return 'RPC_FAILURE';
case Status.BUF_OVERFLOW : return 'BUF_OVERFLOW';
default : return '"'+stat+'"';
}
}
};
/** RPC Command
*
* @enum {number}
*/
var Command = {
/*
** Standard Commands
*/
STD_MONITOR : STD_FIRST_COM,
STD_AGE : (STD_FIRST_COM+1),
STD_COPY : (STD_FIRST_COM + 2),
STD_DESTROY : (STD_FIRST_COM + 3),
STD_INFO : (STD_FIRST_COM + 4),
STD_RESTRICT : (STD_FIRST_COM + 5),
STD_STATUS : (STD_FIRST_COM + 6),
STD_TOUCH : (STD_FIRST_COM + 7),
STD_GETPARAMS : (STD_FIRST_COM + 8),
STD_SETPARAMS : (STD_FIRST_COM + 9),
STD_NTOUCH : (STD_FIRST_COM + 10),
STD_EXIT : (STD_FIRST_COM + 11),
STD_RIGHTS : (STD_FIRST_COM + 12),
STD_EXEC : (STD_FIRST_COM + 13),
STD_LOCATION : (STD_FIRST_COM + 20),
STD_LABEL : (STD_FIRST_COM + 21),
/*
** AFC Commands
*/
AFS_CREATE : (AFS_FIRST_COM + 1),
AFS_DELETE : (AFS_FIRST_COM + 2),
AFS_FSCK : (AFS_FIRST_COM + 3),
AFS_INSERT : (AFS_FIRST_COM + 4),
AFS_MODIFY : (AFS_FIRST_COM + 5),
AFS_READ : (AFS_FIRST_COM + 6),
AFS_SIZE : (AFS_FIRST_COM + 7),
AFS_DISK_COMPACT : (AFS_FIRST_COM + 8),
AFS_SYNC : (AFS_FIRST_COM + 9),
AFS_DESTROY : (AFS_FIRST_COM + 10),
/*
** DNS Commands
*/
DNS_CREATE : (DNS_FIRST_COM),
DNS_DISCARD : (DNS_FIRST_COM + 1),
DNS_LIST : (DNS_FIRST_COM + 2),
DNS_APPEND : (DNS_FIRST_COM + 3),
DNS_CHMOD : (DNS_FIRST_COM + 4),
DNS_DELETE : (DNS_FIRST_COM + 5),
DNS_LOOKUP : (DNS_FIRST_COM + 6),
DNS_SETLOOKUP : (DNS_FIRST_COM + 7),
DNS_INSTALL : (DNS_FIRST_COM + 8),
DNS_REPLACE : (DNS_FIRST_COM + 10),
DNS_GETMASKS : (DNS_FIRST_COM + 11),
DNS_GETSEQNR : (DNS_FIRST_COM + 12),
DNS_RENAME : (DNS_FIRST_COM + 13),
DNS_GETROOT : (DNS_FIRST_COM + 14),
DNS_GETDEFAFS : (DNS_FIRST_COM + 15),
PS_STUN : (PS_FIRST_COM), // Kill a process/ create a snapshot
PS_MIGRATE : (PS_FIRST_COM+1), // Execute a process from a snapshot after migration (->next+)
PS_EXEC : (PS_FIRST_COM+2), // Execute a process from a snapshot (->next)
PS_WRITE : (PS_FIRST_COM+4), // Store a process class template
PS_READ : (PS_FIRST_COM+5), // Get a process class template
PS_CREATE : (PS_FIRST_COM+6), // Create a process from a template and execute
PS_FORK : (PS_FIRST_COM+7), // Fork a process from a running process
PS_SIGNAL : (PS_FIRST_COM+8), // Send a signal to a process
BR_CONNECT : (BR_FIRST_COM),
BR_DISCONN : (BR_FIRST_COM+1),
print: function(cmd) {
switch(cmd) {
case Command.STD_MONITOR : return 'STD_MONITOR';
case Command.STD_AGE : return 'STD_AGE';
case Command.STD_COPY : return 'STD_COPY';
case Command.STD_DESTROY : return 'STD_DESTROY';
case Command.STD_INFO : return 'STD_INFO';
case Command.STD_RESTRICT : return 'STD_RESTRICT';
case Command.STD_STATUS : return 'STD_STATUS';
case Command.STD_TOUCH : return 'STD_TOUCH';
case Command.STD_GETPARAMS : return 'STD_GETPARAMS';
case Command.STD_SETPARAMS : return 'STD_SETPARAMS';
case Command.STD_NTOUCH : return 'STD_NTOUCH';
case Command.STD_EXIT : return 'STD_EXIT';
case Command.STD_RIGHTS : return 'STD_RIGHTS';
case Command.STD_EXEC : return 'STD_EXEC';
case Command.STD_LOCATION : return 'STD_LOCATION';
case Command.STD_LABEL : return 'STD_LABEL';
case Command.AFS_CREATE : return 'AFS_CREATE';
case Command.AFS_DELETE : return 'AFS_DELETE';
case Command.AFS_FSCK : return 'AFS_FSCK';
case Command.AFS_INSERT : return 'AFS_INSERT';
case Command.AFS_MODIFY : return 'AFS_MODIFY';
case Command.AFS_READ : return 'AFS_READ';
case Command.AFS_SIZE : return 'AFS_SIZE';
case Command.AFS_DISK_COMPACT : return 'AFS_DISK_COMPACT';
case Command.AFS_SYNC : return 'AFS_SYNC';
case Command.AFS_DESTROY : return 'AFS_DESTROY';
case Command.DNS_CREATE : return 'DNS_CREATE';
case Command.DNS_DISCARD : return 'DNS_DISCARD';
case Command.DNS_LIST : return 'DNS_LIST';
case Command.DNS_APPEND : return 'DNS_APPEND';
case Command.DNS_CHMOD : return 'DNS_CHMOD';
case Command.DNS_DELETE : return 'DNS_DELETE';
case Command.DNS_LOOKUP : return 'DNS_LOOKUP';
case Command.DNS_SETLOOKUP : return 'DNS_SETLOOKUP';
case Command.DNS_INSTALL : return 'DNS_INSTALL';
case Command.DNS_REPLACE : return 'DNS_REPLACE';
case Command.DNS_GETMASKS : return 'DNS_GETMASKS';
case Command.DNS_GETSEQNR : return 'DNS_GETSEQNR';
case Command.DNS_RENAME : return 'DNS_RENAME';
case Command.DNS_GETROOT : return 'DNS_GETRROT';
case Command.DNS_GETDEFAFS : return 'DNS_GETDEFAFS';
case Command.PS_STUN : return 'PS_STUN';
case Command.PS_EXEC : return 'PS_EXEC';
case Command.PS_MIGRATE : return 'PS_MIGRATE';
case Command.PS_READ : return 'PS_READ';
case Command.PS_WRITE : return 'PS_WRITE';
case Command.PS_CREATE : return 'PS_CREATE';
case Command.PS_FORK : return 'PS_FORK';
case Command.PS_SIGNAL : return 'PS_SIGNAL';
case Command.BR_CONNECT : return 'BR_CONNECT';
case Command.BR_DISCONN : return 'BR_DISCONN';
default: return '"'+cmd+'"';
}
}
};
var PORT_SIZE = 6;
var PRIV_SIZE = 4+PORT_SIZE;
var CAP_SIZE = 16;
/** Object Rights
*
* @enum {number}
*/
var Rights = {
AFS_RGT_READ : 0x1,
AFS_RGT_CREATE : 0x2,
AFS_RGT_MODIFY : 0x4,
AFS_RGT_DESTROY : 0x8,
AFS_RGT_ADMIN : 0x80,
DNS_COLMASK : ((1 << DNS_MAXCOLUMNS) - 1), // Rights to access specific columns of a directory row, one bit, one column.
DNS_RGT_COLALL : ((1 << DNS_MAXCOLUMNS) - 1),
DNS_RGT_COL1 : 0x01,
DNS_RGT_OWNER : 0x01,
DNS_RGT_COL2 : 0x02,
DNS_RGT_GROUP : 0x02,
DNS_RGT_COL3 : 0x04,
DNS_RGT_OTHERS : 0x04,
DNS_RGT_COL4 : 0x08,
DNS_RGT_READ : 0x10,
DNS_RGT_CREATE : 0x20,
DNS_RGT_MODIFY : 0x40,
DNS_RGT_DELETE : 0x80,
HOST_INFO : 0x01,
HOST_READ : 0x02,
HOST_WRITE : 0x04,
HOST_EXEC : 0x08,
PSR_READ : 0x01,
PSR_WRITE : 0x02,
PSR_CREATE : 0x04,
PSR_DELETE : 0x08,
PSR_EXEC : 0x10,
PSR_KILL : 0x20,
PSR_ALL : 0xff,
NEG_SCHED : 0x08,
NEG_CPU : 0x10,
NEG_RES : 0x20,
NEG_LEVEL : 0x40,
PRV_ALL_RIGHTS : 0xff
};
var DEF_RPC_MAX_HOP = 4;
var priv2pub_cache = [];
/**
*
* @param {number []} [port_vals]
* @returns {string}
*/
var port = function (port_vals) {
if (port_vals==undefined) port_vals=[0,0,0,0,0,0];
var port='';
for(var i = 0; i< PORT_SIZE;i++) {
port=port+Perv.char_of_int(port_vals[i]);
}
return port;
};
/**
*
* @param {number} [obj]
* @param {number} [rights]
* @param {port} [rand]
* @constructor
*/
var privat = function (obj,rights,rand) {
if (obj==undefined) {
// Create empty private field
this.prv_obj=0;
this.prv_rights=0;
this.prv_rand=port();
} else {
this.prv_obj = obj; // Integer
this.prv_rights = rights; // Integer
this.prv_rand = rand; // Port=string
}
};
/**
*
* @param {port} [cap_port]
* @param {privat} [cap_priv]
* @constructor
*/
var capability = function(cap_port, cap_priv) {
if (cap_port==undefined) {
// Create empty capability
this.cap_port = port();
this.cap_priv = new privat();
} else {
this.cap_port = cap_port; // Port=string
if (cap_priv==undefined)
this.cap_priv = new privat();
else
this.cap_priv = cap_priv; // Private
}
};
/*
** RPC communication is XML based using the HTTP interface.
** RPC communication is synchronous, hence a callback
** function is used to handle the reply (acknowledge).
*/
/**
*
* @param {port} [h_port]
* @param {privat} [h_priv]
* @param {Command} [h_command]
* @param {(Status.STD_OK|*)} [h_status]
* @constructor
*/
var header = function(h_port,h_priv,h_command,h_status) {
if (h_port==undefined) {
// Create empty header
this.h_port = port();
this.h_priv = new privat();
this.h_command = undefined;
this.h_status = undefined;
} else {
this.h_port = h_port;
this.h_priv = h_priv;
this.h_command = h_command;
this.h_status = h_status;
}
};
/**
*
* @param {number} [obj]
* @param {number} [rights]
* @param {port} [rand]
* @returns {privat}
*/
function Private(obj,rights,rand) {
var _obj = new privat(obj,rights,rand);
Object.preventExtensions(_obj);
return _obj;
}
/**
*
* @param {port} [cap_port]
* @param {privat} [cap_priv]
* @returns {capability}
*/
function Capability (cap_port, cap_priv) {
var obj = new capability(cap_port, cap_priv);
Object.preventExtensions(obj);
return obj;
}
/**
*
* @param {port} [h_port]
* @param {privat} [h_priv]
* @param {Command} [h_command]
* @param {(Status.STD_OK|*)} [h_status]
* @returns {header}
*/
function Header(h_port,h_priv,h_command,h_status) {
var obj = new header(h_port,h_priv,h_command,h_status);
Object.preventExtensions(obj);
return obj;
}
/*
** Hash table of all locally created unique ports.
*/
var uniqports=[];
/**
*
*/
var Net = {
// Direction:Direction,
PORT_SIZE:PORT_SIZE,
PRIV_SIZE:PRIV_SIZE,
AFS_REQBUFSZ:AFS_REQBUFSZ,
CAP_SIZE:CAP_SIZE,
DNS_MAXCOLUMNS:DNS_MAXCOLUMNS,
TIMEOUT:5000,
DEF_RPC_MAX_HOP:DEF_RPC_MAX_HOP,
Status:Status,
Command:Command,
Rights:Rights,
Private:Private,
Capability: Capability,
Header: Header,
Port: port,
/**
* @type {port}
*/
nilport: port(),
nilpriv: Private(0,0,this.nilport),
nilcap: Capability(this.nilport,this.nilpriv),
/*
** Utils to get and set single bytes of a port
*/
get_portbyte: function(port,i) {
return Perv.int_of_char(String.get(port,i))
},
set_portbyte: function(port,i,byte) {
return String.set(port, i, (Perv.char_of_int(byte)));
},
/*
* Return a unique key of a capability that can be used for hash tables
*/
key: function (cap) {
return cap.cap_port+
cap.cap_priv.prv_obj+
cap.cap_priv.prv_rights+
cap.cap_priv.prv_rand;
},
/*
** Encryption function
*/
one_way: function (port) {
var key = Array.create(64,0);
var block = Array.create(48,0);
var pubport = String.make (PORT_SIZE,'\0');
var i, j, k;
/*
** We actually need 64 bit key.
** Throw some zeroes in at bits 6 and 7 mod 8
** The bits at 7 mod 8 etc are not used by the algorithm
*/
j=0;
for (i = 0; i< 64; i++) {
if ((i & 7) > 5)
key[i] = 0;
else {
if ((this.get_portbyte(port, (j >> 3)) & (1 << (j & 7))) != 0)
key[i] = 1;
else
key[i] = 0;
j++;
}
}
Des48.des_OWsetkey(key);
/*
** Now go encrypt constant 0
*/
block=Des48.des_OWcrypt48(block);
/*
** and put the bits in the destination port
*/
var pb = 0;
for (i = 0; i < PORT_SIZE;i++) {
var pbyte = 0;
for (j = 0; j < 8; j++) {
pbyte = pbyte | (block[pb] << j);
pb++;
}
pubport=this.set_portbyte(pubport, i, pbyte);
}
return pubport;
},
/*
** Check whether the required rights [R1;R2;..] are
** present in the rights field rg. Return a boolean value.
*/
rights_req : function(rights,required) {
var all=true;
Array.iter(required,function(rq) {
if (rq & rights == 0) all = false;
});
return all;
},
port_cmp: function(port1,port2) {
var i;
var eq=true;
for(i=0;i<PORT_SIZE;i++) { if (String.get(port1,i)!=String.get(port2,i)) eq=false;}
return eq;},
port_copy: function(port) {
return String.copy(port);
},
/*
** Derive a port from a string.
*/
port_name: function(name){
var p = String.make(PORT_SIZE,'\0');
var i;
var n = name.length;
for (i = 0; i < n;i++) {
var k = i % PORT_SIZE;
p = String.set(p, k, Perv.char_of_int(
(Perv.int_of_char(String.get(p, k)) +
Perv.int_of_char(String.get(name, i)))
& 0xff));
}
return p;
},
port_to_str: function(port,compact) {
var i,str='';
if (port) {
for (i = 0; i < PORT_SIZE; i++) {
var num = Perv.int_of_char(String.get(port, i));
if (!compact && i > 0) str = str + ':';
str = str + pad(num.toString(16).toUpperCase(), 2);
}
} else str='undefined';
return str;
},
port_of_str: function (str,compact) {
var tokens=str.split(':'),i,port='';
for (i=0;i<PORT_SIZE;i++) {
var num='0x'+tokens[i];
port=port+Perv.char_of_int(parseInt(num,16));
}
return port;
},
/** String parameter to port conversion including "undefined" case.
*
* @param str
* @returns {string}
*/
port_of_param: function (str) {
if (str==undefined ||
String.equal(str,'undefined')) return undefined;
var tokens=str.split(':');
var i;
var port='';
for (i=0;i<PORT_SIZE;i++) {
var num='0x'+tokens[i];
port=port+Perv.char_of_int(parseInt(num,16));
}
return port;
},
prv2pub: function(port) {
var putport;
if (priv2pub_cache[port] == undefined) {
putport=this.one_way(port);
priv2pub_cache[port] = putport;
} else putport = priv2pub_cache[port];
return putport;
},
/**
** Decode a private structure
*
* @param {privat} prv
* @param {port} rand
* @returns {boolean}
*/
prv_decode: function(prv,rand) {
if (prv.prv_rights == Rights.PRV_ALL_RIGHTS)
return this.port_cmp(prv.prv_rand,rand);
else {
var tmp_port = this.port_copy(rand),
pt0 = this.get_portbyte(tmp_port, 0),
pr0 = prv.prv_rights;
tmp_port = this.set_portbyte(tmp_port, 0, (pt0 ^ pr0));
tmp_port = this.one_way(tmp_port);
return this.port_cmp(prv.prv_rand, tmp_port)
}
},
/*
** Encode a private part from the object number, the rights field
** and the random port.
** Returns the created private structure.
*/
prv_encode: function(obj,rights,rand) {
var tmp_port = this.port_copy(rand);
var r1 = rights;
var rmask = Rights.PRV_ALL_RIGHTS;
if (rights == Rights.PRV_ALL_RIGHTS)
return this.Private(obj,r1 & rmask,tmp_port);
else {
var pt0 = this.get_portbyte(tmp_port,0);
tmp_port = this.set_portbyte(tmp_port,0,pt0 ^ r1);
tmp_port = this.one_way(tmp_port);
return this.Private(obj,r1 & rmask,tmp_port)
}
},
/*
** Return the private object number form a private structure
*/
prv_number: function(prv) {
return prv.prv_obj;
},
/*
** Return the private rights field.
*/
prv_rights: function(prv) {
return prv.prv_rights & Rights.PRV_ALL_RIGHTS;
},
/*
** Check the private rights field: 1. Validation, 2: Required rights.
*/
prv_rights_check: function(prv,rand,required) {
if (!Net.prv_decode(prv,rand)) return false;
return (prv.prv_rights & required)==required;
},
/** Restrict a private field (rights&mask) of a capability.
*
* @param {privat} priv
* @param {number} mask rights restriction mask
* @param {port} random secret server random port
*/
restrict: function(priv,mask,random) {
var pr =
this.prv_encode(priv.prv_obj,
priv.prv_rights & mask,
random);
return pr;
},
/*
* Return a new random port.
*
* Warning: the quality of the random ports are strongly
* related to JSVMs underlying random generator. Be warned!
*
* @returns {port}
*/
uniqport: function() {
var port = String.create (PORT_SIZE);
var exists = true;
while (exists) {
var i;
for (i = 0; i <= (PORT_SIZE - 1); i++) {
port = String.set(port, i, (Perv.char_of_int(Rand.int(256))));
}
if (uniqports[port]==undefined)
{
uniqports[port]=port;
exists=false;
}
}
return port;
},
/** Write a capability to a file.
*
* @param {capability} cap
* @param {string} path
*/
cap_to_file: function(cap,path) {
try {
Fs.writeFileSync(path, this.Print.capability(cap));
} catch(e) {
}
},
/** Read a capability from a file.
*
* @param {string} path
* @returns {capability|undefined}
*/
cap_of_file: function(path) {
try {
var cap=undefined;
var data = Fs.readFileSync(path);
var cp = this.Parse.capability(data.toString(), 0);
cap = cp.cap;
return cap;
} catch(e) {
return undefined;
}
},
Position: function (x,y) {
this.x = x;
this.y = y;
},
Copy: {
/**
*
* @param src
* @returns {port}
*/
port: function(src) {
// !!!!
return String.copy(src);
},
/**
*
* @param src
* @param dst
* @returns {privat}
*/
private: function(src,dst) {
if (dst!=undefined) {
dst.prv_obj = src.prv_obj;
dst.prv_rights = src.prv_rights;
dst.prv_rand = this.port(src.prv_rand);
return dst;
} else {
var dstnew=Private();
dstnew.prv_obj = src.prv_obj;
dstnew.prv_rights = src.prv_rights;
dstnew.prv_rand = this.port(src.prv_rand);
return dstnew;
}
},
/**
*
* @param src
* @param dst
* @returns {capability}
*/
capability: function(src,dst) {
if (dst!=undefined) {
dst.cap_port = this.port(src.cap_port);
this.private(src.cap_priv, dst.cap_priv);
return dst;
}
else {
var dstnew=Capability();
dstnew.cap_port = this.port(src.cap_port);
this.private(src.cap_priv, dstnew.cap_priv);
return dstnew;
}
},
/**
*
* @param src
* @param dst
*/
header: function(src,dst) {
dst.h_port=this.port(src.h_port);
dst.h_status=src.h_status;
dst.h_command=src.h_command;
if (src.h_priv!=undefined) {
if (dst.h_priv==undefined) {
var obj = new privat();
Object.preventExtensions(obj);
dst.h_priv=obj;
}
this.private(src.h_priv,dst.h_priv);
} else dst.h_priv=undefined;
}
},
Equal: {
port: function (port1,port2) {
if (port1==undefined || port2==undefined) return (port1==port2);
else return String.equal(port1,port2);
},
private: function (prv1,prv2) {
return (prv1==undefined&&prv2==undefined) ||
(prv1.prv_obj==prv2.prv_obj &&
prv1.prv_rights==prv2.prv_rights &&
this.port(prv1.prv_rand,prv2.prv_rand))
},
capability: function(cap1,cap2) {
return (cap1==undefined&&cap2==undefined) ||
(this.private(cap1.cap_priv,cap2.cap_priv) &&
this.port(cap1.cap_port,cap2.cap_port))
},
header: function(hdr1,hdr2) {
return (hdr1==undefined&&hdr2==undefined) ||
(this.private(hdr1.h_priv,hdr1.h_priv) &&
this.port(hdr1.h_port,hdr2.h_port) &&
hdr1.h_status==hdr2.h_status &&
hdr1.h_command==hdr2.h_command)
}
},
/**
* @typedef {{
* port:function(string,number):{port:port,pos:number}|undefined,
* private:function(string,number):{priv:privat,pos:number}|undefined,
* capability:function(string,number):{cap:capability,pos:number}|undefined
* }} Parse
*/
Parse: {
/**
*
* @param str
* @param pos
* @returns {{port:port,pos:number}}
*/
port: function(str,pos) {
var port='';
var len=str.length;
if (pos==undefined) pos=0;
if (len<(pos+17)) return undefined;
if (str[pos]=='[') pos++;
for(var i=0;i<6;i++) {
var sv='0x'+str[pos]+str[pos+1];
port=port+Perv.char_of_int(Perv.int_of_string(sv));
pos=pos+2;
if (str[pos]==':') pos++;
}
if (str[pos]==']') pos++;
return {port:port,pos:pos};
},
/**
*
* @param str
* @param pos
* @returns {{priv:privat,pos:number}}
*/
private: function(str,pos) {
var priv=Private();
var sv;
var len=str.length;
if (pos==undefined) pos=0;
if (len<(pos+25)) return undefined;
if (str[pos]=='(') pos++;
sv='';
while(str[pos]!='(') {
sv=sv+str[pos];
pos++;
}
priv.prv_obj=Perv.int_of_string(sv);
sv='';
if (str[pos]=='(') pos++;
while(str[pos]!=')') {
sv=sv+str[pos];
pos++;
}
priv.prv_rights=Perv.int_of_string('0x'+sv);
if (str[pos]==')') pos++;
var pp=this.port(str,pos);
if (pp==undefined) return undefined;
priv.prv_rand=pp.port;
pos=pp.pos;
return {priv:priv,pos:pos};
},
/**
*
* @param str
* @param pos
* @returns {{cap:capability,pos:number}|undefined}
*/
capability: function(str,pos) {
var cap=Capability();
if (pos==undefined) pos=0;
var pp=this.port(str,pos);
if (pp==undefined) return undefined;
cap.cap_port=pp.port;
pos=pp.pos;
pp=this.private(str,pos);
if (pp==undefined) return undefined;
cap.cap_priv=pp.priv;
pos=pp.pos;
return {cap:cap,pos:pos};
}
},
Print: {
/**
*
* @param cap
* @returns {string}
*/
capability: function (cap) {
var str='';
if (cap==undefined) return 'undefined';
if (cap.cap_port!=undefined) str='['+this.port(cap.cap_port)+']'; else str = '[]';
if (cap.cap_priv!=undefined) str=str+'('+this.private(cap.cap_priv)+')'; else str=str+'()';
return str;
},
command: Command.print,
/**
*
* @param hdr
* @returns {string}
*/
header: function (hdr) {
var str='';
if (hdr==undefined) return 'undefined';
if (hdr.h_port!=undefined) str='['+this.port(hdr.h_port)+']'; else str = '[]';
if (hdr.h_command!=undefined) str=str+': '+Command.print(hdr.h_command);
if (hdr.h_priv!=undefined) str=str+'('+this.private(hdr.h_priv)+')'; else str=str+'()';
if (hdr.h_status!=undefined) str=str+' ?'+Status.print(hdr.h_status);
return str;
},
/**
*
* @param port
* @returns {string}
*/
port: function(port) {
var i;
var str='';
if (port!=undefined) {
for (i = 0; i < PORT_SIZE; i++) {
var num = Perv.int_of_char(String.get(port, i));
if (i > 0) str = str + ':';
str = str + pad(num.toString(16).toUpperCase(), 2);
}
} else str='undefined';
return str;
},
/**
*
* @param priv
* @returns {string}
*/
private: function(priv) {
var str='';
if (priv==undefined) return 'undefined';
str=priv.prv_obj;
str=str+'('+String.format_hex(priv.prv_rights,2).toUpperCase()+')[';
str=str+this.port(priv.prv_rand)+']';
return str;
},
status: Status.print
}
};
module.exports = Net;
};
BundleModuleCode['com/cbl']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2017 bLAB
** $CREATED: 27-11-17 by sbosse.
** $VERSION: 1.1.1
**
** $INFO:
**
** JavaScript Callback List
**
** Assume there is a set of non-blocking IO operations with callbacks io1,io2,.., and there is a final
** callback function that should be called after all io operations have finished.
**
** $ENDOFINFO
*/
function CBL(callback) {
if (!(this instanceof CBL)) return new CBL(callback);
this.schedules=[];
this.callback=callback;
}
// Next schedule
CBL.prototype.next = function (status) {
var f=this.schedules.shift();
// if (f) console.log('next '+f.toString())
if (f) {
f(this.next.bind(this),status);
} else if (this.callback) this.callback(status);
}
// Add next IO callback at the end of the list
CBL.prototype.push = function (f) {
this.schedules.push(f);
}
// Execute CBL
CBL.prototype.start = function () {
this.next();
}
// Add next IO callback at the top of the list
CBL.prototype.top = function (f) {
this.schedules.unshift(f);
}
module.exports=CBL;
};
BundleModuleCode['jam/amp']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: 09-02-16 by sbosse.
** $RCS: $Id: amp.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.11.1
**
** $INFO:
**
** JAM Agent Management Port (AMP) over UDP/HTTP/devices/streams
**
**
** New: Fully negotiated IP Multicast Ports (P2N)
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Lz = Require('os/lz-string');
var Comp = Require('com/compat');
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var Command = Net.Command;
var Status = Net.Status;
var current=none;
var Aios=none;
var CBL = Require('com/cbl');
var COM = Require('jam/ampCOM'),
AMMode=COM.AMMode,
AMMessageType=COM.AMMessageType,
AMState=COM.AMState,
amp=COM.amp,
options=COM.options,
url2addr=COM.url2addr,
addr2url=COM.addr2url,
addrequal=COM.addrequal,
resolve=COM.resolve,
ipequal=COM.ipequal,
getNetworkIP=COM.getNetworkIP,
doUntilAck=COM.doUntilAck;
options.localhost='localhost';
options.version='1.11.1',
options.AMC_MAXLIVE=5,
options.TIMER=500,
options.TRIES=10;
options.REGTMO=1000;
options.pem={};
/***********************
** AMP
************************/
var ampMAN = Require('jam/ampMAN');
var ampRPC = Require('jam/ampRPC');
if (global.TARGET!= 'browser') {
/******************************* AMP *************************************/
var ampUDP = Require('jam/ampUDP');
var ampTCP = Require('jam/ampTCP');
var ampStream = Require('jam/ampStream');
}
var ampHTTP = Require('jam/ampHTTP');
var ampHTTPS = Require('jam/ampHTTPS');
/** Main AMP constructor
* ====================
*
*/
var Amp = function (options) {
var obj;
switch (options.proto) {
case 'stream':
obj=new amp.stream(options);
break;
case 'http':
obj=new amp.http(options);
break;
case 'https':
obj=new amp.https(options);
break;
case 'tcp':
obj=new amp.tcp(options);
break;
case 'udp':
default:
obj=new amp.udp(options);
}
return obj;
}
module.exports.current=function (module) {
current=module.current; Aios=module;
if (ampMAN) ampMAN.current(module);
if (ampUDP) ampUDP.current(module);
if (ampHTTP) ampHTTP.current(module);
if (ampHTTPS) ampHTTPS.current(module);
if (ampTCP) ampTCP.current(module);
if (ampStream) ampStream.current(module);
if (ampRPC) ampRPC.current(module);
};
module.exports.Amp=Amp;
module.exports.AMMessageType=AMMessageType;
module.exports.AMState=AMState;
module.exports.AMMode=AMMode;
module.exports.Com=COM;
module.exports.Rpc=ampRPC;
module.exports.url2addr=url2addr;
module.exports.addr2url=addr2url;
module.exports.resolve=resolve;
module.exports.Command=Command
module.exports.Status=Status
module.exports.options=options;
};
BundleModuleCode['jam/ampCOM']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 09-02-16 by sbosse.
** $RCS: $Id: ampCOM.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.16.1
**
** $INFO:
**
** JAM Agent Management Port (AMP) - Common Types and Utils
**
**
**
** $ENDOFINFO
*/
var options = {
debug:{},
magic: 0x6dab, // start of an AMP message (16bit)
peekIP: '134.102.22.124', // used by getnetworkip, must be an HTTP server
localhost : 'localhost', // default name for localhost
}
var Comp = Require('com/compat');
var Dns = Require('dns');
// Channel mode flags
var AMMode = {
AMO_UNICAST: 1, // P2P
AMO_MULTICAST: 2, // P2N
AMO_STATIC: 4, //
AMO_BUFFER: 8, // Transfer buffer data
AMO_OBJECT: 16, // Transfer objects instead of buffer data
AMO_COMPRESS: 32, // Compress data
AMO_SERVER: 64, // This is HTTP server mode
AMO_CLIENT: 128, // This is HTTP client mode
AMO_ONEWAY:256, // Other side can be reache dw/o link negotiation
print: function (m) {
var s='',sep='';
if (m & AMMode.AMO_UNICAST) s += (sep+'UNI'),sep='|';
if (m & AMMode.AMO_MULTICAST) s += (sep+'MUL'),sep='|';
if (m & AMMode.AMO_STATIC) s += (sep+'STA'),sep='|';
if (m & AMMode.AMO_BUFFER) s += (sep+'BUF'),sep='|';
if (m & AMMode.AMO_OBJECT) s += (sep+'OBJ'),sep='|';
if (m & AMMode.AMO_COMPRESS) s += (sep+'ZIP'),sep='|';
if (m & AMMode.AMO_CLIENT) s += (sep+'CLI'),sep='|';
if (m & AMMode.AMO_SERVER) s += (sep+'SRV'),sep='|';
if (m & AMMode.AMO_ONEWAY) s += (sep+'ONE'),sep='|';
return s;
}
}
// Message type
var AMMessageType = {
AMMACK:0,
AMMPING:1,
AMMPONG:2,
AMMLINK:3,
AMMUNLINK:4,
AMMRPCHEAD:6, // Header followed by multiple data requests
AMMRPCDATA:7,
// Broker Rendezvous support
AMMCONTROL:8,
AMMRPC:9, // Header + data in one message
AMMCOLLECT:10, // Collect messages
AMMRPCHEADDATA:11, // Header with embedded data
// Port Scan for external Run Server - returns AMP info
AMMSCAN : 12,
AMMINFO : 13,
print:function(op) {
switch (op) {
case AMMessageType.AMMACK: return "AMMACK";
case AMMessageType.AMMPING: return "AMMPING";
case AMMessageType.AMMPONG: return "AMMPONG";
case AMMessageType.AMMLINK: return "AMMLINK";
case AMMessageType.AMMUNLINK: return "AMMUNLINK";
case AMMessageType.AMMRPCHEAD: return "AMMRPCHEAD";
case AMMessageType.AMMRPCHEADDATA: return "AMMRPCHEADDATA";
case AMMessageType.AMMRPCDATA: return "AMMRPCDATA";
case AMMessageType.AMMRPC: return "AMMRPC";
case AMMessageType.AMMCOLLECT: return "AMMCOLLECT";
// Rendezvous Broker Message
case AMMessageType.AMMCONTROL: return "AMMCONTROL";
case AMMessageType.AMMSCAN: return "AMMSCAN";
case AMMessageType.AMMINFO: return "AMMINFO";
default: return "Chan.AMMessageType?";
}
}
};
// Channel state
var AMState = {
AMS_NOTINIT:1, // AMP Not initialized conenction
AMS_INIT:2, // AMP Server started, but not confirmed
AMS_READY:3, // AMP Server initialized and confirmed (other end point not connected)
AMS_NEGOTIATE:4, // AMP Server intiialized, in negotiation state (other end point not connected)
AMS_CONNECTED:5, // AMP Other side connected
AMS_AWAIT:6, // AMP waits for event (pairing)
AMS_NOTCONNECTED:10, // AMP Other side not connected
// Optional IP broker service
AMS_RENDEZVOUS:7, // Broker IP P2P rendezvous; starting
AMS_REGISTERED:8, // Broker IP P2P rendezvous; registered; expecting pairing
AMS_PAIRING:9, // Broker IP P2P rendezvous; now pairing; send punches until paired
AMS_PAIRED:10, // Broker IP P2P rendezvous; acknowldeged and paired -> NOTCONNECTED
print:function(op) {
switch (op) {
case AMState.AMS_NOTINIT: return "AMS_NOTINIT";
case AMState.AMS_INIT: return "AMS_INIT";
case AMState.AMS_READY: return "AMS_READY";
case AMState.AMS_NEGOTIATE: return "AMS_NEGOTIATE";
case AMState.AMS_CONNECTED: return "AMS_CONNECTED";
case AMState.AMS_AWAIT: return "AMS_AWAIT";
case AMState.AMS_NOTCONNECTED: return "AMS_NOTCONNECTED";
case AMState.AMS_RENDEZVOUS: return "AMS_RENDEZVOUS";
case AMState.AMS_REGISTERED: return "AMS_REGISTERED";
case AMState.AMS_PAIRING: return "AMS_PAIRING";
case AMState.AMS_PAIRED: return "AMS_PAIRED";
default: return "Chan.AMState?";
}
}
};
/** Used by AMP messages (msg.cmd,msg.status)
*
*/
// Standard Object Service
var STD_FIRST_COM = 1000;
var STD_LAST_COM = 1999;
var STD_FIRST_ERR = (-STD_FIRST_COM);
var STD_LAST_ERR = (-STD_LAST_COM);
// System Process Server (incl. Agent Platform Manager)
var PS_FIRST_COM = 2200;
var PS_LAST_COM = 2299;
var PS_FIRST_ERR = (-PS_FIRST_COM);
var PS_LAST_ERR = (-PS_LAST_COM);
var Command = {
/*
** Standard Commands
*/
STD_MONITOR : STD_FIRST_COM,
STD_AGE : (STD_FIRST_COM+1),
STD_COPY : (STD_FIRST_COM + 2),
STD_DESTROY : (STD_FIRST_COM + 3),
// Get agent or node info
STD_INFO : (STD_FIRST_COM + 4),
STD_RESTRICT : (STD_FIRST_COM + 5),
// Get agent or node status
STD_STATUS : (STD_FIRST_COM + 6),
STD_TOUCH : (STD_FIRST_COM + 7),
STD_GETPARAMS : (STD_FIRST_COM + 8),
STD_SETPARAMS : (STD_FIRST_COM + 9),
STD_NTOUCH : (STD_FIRST_COM + 10),
STD_EXIT : (STD_FIRST_COM + 11),
STD_RIGHTS : (STD_FIRST_COM + 12),
STD_EXEC : (STD_FIRST_COM + 13),
STD_LOCATION : (STD_FIRST_COM + 20),
STD_LABEL : (STD_FIRST_COM + 21),
/*
** Agent Process Control
*/
PS_STUN : (PS_FIRST_COM), // Kill a process/ create a snapshot
PS_MIGRATE : (PS_FIRST_COM+1), // Execute a process from a snapshot after migration (->next+)
PS_EXEC : (PS_FIRST_COM+2), // Execute a process from a snapshot (->next)
PS_WRITE : (PS_FIRST_COM+4), // Store a process class template
PS_READ : (PS_FIRST_COM+5), // Get a process class template
PS_CREATE : (PS_FIRST_COM+6), // Create a process from a template and execute
PS_FORK : (PS_FIRST_COM+7), // Fork a process from a running process
PS_SIGNAL : (PS_FIRST_COM+8), // Send a signal to a process
};
var Status = {
STD_OK:0,
STD_CAPBAD : STD_FIRST_ERR,
STD_COMBAD : (STD_FIRST_ERR-1),
STD_ARGBAD : (STD_FIRST_ERR-2),
STD_NOTNOW : (STD_FIRST_ERR-3),
STD_NOSPACE : (STD_FIRST_ERR-4),
STD_DENIED : (STD_FIRST_ERR-5),
STD_NOMEM : (STD_FIRST_ERR-6),
STD_EXISTS : (STD_FIRST_ERR-7),
STD_NOTFOUND : (STD_FIRST_ERR-8),
STD_SYSERR : (STD_FIRST_ERR-9),
STD_INTR : (STD_FIRST_ERR-10),
STD_OVERFLOW : (STD_FIRST_ERR-11),
STD_WRITEPROT : (STD_FIRST_ERR-12),
STD_NOMEDIUM : (STD_FIRST_ERR-13),
STD_IOERR : (STD_FIRST_ERR-14),
STD_WRONGSRV : (STD_FIRST_ERR-15),
STD_OBJBAD : (STD_FIRST_ERR-16),
STD_UNKNOWN : (STD_FIRST_ERR-17),
RPC_FAILURE : -1,
BUF_OVERFLOW : -2,
}
var amp={
AMMessageType:AMMessageType,
AMState:AMState
};
/** Search a channel that is connected to node 'destnode'
*
*/
function lookupNode(node,destnode) {
var chan,url;
if (node.connections.ip && node.connections.ip.lookup) {
url=node.connections.ip.lookup(destnode);
if (url) return {
chan:node.connections.ip,
url:url,
link:node.connections.ip.routingTable[url]
};
}
}
/*************************
** IP UTILS
*************************/
function isLocal(addr) {
return addr=='localhost'||
addr=='127.0.0.1'
}
function isIpAddr(addr) {
return (/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/.test(addr))
}
/* typeof @url = "<proto>://<domain>:<ipport>" | "<domain>:<ipport>" |
* "<name>:<ipport>" | "<ip>:<ipport>" | "<ip>:<portname>" | "<ipport>"
* and @ipport = (1-65535) | "*"
* and @port = string
*/
function parseUrl(url) {
if (!isNaN(Number(url)) || url=='*') return {
proto: undefined,
address: undefined,
port: url,
param: undefined,
value: undefined
}
var tokens = url.match(/((http|https|udp|tcp):\/\/)?([a-zA-Z0-9_\.\-]+):(\[?[a-zA-Z0-9]+\]?|\*)(\?([a-zA-z0-9]+)=([a-zA-Z0-9:]+))?/)
if (!tokens)
tokens = url.match(/((http|https|udp|tcp):\/\/)?([a-zA-Z0-9_\.\-]+)/);
return {
proto: tokens[2],
address: tokens[3],
port: tokens[4],
param: tokens[6],
value: tokens[7]
}
}
function url2addr(url,defaultIP,callback) {
var addr={address:defaultIP||options.localhost,port:undefined},
parts = parseUrl(url);
if (parts.proto) addr.proto=parts.proto;
if (parts.address) addr.address=parts.address;
if (parts.port && parts.port!='*')
addr.port=!isNaN(Number(parts.port))?Number(parts.port):parts.port;
if (parts.param) {
addr.parameter={};
addr.parameter[parts.param]=parts.value;
}
if (!isLocal(parts.address) && !isIpAddr(parts.address)) {
// <domain>:<ipport>
// needs dns lookup with callback (async)
if (Dns)
Dns.lookup(parts.address, function (err,_addr) {
if (!err) addr.address=_addr;
if (callback) callback(addr);
});
else if (callback) callback(addr);
return addr;
}
if (callback) callback(addr);
else return addr;
};
function params(po) {
var s='?',sep='';
for(var p in po) {
s += (sep+p+'='+po[p]);
sep='&';
}
return s;
}
function addr2url(addr,noproto) {
return (!noproto && addr.proto?(addr.proto+'://'):'')+
(isLocal(addr.address)?options.localhost:addr.address)+':'+
(addr.port?addr.port:'*')+
(!noproto && addr.parameter?params(addr.parameter):'')
};
function obj2url(obj) {
if (!obj) return '*';
if (obj.name && !obj.address) return obj.name+':*';
if (!obj.address) return '*';
return (isLocal(obj.address)?options.localhost:obj.address)+':'+(obj.port?obj.port:'*')
};
function addrequal(addr1,addr2) {
return ipequal(addr1.address,addr2.address) && addr1.port==addr2.port;
}
function addrempty(addr) {
return !(addr && addr.address && addr.port);
}
function resolve (url) {return addr2url(url2addr(url)) }
function ipequal(ip1,ip2) {
if (ip1==undefined || ip2==undefined) return false;
else if ((Comp.string.equal(ip1,'localhost') || Comp.string.equal(ip1,'127.0.0.1')) &&
(Comp.string.equal(ip2,'localhost') || Comp.string.equal(ip2,'127.0.0.1'))) return true;
else return ip1==ip2;
}
// Use remote TCP connection to get this host IP (private address if behind NAT)
var ipnet = Require('net');
var myip;
function getNetworkInterfaces() {
var results = null;
try {
var networkInterfaces = require('os').networkInterfaces;
var nets = networkInterfaces();
for (var name of Object.keys(nets)) {
for (var net of nets[name]) {
// Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
if (net.family === 'IPv4' && !net.internal) {
results=results||{};
if (!results[name]) {
results[name] = [];
}
results[name].push(net.address);
}
}
}
} catch (e) {};
return results
}
function getNetworkIP(server,callback) {
var socket;
// 0. Use user defined environment variable
try {
if (typeof process != 'undefined' &&
process.env &&
process.env['HOSTIP'])
return callback(undefined,process.env['HOSTIP']);
} catch (e) {
}
// 1. Try to connect external HTTP server to get our public IP
if (!ipnet) return callback('Not supported','error');
if (myip) return callback(undefined,myip);
if (!server) server={address:options.peekIP,port:80};
socket = ipnet.createConnection(server.port, server.address);
socket.on('connect', function() {
myip=socket.address().address;
callback(undefined, socket.address().address);
socket.end();
});
socket.on('error', function(e) {
// Try to get our (local) IP from network interface information
var results = getNetworkInterfaces();
if (!results)
return callback(e, 'error');
else {
for(var i in results) return callback(undefined,results[i]);
}
});
}
function doUntilAck(interval, fn, ack, arg) {
if (ack()) return;
fn(arg);
return setTimeout(function() {
doUntilAck(interval, fn, ack, arg);
}, interval);
}
module.exports = {
AMMode:AMMode,
AMMessageType:AMMessageType,
AMState:AMState,
doUntilAck:doUntilAck,
getNetworkIP:getNetworkIP,
amp:amp,
options:options,
addrempty:addrempty,
addrequal:addrequal,
addr2url:addr2url,
ipequal:ipequal,
isLocal:isLocal,
lookupNode:lookupNode,
obj2url:obj2url,
resolve:resolve,
url2addr:url2addr,
Command:Command,
Status:Status,
}
};
BundleModuleCode['jam/ampMAN']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: 30-01-18 by sbosse.
** $RCS: $Id: ampMAN.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.14.9
**
** $INFO:
**
** JAM Agent Management Port (AMP) - General Management Operations
**
**
** New:
** - Single message transfers (HEADER+DATA)
**
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Lz = Require('os/lz-string');
var Comp = Require('com/compat');
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var Sec = Require('jam/security');
var Command = Net.Command;
var Status = Net.Status;
var current=none;
var Aios=none;
var CBL = Require('com/cbl');
var COM = Require('jam/ampCOM'),
AMMode=COM.AMMode,
AMMessageType=COM.AMMessageType,
AMState=COM.AMState,
amp=COM.amp,
options=COM.options,
url2addr=COM.url2addr,
addr2url=COM.addr2url,
addrequal=COM.addrequal,
resolve=COM.resolve,
ipequal=COM.ipequal,
addrempty=COM.addrempty,
getNetworkIP=COM.getNetworkIP;
// Get data from message
function msgData(msg) {
// typeof msg.data = Array | Buffer | { type: 'Buffer', data: Array }
return msg.data && msg.data.data?msg.data.data:msg.data;
}
module.exports.current=function (module) { current=module.current; Aios=module; };
amp.man = function (options) {
}
// Message logger
amp.man.prototype.LOG = function (op,msg) {
if (!this.logging) return;
switch (op) {
case 'print':
for(var i in this.logs) {
Aios.log(this.logs[i].op,this.logs[i].time,this.logs[i].msg,AMState.print(this.logs[i].state));
}
this.logs=[];
break;
case 'enable':
this.logging=true;
break;
case 'disable':
this.logging=false;
break;
default:
var date = new Date();
var time = Math.floor(date.getTime());
this.logs.push({op:op,time:time,msg:msg,state:(this.url && this.links[this.url].state)});
}
}
/** Transation cache for receiving data fragments that can be out of order.
* typeof @data = [handler:{tid,remote,cmd,size,frags,buf},data:[],timeout:number]
*
*/
amp.man.prototype.addTransaction = function (remote,tid,data) {
if (this.mode & AMMode.AMO_MULTICAST)
this.transactions[remote.address+remote.port+tid]=data;
else
this.transactions[tid]=data;
}
amp.man.prototype.deleteTransaction = function (remote,tid) {
if (this.mode & AMMode.AMO_MULTICAST)
delete this.transactions[remote.address+remote.port+tid];
else
delete this.transactions[tid];
}
amp.man.prototype.findTransaction = function (remote,tid) {
if (this.mode & AMMode.AMO_MULTICAST)
return this.transactions[remote.address+remote.port+tid];
else
return this.transactions[tid];
}
/** Check the state of a link
*
*/
amp.man.prototype.checkState = function (state,addr) {
switch (state) {
case AMState.AMS_CONNECTED:
if (this.mode & AMMode.AMO_ONEWAY) return true;
if (this.mode & AMMode.AMO_MULTICAST) return this.links[addr2url(addr,true)];
if (this.url && this.links[this.url].state == AMState.AMS_CONNECTED) return true;
break;
}
return false;
}
/** Update AMP object configuration
*
*/
amp.man.prototype.config = function(options) {
for(var p in options) this[p]=options[p];
}
/** Handle events
*
*/
amp.man.prototype.emit = function(event,arg,aux,aux2) {
if (this.events[event]) this.events[event](arg,aux,aux2);
}
/** Handler for incoming messages (proecssed by receiver)
*
*/
amp.man.prototype.handle = function (msg,remote,response) {
var handler,thisnum,ipport,cmsg,url,ack,info;
if (this.verbose > 1) this.out('handle '+AMMessageType.print(msg.type)+' from '+addr2url(remote),true);
switch (msg.type) {
case AMMessageType.AMMRPCHEAD:
case AMMessageType.AMMRPCHEADDATA:
if (!this.checkState(AMState.AMS_CONNECTED,remote)) return;
handler={};
handler.tid=msg.tid;
// handler.remote=remote.address+':'+Buf.buf_get_int16(buf);
handler.remote=remote;
handler.cmd=msg.cmd;
handler.size=msg.size;
handler.frags=msg.frags;
if (msg.size<0) this.err('Got invalid message (size<0) from '+addr2url(remote)); // in16 limit
// console.log(handler)
if (handler.size>0 && handler.frags>0) {
// AMMRPCDATA messages are following (used by UDP)
handler.buf=Buf.Buffer();
dlist = Comp.array.range(0, handler.frags - 1);
// Add transaction to cache for pending data
this.addTransaction(remote, handler.tid, [handler,dlist,1000]);
} else if (handler.size>0) {
// Single message transfer; message contains all data (msg.data: Buf.buffer!, used by TCP)
handler.buf=msg.data;
this.callback(handler);
} else {
// No data; control message
handler.buf=Buf.Buffer();
this.callback(handler);
}
break;
case AMMessageType.AMMRPCDATA:
if (!this.checkState(AMState.AMS_CONNECTED,remote)) return;
thisnum = msg.off/this.dlimit;
transaction = this.findTransaction(remote,msg.tid);
if (transaction!=undefined) {
handler=transaction[0];
if (this.verbose>1)
this.out('receiver: adding data num='+
thisnum+' off='+msg.off+' size='+msg.size+' dlist='+transaction[1],true);
Buf.buf_get_buf(msg.data,handler.buf,msg.off,msg.size);
transaction[1]=Comp.array.filter(transaction[1],function(num) {return (num!=thisnum)});
if (Comp.array.empty(transaction[1])) {
if (this.verbose>2) this.out('[AMP] receiver: finalize '+addr2url(remote),true);
// Io.out(handler.data.toString());
// Deliver
this.callback(handler);
this.deleteTransaction(remote,msg.tid);
}
}
break;
case AMMessageType.AMMRPC:
// Single data transfer - used by HTTP/Browser
if (!this.checkState(AMState.AMS_CONNECTED,remote)) return;
// Complete RPC message
handler={};
handler.tid=msg.tid;
// handler.remote=remote.address+':'+Buf.buf_get_int16(buf);
handler.remote=remote;
handler.cmd=msg.cmd;
handler.size=msg.size;
handler.frags=msg.frags;
handler.buf=Buf.Buffer(msgData(msg));
this.callback(handler);
if (this.ack && response) this.ack(response);
break;
case AMMessageType.AMMPING:
url=addr2url(remote,true);
ipport=remote.port;
if (this.mode&AMMode.AMO_MULTICAST) {
if (!this.links[url] || this.links[url].state!=AMState.AMS_CONNECTED) return;
} else if (this.url) {
if (this.links[this.url].state!=AMState.AMS_CONNECTED) return;
}
// Send back a PONG message only if we're connected
this.pong({address:remote.address,port:ipport},response);
break;
case AMMessageType.AMMPONG:
ipport=remote.port;
if (this.mode&AMMode.AMO_MULTICAST) {
url=addr2url(remote,true);
if (this.links[url] && this.links[url].state==AMState.AMS_CONNECTED) {
this.links[url].live = options.AMC_MAXLIVE;
}
} else if (this.url && this.links[this.url].state==AMState.AMS_CONNECTED) {
this.links[this.url].live = options.AMC_MAXLIVE;
}
if (this.ack && response) this.ack(response);
break;
case AMMessageType.AMMACK:
// TODO: check pending waiters (scan mode)
if (msg.status=="ELINKED") {
if (this.mode&AMMode.AMO_MULTICAST) {
// Multicast mode
url=addr2url(remote,true);
if (!this.links[url] || this.links[url].state==AMState.AMS_NOTCONNECTED) {
// Ad-hoc remote connect
if (!this.links[url]) this.links[url]={};
this.links[url].snd=remote;
this.links[url].live=options.AMC_MAXLIVE;
this.links[url].port=msg.port;
this.links[url].ipport=remote.port;
this.links[url].state=AMState.AMS_CONNECTED;
this.links[url].node=msg.node;
this.emit('route+',url,msg.node);
this.watchdog(true);
if (this.verbose)
this.out('Linked with ad-hoc '+this.proto+' '+url+', AMP '+
Net.Print.port(msg.port)+', Node '+msg.node,true);
}
}
}
break;
case AMMessageType.AMMLINK:
ipport=remote.port;
url=addr2url(remote,true);
if (this.secure && (!msg.secure || !Sec.Port.equal(this.secure,msg.secure))) return;
if (this.mode&AMMode.AMO_MULTICAST) {
// Multicast mode
if (!this.links[url] || this.links[url].state==AMState.AMS_NOTCONNECTED) {
// Ad-hoc remote connect
if (!this.links[url]) this.links[url]={};
this.links[url].snd=remote;
this.links[url].live=options.AMC_MAXLIVE;
this.links[url].port=msg.port;
this.links[url].ipport=remote.port;
// back link acknowledge
this.link(this.links[url].snd,false,none,response);
// no ack="EOK" -- ack send by link response!;
this.links[url].state=AMState.AMS_CONNECTED;
this.links[url].node=msg.node;
// if (this.mode&AMMode.AMO_UNICAST) this.snd=remote,this.url=url;
this.emit('route+',url,msg.node,msg.remote);
this.watchdog(true);
if (this.verbose)
this.out('Linked with ad-hoc '+this.proto+' '+url+', AMP '+
Net.Print.port(msg.port)+', Node '+msg.node,true);
} else if (this.links[url].state==AMState.AMS_CONNECTED) {
// Already linked! Just acknowledge
ack="ELINKED";
}
} else {
// Unicast mode; only one connection
if (this.links[url] && !addrempty(this.links[url].snd) &&
this.links[url].state==AMState.AMS_NOTCONNECTED &&
ipequal(this.links[url].snd.address,remote.address) &&
this.links[url].snd.port==ipport) // ipport or remote.port??
{
// Preferred / expected remote connect
this.links[url].snd=remote;
this.links[url].port=msg.port;
this.links[url].ipport=remote.port;
this.links[url].node=msg.node;
this.links[url].live=options.AMC_MAXLIVE;
// back link acknowledge
this.link(this.links[url].snd);
this.links[url].state=AMState.AMS_CONNECTED;
// Inform router
this.emit('route+',url,msg.node,msg.remote);
this.watchdog(true);
if (this.verbose)
this.out('Linked with preferred '+this.proto+' '+ url +', '+
Net.Print.port(msg.port),true);
} else if ((!this.links[url] && !this.url) ||
(this.links[url] && this.links[url].state==AMState.AMS_NOTCONNECTED) ||
(this.broker && this.url && this.links[this.url].state==AMState.AMS_NOTCONNECTED)) {
if (!this.links[url]) this.links[url]={};
this.links[url].snd=remote;
this.links[url].live=options.AMC_MAXLIVE;
this.links[url].port=msg.port;
this.links[url].ipport=remote.port;
this.links[url].node=msg.node;
// back link acknowledge
this.link(this.links[url].snd,false,none,response);
// no ack="EOK"; - ack was send with link message!
this.links[url].state=AMState.AMS_CONNECTED;
this.url=url; // remember this link
// Inform router
this.emit('route+',url,msg.node);
this.watchdog(true);
if (this.verbose)
this.out('Linked with ad-hoc ' + this.proto +' '+ url +', '+
Net.Print.port(msg.port),true);
}
}
if (ack && this.ack && response) this.ack(response,ack);
break;
case AMMessageType.AMMUNLINK:
ipport=remote.port;
if (this.mode&AMMode.AMO_MULTICAST) {
// Multicast mode
url=addr2url(remote,true); // ipport or remote.port??
if (this.links[url] && !addrempty(this.links[url].snd) && ipequal(this.links[url].snd.address,remote.address) &&
this.links[url].snd.port==ipport && this.links[url].state==AMState.AMS_CONNECTED) {
this.links[url].state=AMState.AMS_NOTCONNECTED;
// Not negotiated. Just close the link!
if (this.verbose)
this.out('Unlinked ' +url+', '+
Net.Print.port(msg.port),true);
// Inform router
this.emit('route-',url);
if (!this.links[url].snd.connect) this.links[url].snd={};
if (this.cleanup) this.cleanup(url);
}
} else {
// Unicast mode
if (this.url && !addrempty(this.links[this.url].snd) &&
ipequal(this.links[this.url].snd.address,remote.address) &&
this.links[this.url].snd.port==ipport &&
this.links[this.url].state==AMState.AMS_CONNECTED)
{
this.links[this.url].state=AMState.AMS_NOTCONNECTED;
addr=this.links[this.url].snd;
// Not negotiated. Just close the link!
if (this.verbose)
this.out('Unlinked ' + this.url +', '+
Net.Print.port(msg.port),true);
// Inform router
this.emit('route-',addr2url(addr));
if (!this.links[this.url].snd.connect) this.links[this.url].snd=null;
if (this.cleanup) this.cleanup(url);
}
}
if (this.ack && response) this.ack(response);
break;
// optional rendezvous brokerage ; remote is broker IP!!!
case AMMessageType.AMMCONTROL:
cmsg = JSON.parse(msgData(msg));
if (this.verbose>1) this.out('# got message '+msgData(msg),true);
this.LOG('rcv',cmsg);
// All brokerage and pairing is handled by the root path '*'!
if (this.control && this.links['*'])
this.control(this.links['*'],cmsg,remote);
break;
case AMMessageType.AMMSCAN:
url=addr2url(remote,true);
ipport=remote.port;
info={
world:(current.world&&current.world.id),
stats:(current.world&&current.world.info()),
};
this.scan({address:remote.address,port:ipport,info:info},response);
break;
default:
this.out('handle: Unknown message type '+msg.type,true);
}
}
/** Install event handler
*
*/
amp.man.prototype.on = function(event,handler) {
this.events[event]=handler;
}
// Status of link, optionally checking destination
amp.man.prototype.status = function (ip,ipport) {
var p,url,sl=[];
if (ip=='%') {
// return all connected nodes
for(p in this.links) if (this.links[p] && this.links[p].state==AMState.AMS_CONNECTED)
sl.push(this.links[p].node);
return sl;
}
if (this.mode&AMMode.AMO_MULTICAST) {
if (!ip) {
for(p in this.links) if (this.links[p] && this.links[p].state==AMState.AMS_CONNECTED) return true;
return false;
} else {
url=addr2url({address:ip,port:ipport});
if (!this.links[url]) return false;
return this.links[url].state==AMState.AMS_CONNECTED;
}
}
if (!ip && this.url) return this.links[this.url].state==AMState.AMS_CONNECTED ||
(this.mode&AMMode.AMO_ONEWAY)==AMMode.AMO_ONEWAY;
return (this.url && ipequal(this.links[this.url].snd.address,ip) && this.links[this.url].snd.port==ipport);
}
};
BundleModuleCode['jam/security']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2019 bLAB
** $CREATED: 04-02-19 by sbosse.
** $RCS: $Id: security.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.1.3
**
** $INFO:
**
** JAM Capability and Security Management. Derived from dos/net module.
**
**
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Des48 = Require('dos/des48');
var Base64 = Require('os/base64');
var Comp = Require('com/compat');
var String = Comp.string;
var Array = Comp.array;
var Perv = Comp.pervasives;
var current = none;
var Aios = none;
var Rnd = Require('com/pwgen');
var PORT_SIZE = 6;
var PRIV_SIZE = 4+PORT_SIZE;
var CAP_SIZE = 16;
var PRV_ALL_RIGHTS = 0xff;
var priv2pub_cache = [];
var uniquePorts = {};
var Rights = {
HOST_INFO : 0x01,
HOST_READ : 0x02,
HOST_WRITE : 0x04,
HOST_EXEC : 0x08,
PSR_READ : 0x01,
PSR_WRITE : 0x02,
PSR_CREATE : 0x04,
PSR_DELETE : 0x08,
PSR_EXEC : 0x10,
PSR_KILL : 0x20,
PSR_ALL : 0xff,
NEG_SCHED : 0x08,
NEG_CPU : 0x10,
NEG_RES : 0x20,
NEG_LIFE : 0x40,
NEG_LEVEL : 0x80,
PRV_ALL_RIGHTS : 0xff
};
/**
*
* typeof @port_valse = number []
* typeof return = string
*/
var Port = function (port_vals) {
if (port_vals==undefined) port_vals=[0,0,0,0,0,0];
var port='';
for(var i = 0; i< PORT_SIZE;i++) {
port=port+Perv.char_of_int(port_vals[i]);
}
return port;
};
/**
*
* typeof @obj = number | undefined
* typeof @rights = number | undefined
* typeof @rand = port | undefined
* typeof function = constructor
*/
var Private = function (obj,rights,rand) {
if (obj==undefined) {
// Create empty private field
return {
prv_obj : 0,
prv_rights : 0,
prv_rand : Port()
}
} else {
return {
prv_obj : obj, // Integer
prv_rights : rights, // Integer
prv_rand : rand // Port=string
}
}
}
/**
*
* typeof @cap_port = port
* typeof @cap_priv = privat
* typeof function = @constructor
*/
var Capability = function(cap_port, cap_priv) {
if (cap_port==undefined) {
// Create empty capability
return {
cap_port : Port(),
cap_priv : Private()
}
} else {
return {
cap_port : cap_port, // Port=string
cap_priv : cap_priv?cap_priv:Private()
}
}
}
function cap_parse(str,offset) {
var cap=Capability(),
pos=0;
if (offset!=undefined) pos=offset;
var pp=port_parse(str,pos);
if (pp==undefined) return undefined;
cap.cap_port=pp.port;
pos=pp.pos;
pp=prv_parse(str,pos);
if (pp==undefined) return undefined;
cap.cap_priv=pp.priv;
pos=pp.pos;
return {cap:cap,pos:pos};
}
function cap_of_string(str) { var pp = cap_parse(str,0); return pp?pp.cap:undefined }
function cap_to_string(cap) {
var str='';
if (cap==undefined) return 'undefined';
if (cap.cap_port!=undefined) str='['+port_to_string(cap.cap_port)+']'; else str = '[]';
if (cap.cap_priv!=undefined) str=str+'('+prv_to_string(cap.cap_priv)+')'; else str=str+'()';
return str;
}
/*
** Utils to get and set single bytes of a port
*/
function get_portbyte(port,i) {
return Perv.int_of_char(String.get(port,i))
}
function set_portbyte(port,i,byte) {
return String.set(port, i, (Perv.char_of_int(byte)));
}
/*
** Encryption function
*/
function one_way(port) {
var key = Array.create(64,0);
var block = Array.create(48,0);
var pubport = String.make (PORT_SIZE,'\0');
var i, j, k;
/*
** We actually need 64 bit key.
** Throw some zeroes in at bits 6 and 7 mod 8
** The bits at 7 mod 8 etc are not used by the algorithm
*/
j=0;
for (i = 0; i< 64; i++) {
if ((i & 7) > 5)
key[i] = 0;
else {
if ((get_portbyte(port, (j >> 3)) & (1 << (j & 7))) != 0)
key[i] = 1;
else
key[i] = 0;
j++;
}
}
Des48.des_OWsetkey(key);
/*
** Now go encrypt constant 0
*/
block=Des48.des_OWcrypt48(block);
/*
** and put the bits in the destination port
*/
var pb = 0;
for (i = 0; i < PORT_SIZE;i++) {
var pbyte = 0;
for (j = 0; j < 8; j++) {
pbyte = pbyte | (block[pb] << j);
pb++;
}
pubport=set_portbyte(pubport, i, pbyte);
}
return pubport;
}
function pad(str,size) {
while (str.length < (size || 2)) {str = "0" + str;}
return str;
}
function port_cmp(port1,port2) {
if (port1==undefined || port2==undefined) return (port1==port2);
else return String.equal(port1,port2);
}
function port_copy(port) {
return String.copy(port);
}
// Expected format: XX:XX:XX:XX:XX
function port_of_string(str,compact) {
var tokens=str.split(':'),i,port='';
for (i=0;i<PORT_SIZE;i++) {
var num='0x'+tokens[i];
port=port+Perv.char_of_int(parseInt(num,16));
}
return port;
}
function port_parse(str,pos) {
var port='';
var len=str.length;
if (pos==undefined) pos=0;
if (len<(pos+17)) return undefined;
if (str[pos]=='[') pos++;
for(var i=0;i<6;i++) {
var sv='0x'+str[pos]+str[pos+1];
port=port+Perv.char_of_int(Perv.int_of_string(sv));
pos=pos+2;
if (str[pos]==':') pos++;
}
if (str[pos]==']') pos++;
return {port:port,pos:pos};
}
function port_to_string(port,compact) {
var i,str='';
if (port) {
for (i = 0; i < PORT_SIZE; i++) {
var num = Perv.int_of_char(String.get(port, i));
if (!compact && i > 0) str = str + ':';
str = str + pad(num.toString(16).toUpperCase(), 2);
}
} else str='undefined';
return str;
}
function prv2pub (port) {
var putport;
if (priv2pub_cache[port] == undefined) {
putport=one_way(port);
priv2pub_cache[port] = putport;
} else putport = priv2pub_cache[port];
return putport;
}
function prv_cmp(prv1,prv2) {
return (prv1==undefined&&prv2==undefined) ||
(prv1.prv_obj==prv2.prv_obj &&
prv1.prv_rights==prv2.prv_rights &&
port_cmp(prv1.prv_rand,prv2.prv_rand))
}
/**
** Decode a private structure (check for a valid private field)
*
* typeof @prv = privat
* typeof @rand = port
* returns boolean
*/
function prv_decode (prv,rand) {
if (prv.prv_rights == PRV_ALL_RIGHTS)
return port_cmp(prv.prv_rand,rand);
else {
var tmp_port = port_copy(rand),
pt0 = get_portbyte(tmp_port, 0),
pr0 = prv.prv_rights;
tmp_port = set_portbyte(tmp_port, 0, (pt0 ^ pr0));
tmp_port = one_way(tmp_port);
return port_cmp(prv.prv_rand, tmp_port)
}
}
/*
** Encode a private part from the object number, the rights field
** and the random port.
** Returns the created private structure.
*/
function prv_encode(obj,rights,rand) {
var tmp_port = port_copy(rand),
r1 = rights,
rmask = PRV_ALL_RIGHTS;
if (rights == PRV_ALL_RIGHTS)
return Private(obj,r1 & rmask,tmp_port);
else {
var pt0 = get_portbyte(tmp_port,0);
tmp_port = set_portbyte(tmp_port,0,pt0 ^ r1);
tmp_port = one_way(tmp_port);
return Private(obj,r1 & rmask,tmp_port)
}
}
function prv_of_string(str) { var pp=prv_parse(str,0); return pp?pp.priv:undefined }
/*
** Return the private object number form a private structure
*/
function prv_number(prv) {
return prv.prv_obj;
}
// Expected format: obj(right)[port]
function prv_parse(str,offset) {
var priv=Private();
var sv;
var len=str.length,pos=offset;
if (str[pos]=='(') pos++;
sv='';
while(str[pos]!='(') {
sv=sv+str[pos];
pos++;
}
priv.prv_obj=Perv.int_of_string(sv);
sv='';
if (str[pos]=='(') pos++;
while(str[pos]!=')') {
sv=sv+str[pos];
pos++;
}
priv.prv_rights=Perv.int_of_string('0x'+sv);
if (str[pos]==')') pos++;
var pp=port_parse(str,pos);
if (pp==undefined) return undefined;
priv.prv_rand=pp.port;
pos=pp.pos;
return {priv:priv,pos:pos};
}
function prv_to_string(priv) {
var str='';
if (priv==undefined) return 'undefined';
str=priv.prv_obj;
str=str+'('+String.format_hex(priv.prv_rights,2).toUpperCase()+')[';
str=str+port_to_string(priv.prv_rand)+']';
return str;
}
/** Restrict a private field (rights&mask) of a capability.
*
* @param {privat} priv
* @param {number} mask rights restriction mask
* @param {port} random secret server random port
*/
function prv_restrict(priv,mask,random) {
var pr = prv_encode(priv.prv_obj,
priv.prv_rights & mask,
random);
return pr;
}
/*
** Return the private rights field.
*/
function prv_rights(prv) {
return prv.prv_rights & Rights.PRV_ALL_RIGHTS;
}
/*
** Check the private rights field: 1. Validation, 2: Required rights.
*/
function prv_rights_check(prv,rand,required) {
if (!prv_decode(prv,rand)) return false;
return (prv.prv_rights & required)==required;
}
/*
* Return a new random unique port.
*
* Warning: the quality of the random ports are strongly
* related to JSVMs underlying random generator.
*
* typeof return = port
*/
function uniqport() {
var port = String.create (PORT_SIZE);
var i,values;
do {
values = Rnd.generate({number:true,length:PORT_SIZE});
for (i = 0; i <= (PORT_SIZE - 1); i++)
port = String.set(port, i, (Perv.char_of_int(values[i])));
if (uniquePorts[port]) uniquePorts[port]++;
else uniquePorts[port]=1;
} while (uniquePorts[port]>1);
return port;
}
Port.equal = port_cmp
Port.toString = port_to_string
Port.ofString = port_of_string
Port.prv2pub = prv2pub
Port.random = uniqport
Port.unique = uniqport
Private.decode = prv_decode
Private.encode = prv_encode
Private.equal = prv_cmp
Private.number = prv_number
Private.ofString = prv_of_string
Private.restrict = prv_restrict
Private.rights = prv_rights
Private.rights_check = prv_rights_check
Private.toString = prv_to_string
Capability.toString = cap_to_string
Capability.ofString = cap_of_string
var Security = {
current:function (module) { current=module.current; Aios=module; },
PORT_SIZE:PORT_SIZE,
PRIV_SIZE:PRIV_SIZE,
Rights:Rights,
Private:Private,
Capability: Capability,
Port: Port,
nilport: Port(),
nilpriv: Private(0,0,Port()),
nilcap: Capability(Port(),Private(0,0,Port())),
one_way : one_way,
prv2pub : prv2pub,
}
module.exports = Security;
};
BundleModuleCode['com/pwgen']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Bermi Ferrer, Stefan Bosse
** $INITIAL: (C) 2011-2015 Bermi Ferrer <bermi@bermilabs.com>, 2017-2018 Stefan Bosse
** $REVESIO: 1.3.1
**
** $INFO:
*
* password-generator using crypto random number generation (slow,HQ)
* !using built-in crypto random generators using either native crypto module or polyfill!
*
* options = {length,memorable,lowercase,uppercase,pattern,number?:boolean,range?:[]}
*
* Using always twister random byte generator (not random byte array)
*
* $ENDINFO
*/
var Crypto = Require('os/crypto.rand'); // Require('crypto');
module.exports.generate = function (options) {
function numgen (options) {
// assuming byte number range 0-255
var arr = new Uint8Array(options.length||8);
getRandomValues(arr);
return arr;
}
function pwgen (options) {
var localName, consonant, letter, vowel, pattern = options.pattern,
char = "", n, i, validChars = [], prefix=options.prefix;
letter = /[a-zA-Z]$/;
vowel = /[aeiouAEIOU]$/;
consonant = /[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]$/;
if (options.length == null) {
options.length = 10;
}
if (pattern == null) {
pattern = /\w/;
}
if (prefix == null) {
prefix = '';
}
// Non memorable passwords will pick characters from a pre-generated
// list of characters
if (!options.memorable) {
for (i = 33; 126 > i; i += 1) {
char = String.fromCharCode(i);
if (char.match(pattern)) {
validChars.push(char);
}
}
if (!validChars.length) {
throw new Error("Could not find characters that match the " +
"password pattern " + pattern + ". Patterns must match individual " +
"characters, not the password as a whole.");
}
}
while (prefix.length < options.length) {
if (options.memorable) {
if (prefix.match(consonant)) {
pattern = vowel;
} else {
pattern = consonant;
}
n = Crypto.randomByte(33,126); // rand(33, 126);
char = String.fromCharCode(n);
} else {
char = validChars[rand(0, validChars.length)];
}
if (options.lowercase) char = char.toLowerCase();
else if (options.uppercase) char = char.toUpperCase();
if (char.match(pattern)) {
prefix = "" + prefix + char;
}
}
return prefix;
};
function rand(min, max) {
var key, value, arr = new Uint8Array(max);
getRandomValues(arr);
for (key in arr) {
if (arr.hasOwnProperty(key)) {
value = arr[key];
if (value > min && value < max) {
return value;
}
}
}
return rand(min, max);
}
function getRandomValues(buf) {
var bytes = Crypto.randomBytes(buf.length);
buf.set(bytes);
}
if (options.number)
return numgen(options)
else
return pwgen(options);
};
};
BundleModuleCode['os/crypto.rand']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2018 bLAB
** $CREATED: 15-1-16 by sbosse.
** $VERSION: 1.2.4
**
** $INFO:
**
** Crypto module with HQ random number generators (replacing not available crypto.getRandomValues
** if there is no global crypto module).
**
** $ENDOFINFO
*/
var crypto = global.crypto || global.msCrypto;
if (!crypto && typeof require != 'undefined') try { crypto=global.crypto=require('require') } catch (e) {};
var twister;
var MersenneTwister = function(seed) {
if (seed == undefined) {
/**
** It is not sure that Math.random is seeded randomly
** Thus, a combination of current system time and Math.random
** is used to seed and initialize this random generator
*/
seed = new Date().getTime();
seed *= Math.random()*91713;
seed |= 0;
}
/* Period parameters */
this.N = 624;
this.M = 397;
this.MATRIX_A = 0x9908b0df; /* constant vector a */
this.UPPER_MASK = 0x80000000; /* most significant w-r bits */
this.LOWER_MASK = 0x7fffffff; /* least significant r bits */
this.mt = new Array(this.N); /* the array for the state vector */
this.mti=this.N+1; /* mti==N+1 means mt[N] is not initialized */
if (seed.constructor == Array) {
this.init_by_array(seed, seed.length);
}
else {
this.init_seed(seed);
}
}
/* initializes mt[N] with a seed */
/* origin name init_genrand */
MersenneTwister.prototype.init_seed = function(s) {
this.mt[0] = s >>> 0;
for (this.mti=1; this.mti<this.N; this.mti++) {
var s = this.mt[this.mti-1] ^ (this.mt[this.mti-1] >>> 30);
this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253)
+ this.mti;
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
this.mt[this.mti] >>>= 0;
/* for >32 bit machines */
}
}
/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
/* slight change for C++, 2004/2/26 */
MersenneTwister.prototype.init_by_array = function(init_key, key_length) {
var i, j, k;
this.init_seed(19650218);
i=1; j=0;
k = (this.N>key_length ? this.N : key_length);
for (; k; k--) {
var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30)
this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525)))
+ init_key[j] + j; /* non linear */
this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */
i++; j++;
if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; }
if (j>=key_length) j=0;
}
for (k=this.N-1; k; k--) {
var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30);
this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941))
- i; /* non linear */
this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */
i++;
if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; }
}
this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */
}
/* generates a random number on [0,0xffffffff]-interval */
/* origin name genrand_int32 */
MersenneTwister.prototype.random_int = function() {
var y;
var mag01 = new Array(0x0, this.MATRIX_A);
/* mag01[x] = x * MATRIX_A for x=0,1 */
if (this.mti >= this.N) { /* generate N words at one time */
var kk;
if (this.mti == this.N+1) /* if init_seed() has not been called, */
this.init_seed(5489); /* a default initial seed is used */
for (kk=0;kk<this.N-this.M;kk++) {
y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk+1]&this.LOWER_MASK);
this.mt[kk] = this.mt[kk+this.M] ^ (y >>> 1) ^ mag01[y & 0x1];
}
for (;kk<this.N-1;kk++) {
y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk+1]&this.LOWER_MASK);
this.mt[kk] = this.mt[kk+(this.M-this.N)] ^ (y >>> 1) ^ mag01[y & 0x1];
}
y = (this.mt[this.N-1]&this.UPPER_MASK)|(this.mt[0]&this.LOWER_MASK);
this.mt[this.N-1] = this.mt[this.M-1] ^ (y >>> 1) ^ mag01[y & 0x1];
this.mti = 0;
}
y = this.mt[this.mti++];
/* Tempering */
y ^= (y >>> 11);
y ^= (y << 7) & 0x9d2c5680;
y ^= (y << 15) & 0xefc60000;
y ^= (y >>> 18);
return y >>> 0;
}
/* generates a random number on [0,0x7fffffff]-interval */
/* origin name genrand_int31 */
MersenneTwister.prototype.random_int31 = function() {
return (this.random_int()>>>1);
}
/* generates a random number on [0,1]-real-interval */
/* origin name genrand_real1 */
MersenneTwister.prototype.random_incl = function() {
return this.random_int()*(1.0/4294967295.0);
/* divided by 2^32-1 */
}
/* generates a random number on [0,1)-real-interval */
MersenneTwister.prototype.random = function() {
return this.random_int()*(1.0/4294967296.0);
/* divided by 2^32 */
}
/* generates a random number on (0,1)-real-interval */
/* origin name genrand_real3 */
MersenneTwister.prototype.random_excl = function() {
return (this.random_int() + 0.5)*(1.0/4294967296.0);
/* divided by 2^32 */
}
/* generates a random number on [0,1) with 53-bit resolution*/
/* origin name genrand_res53 */
MersenneTwister.prototype.random_long = function() {
var a=this.random_int()>>>5, b=this.random_int()>>>6;
return(a*67108864.0+b)*(1.0/9007199254740992.0);
}
function polyfill () {
twister = new MersenneTwister(); // (Math.random()*Number.MAX_SAFE_INTEGER)|0)
if (!crypto) crypto=global.crypto={};
crypto.getRandomValues = function getRandomValues (abv) {
var l = abv.length
while (l--) {
abv[l] = Math.floor(twister.random() * 256)
}
return abv
}
if (!global.Uint8Array && !Uint8Array) throw new Error('crypto.rand: No Uint8Array found!');
if (!global.Uint8Array) global.Uint8Array=Uint8Array;
}
function randomByte (min,max) {
if (!twister) twister = new MersenneTwister();
return Math.floor(twister.random() * (max-min))+min;
}
function randomBytes (size, cb) {
// phantomjs needs to throw
if (size > 65536) throw new Error('requested too many random bytes')
if (!crypto || !crypto.getRandomValues) polyfill();
// in case browserify isn't using the Uint8Array version
var rawBytes = new global.Uint8Array(size);
// This will not work in older browsers.
// See https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues
if (size > 0) { // getRandomValues fails on IE if size == 0
crypto.getRandomValues(rawBytes);
}
// phantomjs doesn't like a buffer being passed here
var bytes = new Buffer(rawBytes);
if (typeof cb === 'function') {
cb(null, bytes)
}
return bytes
}
module.exports = {
randomByte:randomByte,
randomBytes:randomBytes
}
};
BundleModuleCode['jam/ampRPC']=function (module,exports){
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var Command = Net.Command;
var Status = Net.Status;
var COM = Require('jam/ampCOM');
var current=none;
var Aios=none;
// Current node must be set!
// E.g., by using jamlib.setCurrentNode(0)
// typeof @obj =
// { agent : string } |
// { node : string } |
// 'processes' |
// 'agents' |
// 'links' |
// 'ports' |
// 'node' |
// 'ts' |
// { prv: .., .. }
//
// Server side implementation is in chan.js
var Std = {
info : function (node,obj,callback) {
var node0=current.node;
var to=COM.lookupNode(node0,node);
if (to) {
to.link.control({
cmd:COM.Command.STD_INFO,
args:obj,
},to.url, function (reply) {
callback(reply)
})
}
},
status : function (node,obj,callback) {
var node0=current.node;
var to=COM.lookupNode(node0,node);
if (to) {
to.link.control({
cmd:COM.Command.STD_STATUS,
args:obj,
},to.url, function (reply) {
callback(reply)
})
}
},
}
var Run = {
stun : function (node,agent) {
},
}
module.exports = {
current : function (module) { current=module.current; Aios=module; },
Run : Run,
Std : Std
};
};
BundleModuleCode['jam/ampUDP']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: 09-02-16 by sbosse.
** $RCS: $Id: ampUDP.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.17.6
**
** $INFO:
**
** JAM Agent Management Port (AMP) over UDP
**
** Supports:
**
** - Unicast link
** - Multicast link
** - Oneway link
** - Broker service with UDP punch holing and pairing, lookup of registered nodes
**
**
** Events out: 'error','route+','route-'
**
** TODO: Garbage collection
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Lz = Require('os/lz-string');
var Comp = Require('com/compat');
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var Sec = Require('jam/security');
var Command = Net.Command;
var Status = Net.Status;
var current=none;
var Aios=none;
var CBL = Require('com/cbl');
var COM = Require('jam/ampCOM'),
AMMode=COM.AMMode,
AMMessageType=COM.AMMessageType,
AMState=COM.AMState,
amp=COM.amp,
options=COM.options,
url2addr=COM.url2addr,
addr2url=COM.addr2url,
obj2url=COM.obj2url,
addrequal=COM.addrequal,
resolve=COM.resolve,
ipequal=COM.ipequal,
doUntilAck=COM.doUntilAck,
getNetworkIP=COM.getNetworkIP,
magic=COM.options.magic;
module.exports.current=function (module) { current=module.current; Aios=module; };
var dgram = Require('dgram');
/** AMP port using UDP
* ==================
*
* type url = string
* type amp.udp = function (options:{rcv:address,snd?:address,verbose?,logging?,out?:function,log?:number,broker?:address})
* type address = {address:string,port:number}
*/
amp.udp = function (options) {
var self=this;
options=checkOptions(options,{});
this.proto = 'udp';
// Some sanity checks
this.options=options;
if (options.oneway && options.multicast) this.err('Invalid: Both ONEWAY and MULTICAST modes enabled!');
this.verbose=checkOption(options.verbose,0);
if (!options.rcv) options.rcv=url2addr('localhost:*');
this.dir = options.dir; // attached to JAM port
this.rcv = options.rcv; // IP (this side) address
this.broker = options.broker; // IP (rendezvous broker) address
this.node = options.node; // Attached to this node
this.port = options.port||Net.uniqport(); // This AMP Port
this.secure = this.options.secure;
if (this.broker && !this.broker.port)
Io.err('['+Io.Time()+' AMP] No broker port specified!');
this.out = function (msg) {
Aios.print('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] '+msg);
}
this.err = function (msg) {
Aios.print('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] Error: '+msg);
throw 'AMP';
}
// Virtual link table
// MULTICAST (P2N) mode: link cache [{addr,live,state}] of all connected links
// UNICAST (P2P): One link only, remebered by this.url!
this.links={};
if (options.snd) {
url=addr2url(options.snd,true);
this.links[url]={
snd:options.snd,
tries:0,
state:this.broker?AMState.AMS_AWAIT:AMState.AMS_NOTCONNECTED,
live:COM.AMC_MAXLIVE
};
if (this.verbose>0) this.out('Added destiantion route '+url+', '+Io.inspect(this.links[url]));
if (!options.multicast) this.url=url; // Remember this link
}
if (this.broker) {
// Create a root path that handles all brokerag and pairing
url='*';
this.links[url]={
tries:0,
state:AMState.AMS_RENDEZVOUS,
live:COM.options.AMC_MAXLIVE,
queue:{pairing:[],lookup:[]}
};
if (this.verbose>0) this.out('Added default registration route '+url);
}
this.rcv.name=options.name; // Optional name of this port
this.mode = options.multicast?AMMode.AMO_MULTICAST:AMMode.AMO_UNICAST;
if (options.oneway) this.mode |= AMMode.AMO_ONEWAY; // Oneway: No link negotation; no ack.
this.sock = dgram.createSocket("udp4"); // Receiver and sender socket
this.dlimit = options.dlimit||512;
this.count={rcv:0,snd:0,lnk:0,png:0};
this.timer=undefined;
this.inwatchdog=false;
this.events = [];
this.transactions = Comp.hashtbl.create();
this.logs=[];
this.logging=options.logging||false;
if (this.logging) {
setInterval(function () { self.LOG('print') },5000);
}
};
amp.udp.prototype.LOG = amp.man.prototype.LOG;
amp.udp.prototype.addTransaction = amp.man.prototype.addTransaction;
amp.udp.prototype.checkState = amp.man.prototype.checkState;
amp.udp.prototype.deleteTransaction = amp.man.prototype.deleteTransaction;
amp.udp.prototype.emit = amp.man.prototype.emit;
amp.udp.prototype.findTransaction = amp.man.prototype.findTransaction;
amp.udp.prototype.handle = amp.man.prototype.handle;
amp.udp.prototype.on = amp.man.prototype.on;
amp.udp.prototype.status = amp.man.prototype.status;
/** Handle AMP control messages (used by ampbroker and UDP hole punching sub-system)
* for P2P network pairing before linking is performed (NAT traversal)!
*
*/
amp.udp.prototype.control = function(link, msg, remote) {
var self=this;
switch (msg.type) {
case 'lookup':
// Lookup reply
if (link.queue.lookup[msg.path]) {
link.queue.lookup[msg.path](msg.data);
delete link.queue.lookup[msg.path];
}
break;
case 'registered':
if (link.state==AMState.AMS_RENDEZVOUS) {
link.state=AMState.AMS_REGISTERED;
if (this.verbose) self.out('Registered on broker with name '+this.rcv.name);
}
break;
case 'pairing':
if (link.state == AMState.AMS_REGISTERED) {
// pairing of remote endpoint sent by broker; punch now
var punch = { type: 'punch', from: this.rcv.name, to: msg.client.name},
counter = options.TRIES;
link.state=AMState.AMS_PAIRING; // stops sending pair requests
for (var con in msg.client.connections) {
doUntilAck(options.TIMER,
// DO
function(i) {
counter--;
self.send(punch, msg.client.connections[i]);
},
// UNTIL ACK = true
function () {
return counter==0 || link.state!=AMState.AMS_PAIRING;
},
con);
}
}
break;
case 'ack':
if (link.state == AMState.AMS_PAIRING) {
if (this.verbose) self.out('Paired with '+msg.from+'('+addr2url(remote)+')');
// Find waiting virtual link ...
self.links.forEach(function (link2,url) {
if (url=='*') return;
if (link2 && link2.state==AMState.AMS_AWAIT && link2.snd.name==link.snd.name) {
var newurl=addr2url(remote,true);
// Move link to new url
self.links[url]=undefined;
self.links[newurl]=link2;
link2.state=AMState.AMS_NOTCONNECTED;
link2.snd.address=remote.address;
link2.snd.port=remote.port; // Correct address/port???
if (self.mode&AMMode.AMO_UNICAST) self.url=newurl,self;
// console.log('wakeup ',link);
}
});
// this.watchdog(true,true);
// This is the root link '*' performed the pairing on this destination side.
// Wait for next pairing...
link.state=AMState.AMS_RENDEZVOUS;
if (link.queue.pairing.length)
link.snd = {name:link.queue.pairing.shift().snd.name};
else
link.snd = undefined;
self.watchdog(true);
}
break;
case 'punch':
if (msg.to == this.rcv.name) {
// send ACK
this.send({type:'ack',from:this.rcv.name},remote);
}
break;
}
}
/** Initialize AMP
*
*/
amp.udp.prototype.init = function(callback) {
if (callback) callback();
};
/** Negotiate (create) a virtual communication link (peer-to-peer).
*
* The link operation can be called externally establishing a connection or internally.
*
* In oneway mode only a destination endpoint is set and it is assumed the endpoint can receive messages a-priori!
*
* typeof @snd = address
* typeof @callback = function
* typeof @connect = boolean is indicating an initial connect request and not an acknowledge
* typeof @key = string
* +------------+
* VCMessageType (int16)
* Connection Port (port)
* Node ID (string)
* //Receiver IP Port (int32)
* +------------+
*
*/
amp.udp.prototype.link=function(snd,connect,key) {
var self = this,
url,
buf = Buf.Buffer(),
sock = this.sock; // snd_sock;
if (this.verbose>1) this.out('amp.link: to '+(snd && snd.address) + ':' + ((snd && snd.port) || '*'));
snd=this.updateLinkTable(snd||(this.url && this.links[this.url].snd) ,connect,key);
if (snd && snd.parameter && snd.parameter.secure) key=snd.parameter.secure;
if (!snd) return this.watchdog(true);
Buf.buf_put_int16(buf, magic);
Buf.buf_put_int16(buf, AMMessageType.AMMLINK);
Buf.buf_put_port(buf,this.port); // This AMP id
Buf.buf_put_string(buf,this.node?this.node.id:'*');
Buf.buf_put_string(buf,key?key:'');
// Buf.buf_put_int32(buf, this.rcv.port);
this.count.snd += Buf.length(buf);
this.count.lnk++;
sock.send(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) {
sock.close();
self.emit('error',err);
}
});
};
// Ask broker for registered hosts/nodes
amp.udp.prototype.lookup = function(path,callback) {
var link=this.links['*'];
if (!link && callback) return callback([]);
if (callback) link.queue.lookup[path]=callback;
this.send(
{type:'lookup',name: this.rcv.name, linfo: this.rcv, data:path },
this.broker,
function () {}
);
}
// Return link for destination
amp.udp.prototype.lookupLinkTable=function(snd) {
if (this.url) return this.links[this.url]; // Unicast mode
if (!snd) return;
var url = obj2url(snd);
return this.links[url];
}
// Initiate a pairing of two nodes via broker handled by the '*' root link
amp.udp.prototype.pairing = function(link) {
if (!this.links['*'].snd) {
// Root link will perform pairing ...
this.links['*'].snd={name:link.snd.name};
this.watchdog(true);
} else {
this.links['*'].queue.pairing.push(link);
}
}
/**
*
* typeof @snd = address
* typeof @callback = function
*
* +------------+
* AMMessageType (int16)
* Connection Port (port)
* Receiver IP Port (int32)
* +------------+
*/
amp.udp.prototype.ping=function(snd) {
var self = this,
buf = Buf.Buffer(),
sock = this.sock; // snd_sock;
Buf.buf_put_int16(buf, magic);
Buf.buf_put_int16(buf, AMMessageType.AMMPING);
Buf.buf_put_port(buf,this.port);
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
}
if (snd == undefined) this.err('amp.udp.ping: no destinataion set (snd==null)');
// Buf.buf_put_int32(buf, self.rcv.port);
if (this.verbose>1) this.out('amp.ping: to '+addr2url(snd));
this.count.snd += Buf.length(buf);
this.count.png++;
sock.send(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) {
sock.close();
self.emit('error',err);
}
});
};
/**
*
* typeof @snd = address
* typeof @callback = function
* +------------+
* AMMessageType (int16)
* Connection Port (port)
* Receiver IP Port (int32)
* +------------+
*/
amp.udp.prototype.pong=function(snd) {
var self = this,
buf = Buf.Buffer(),
sock = this.sock; // snd_sock;
Buf.buf_put_int16(buf, magic);
Buf.buf_put_int16(buf, AMMessageType.AMMPONG);
Buf.buf_put_port(buf,this.port);
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
}
if (snd == undefined) this.err('amp.udp.pong: no destinataion set (snd==null)');
// Buf.buf_put_int32(buf, this.rcv.port);
if (this.verbose>1) this.out('amp.pong: to '+addr2url(snd));
this.count.snd += Buf.length(buf);
this.count.png++;
sock.send(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) {
sock.close();
self.emit('error',err);
}
});
};
/*
** Set-up a message receiver.
**
** Message structure
**
** msgtyp msgtyp 2
** =AMMRPCHEAD =AMMRPCDATA
** tid tid 2
** remoteport 2
** cmd 2
** size 2
** frags 2
** off 4
** size 2
** more 2
** buf *
**
**
*/
/** Message receiver and handler.
* typeof @rcv=address|undefined
*/
amp.udp.prototype.receiver = function (callback,rcv) {
var self = this;
if (rcv == undefined || rcv.address==undefined) rcv={},rcv.address=this.rcv.address;
if (rcv.port==undefined) rcv.port=this.rcv.port;
if (callback) this.callback=callback;
var buf = Buf.Buffer();
var sock = this.sock; // rcv_sock;
sock.on('listening', function () {
var address = sock.address();
if (!rcv.port) self.rcv.port=rcv.port=address.port;
if (self.verbose>1) self.out('UDP receiver listening on ' + addr2url(rcv));
if (self.dir.ip=='*') self.dir=Aios.DIR.IP(self.rcv.port);
// Try to get network IP address of this host
getNetworkIP(undefined,function (err,ip) {
if (!err) self.rcv.address=ip;
if (self.verbose) self.out('IP port '+addr2url(self.rcv)+ ' (proto '+self.options.proto+')');
if (err) return self.out("! Unable to obtain network connection information: "+err);
});
});
sock.on('error', function (err) {
Io.out('[AMP] UDP error: '+err);
self.sock.close();
});
sock.on('message', function (message, remote) {
var handler,dfrags,dlist,msgtyp,tid,ipport,discard,off,size,thisnum,transaction,more,port,addr,url,data,msg;
remote.address=remote.address.replace(/^::ffff:/,'')
Buf.buf_init(buf);
Buf.buf_of_str(buf,message);
self.count.rcv += message.length;
msg={};
if (message.length >= 10) {
msg.magic=Buf.buf_get_int16(buf);
// consistency check
if (msg.magic!=magic) return;
msg.type=Buf.buf_get_int16(buf);
discard=false;
if (self.verbose>1) {
url=addr2url(remote,true);
self.out('receiver: Receiving Message from '+ url + ' [' + message.length+'] '+
AMMessageType.print(msg.type)+' in state '+
(self.mode&AMMode.AMO_MULTICAST?(self.links[url] && AMState.print(self.links[url].state)):
(self.links[self.url] && AMState.print(self.links[self.url].state))));
}
switch (msg.type) {
case AMMessageType.AMMRPCHEAD:
if (!self.checkState(AMState.AMS_CONNECTED,remote)) return;
msg.tid = Buf.buf_get_int16(buf);
msg.port = Buf.buf_get_port(buf);
msg.cmd=Buf.buf_get_int16(buf);
msg.size=Buf.buf_get_int32(buf); // total data size
msg.frags=Buf.buf_get_int16(buf);
msg.data=Buf.Buffer();
self.handle(msg,remote);
break;
case AMMessageType.AMMRPCDATA:
if (!self.checkState(AMState.AMS_CONNECTED,remote)) return;
msg.tid = Buf.buf_get_int16(buf);
msg.port = Buf.buf_get_port(buf);
msg.off = Buf.buf_get_int32(buf);
msg.size = Buf.buf_get_int16(buf); // fragment size
msg.more = Buf.buf_get_int16(buf);
msg.data = buf;
self.handle(msg,remote);
break;
case AMMessageType.AMMPING:
msg.port = Buf.buf_get_port(buf);
self.handle(msg,remote);
break;
case AMMessageType.AMMPONG:
msg.port = Buf.buf_get_port(buf);
self.handle(msg,remote);
break;
case AMMessageType.AMMLINK:
msg.port = Buf.buf_get_port(buf);
msg.node = Buf.buf_get_string(buf);
msg.secure = Buf.buf_get_string(buf);
if (msg.secure!='') msg.secure=Sec.Port.ofString(msg.secure);
self.handle(msg,remote);
break;
case AMMessageType.AMMUNLINK:
msg.port = Buf.buf_get_port(buf);
self.handle(msg,remote);
break;
// optional rendezvous brokerage
case AMMessageType.AMMCONTROL:
// Control message;
msg.port = Buf.buf_get_port(buf);
msg.data = Buf.buf_get_string(buf);
self.handle(msg,remote);
break;
}
}
});
};
/** Send a request message to a remote node endpoint
*
* function (cmd:integer,msg:Buffer,snd?:address)
*/
amp.udp.prototype.request = function (cmd,msg,snd) {
var self=this,
buf = Buf.Buffer(),
sock = this.sock, // snd_sock;
size = msg.data.length,
frags = div((size+self.dlimit-1),self.dlimit),
tid = msg.tid||Comp.random.int(65536/2);
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
}
if (snd == undefined) this.err('amp.udp.request: no destinataion set(snd==null)');
Buf.buf_put_int16(buf,magic);
Buf.buf_put_int16(buf,AMMessageType.AMMRPCHEAD);
Buf.buf_put_int16(buf,tid); // Transaction Message ID
Buf.buf_put_port(buf,this.port);
// Buf.buf_put_int16(buf,self.rcv.port); // For reply
Buf.buf_put_int16(buf,cmd);
Buf.buf_put_int32(buf,size);
Buf.buf_put_int16(buf,frags);
if (self.verbose>1) self.out('Send AMMRPCHEAD tid='+tid+' @'+Comp.pervasives.mtime());
this.count.snd += Buf.length(buf);
sock.send(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (self.verbose>1) self.out('Send AMMRPCHEAD tid='+tid+'. Done @'+Comp.pervasives.mtime());
if (err) {
if (self.verbose>1) self.out('AMMRPCHEAD Error: '+err);
sock.close();
if (callback) callback(Status.STD_IOERR,err);
} else {
if (size >0) {
var dsend = function (n, off) {
var fsize,more;
if (frags == 1) fsize = size;
else if (n < frags) fsize = self.dlimit;
else fsize = size - off;
if (n==frags) more=0; else more=1;
Buf.buf_init(buf);
Buf.buf_put_int16(buf, magic);
Buf.buf_put_int16(buf, AMMessageType.AMMRPCDATA);
Buf.buf_put_int16(buf, tid); // Transaction Message number
Buf.buf_put_port(buf,self.port);
Buf.buf_put_int32(buf, off); // Data fragment offset
Buf.buf_put_int16(buf, fsize); // Data fragment size
Buf.buf_put_int16(buf, more); // More data?
Buf.buf_put_buf(buf, msg, off, fsize);
if (self.verbose>1) self.out('Send AMMRPCDATA tid='+tid+'. Start #'+n+'/'+frags+' @'+Comp.pervasives.mtime());
self.count.snd += Buf.length(buf);
sock.send(buf.data, 0, Buf.length(buf), snd.port, snd.address, function (err) {
if (self.verbose>1) self.out('Send AMMRPCDATA tid='+tid+'. Done #'+n+'/'+frags+' @'+Comp.pervasives.mtime());
if (err) {
if (self.verbose>1) self.out('AMMRPCDATA Error: '+err);
sock.close();
self.emit('error',err);
}
else if (n < frags) dsend(n + 1, off + fsize);
});
};
dsend(1,0);
}
}
});
};
/** Reply to a request (msg.tid contains request tid)
*/
amp.udp.prototype.reply = function (cmd,msg,snd) {
this.request(cmd,msg,snd);
}
// typeof @snd : {address,port}
amp.udp.prototype.scan=function(snd,response,callback) {
var self = this,msg={magic:magic};
msg.type=response?AMMessageType.AMMACK:AMMessageType.AMMSCAN;
if (this.verbose>1) this.out('amp.scan: to '+addr2url(snd));
// TODO: callback!? Must be handled by the receiver/pkt. manager
// TODO ??
// this.send(msg,snd);
}
// Send a short control message
// typeof @msg : {type:string,..}
// typeof @snd : {address,port}
amp.udp.prototype.send = function (msg, snd) {
var buf = Buf.Buffer(),
sock = this.sock, // snd_sock;
data = JSON.stringify(msg);
this.LOG('snd',msg);
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
}
if (snd == undefined) this.err('amp.udp.send: no destinataion set (snd==null)');
if (this.verbose>1) this.out('amp.send: to '+addr2url(snd)+': '+data);
Buf.buf_put_int16(buf,magic);
Buf.buf_put_int16(buf,AMMessageType.AMMCONTROL);
Buf.buf_put_port(buf,this.port);
Buf.buf_put_string(buf,data);
this.count.snd += Buf.length(buf);
sock.send(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) self.emit('error',err);
});
};
// Start AMP watchdog and receiver
amp.udp.prototype.start = function(callback) {
var self=this,link,startwatch=false
s=this.secure?' (security port '+Sec.Port.toString(this.secure)+')':'';
if (this.verbose>0) this.out('Starting ' + (this.rcv.name?(this.rcv.name+' '):'')+ addr2url(this.rcv)+
(this.mode&AMMode.AMO_UNICAST && this.url?(' -> '+this.url):'')+
' ['+AMMode.print(this.mode)+'] (proto '+this.proto+')'+s);
if (this.mode&AMMode.AMO_UNICAST) {
if (this.url) {
link=this.links[this.url];
link.state = this.broker?AMState.AMS_AWAIT:AMState.AMS_NOTCONNECTED;
if (link.snd && !(this.mode&AMMode.AMO_ONEWAY))
startwatch=true;
if (link.snd && (this.mode&AMMode.AMO_ONEWAY))
this.emit('route+',addr2url(link.snd,true));
if (this.broker) this.pairing(link);
}
}
if (this.broker) {
startwatch=true;
// TODO init link['*'].snd / state ..
}
if (startwatch) this.watchdog(true);
if (!this.sock) {
// after stop?
this.sock=dgram.createSocket("udp4");
// restart listener
this.receiver();
}
this.sock/*rcv_sock*/.bind(this.rcv.port, undefined /*this.rcv.address*/, function (arg) {
if (callback) callback();
});
}
// Stop AMP
amp.udp.prototype.stop = function(callback) {
if (this.mode&AMMode.AMO_MULTICAST)
for(var p in this.links) {
if (this.links[p]) {
// Try to unlink remote endpoint
this.unlink(this.links[p].snd);
this.links[p].state=AMState.AMS_NOTCONNECTED;
}
}
else
this.links.state = AMState.AMS_NOTCONNECTED;
if (this.timer) clearTimeout(this.timer),this.timer=undefined;
if (this.sock) this.sock.close(),this.sock=undefined;
if (callback) callback();
}
// Unlink remote endpoint
amp.udp.prototype.unlink=function(snd) {
var self = this,
buf = Buf.Buffer(),
url = snd?addr2url(snd,true):null,
sock = this.sock; //snd_sock;
if (!this.links[url||this.url] || this.links[url||this.url].state!=AMState.AMS_CONNECTED) return;
this.emit('route-',addr2url(snd,true));
if (this.mode&AMMode.AMO_ONEWAY) return;
Buf.buf_put_int16(buf, magic);
Buf.buf_put_int16(buf, AMMessageType.AMMUNLINK);
Buf.buf_put_port(buf,this.port);
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
url=this.url;
}
if (snd == undefined) this.err('amp.udp.unlink: no destination (snd==null)');
// Buf.buf_put_int32(buf, this.rcv.port);
if (this.verbose>1) this.out('amp.unlink: to '+addr2url(snd));
this.count.snd += Buf.length(buf);
sock.send(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) {
sock.close();
self.emit('error',err)
}
});
this.links[url].state=AMState.AMS_NOTCONNECTED;
if (!this.links[url].snd.connect) this.links[url].snd={}; // Invalidate link - or remove it from table?
if (this.broker) {
// Special case: brokerage! Remove link entry entirely!?
this.links[url]=undefined;
if (this.url) this.url=undefined;
}
if (this.verbose) this.out('Unlinked ' + addr2url(snd));
};
// Update link table, add new entry, and return snd address (or none if the watchdog should handle the messaging)
amp.udp.prototype.updateLinkTable=function(snd,connect) {
var link;
if (!snd) this.err('amp.udp.link: no destinataion set (snd==null)');
url=addr2url(snd,true);
// Add new link to link table if not already existing
if (this.broker && !snd.port && !this.links[url]) {
// Initial broker rendezvous delivering endpoint ip address and port
link=this.links[url]={
state:AMState.AMS_AWAIT,
tries:0,
connect:connect,
live:options.AMC_MAXLIVE,
snd:{name:snd.address} // Watchdog will send link messages initially to broker if address is resolved
};
if (connect) link.snd.connect=true;
if (this.mode&AMMode.AMO_UNICAST) this.url=url; // Remember this link
this.pairing(link);
// Let watchdog handle rendezvous and connect request messages
return;
} else if (this.mode&AMMode.AMO_UNICAST) {
// UNICAST mode
if (!this.links[url]) link=this.links[url]={state:AMState.AMS_NOTCONNECTED};
else link=this.links[url];
if (snd != undefined && snd.address!=undefined && snd.port!=undefined && !link.snd)
link.snd=snd;
if (snd != undefined && snd.address!=undefined && snd.port!=undefined && snd.port!='*' && link.snd.address==undefined)
link.snd.address=snd.address;
if (snd != undefined && snd.port!=undefined && link.snd.port==undefined)
link.snd.port=snd.port;
if (connect) link.snd.connect=true;
// Nothing to do or let watchdog handle link messages?
if ((link.state && link.state!=AMState.AMS_NOTCONNECTED && link.state!=AMState.AMS_PAIRED) ||
this.mode&AMMode.AMO_ONEWAY) return;
// Random port range p0-p1? Let watchdog do the work
if (typeof link.snd.port == 'string') return;
// Send link message
if (snd==undefined || snd.address==undefined) snd={},snd.address=link.snd.address;
if (snd.port==undefined) snd.port=link.snd.port;
this.url=url; // Remember this link
} else {
// MULTICAST mode
if (!this.links[url] || !this.links[url].snd.address)
link=this.links[url]={
snd:snd,
state:AMState.AMS_NOTCONNECTED,
tries:0,
connect:connect,
live:options.AMC_MAXLIVE
};
// Let watchdog handle connect request link messages
if (!this.inwatchdog && connect) {
this.watchdog(true);
return;
}
// if (this.verbose>1) this.out('send link '+Io.inspect(snd));
}
return snd;
}
/** Install a watchdog timer.
*
* 1. If link state is AMS_NOTCONNECTED, retry link request if this.links[].snd is set.
* 2. If link state is AMS_CONNECTED, check link end point.
* 3, If link state is AMS_RENDEZVOUS, get remote endpoint connectivity via broker
*
* @param run
*/
amp.udp.prototype.watchdog = function(run,immed) {
var self=this;
if (this.timer) clearTimeout(self.timer),this.timer=undefined;
if (run) this.timer=setTimeout(function () {
var con,to,tokens;
if (!self.timer || !self.sock || self.inwatchdog) return; // stopped or busy?
self.timer = undefined;
self.inwatchdog=true;
function handle(obj,url) {
if (self.verbose>1) self.out('Watchdog: handle link '+
url+(obj.snd?('('+obj2url(obj.snd)+')'):'')+' in state '+AMState.print(obj.state)+
'['+obj.live+'] '+
(obj.tries!=undefined?('[#'+obj.tries+']'):''));
switch (obj.state) {
case AMState.AMS_CONNECTED:
if (obj.live == 0) {
// No PING received, disconnect...
if (self.verbose>0)
self.out('Endpoint ' + addr2url(obj.snd) +
' not responding, propably dead. Unlinking...');
// self.emit('route-',addr2url(obj.snd)) .. done in unlink
if (self.mode&AMMode.AMO_MULTICAST) self.unlink(obj.snd);
else self.unlink();
obj.state = AMState.AMS_NOTCONNECTED;
if (!obj.snd.connect) obj.snd={};
if (self.broker) {
// Re-register on broker for rendezvous ...
self.watchdog(true);
if (self.links['*']) {
self.links['*'].state=AMState.AMS_RENDEZVOUS;
}
}
} else {
obj.tries=0;
obj.live--;
self.watchdog(true);
if (self.mode&AMMode.AMO_MULTICAST) self.ping(obj.snd);
else self.ping();
}
break;
case AMState.AMS_NOTCONNECTED:
if (!obj.snd) return;
if (obj.snd.port && typeof obj.snd.port == 'string') {
// Random port connection from a port range p0-p1; save it and start with first
// random selection
tokens=obj.snd.port.split('-');
if (tokens.length==2) obj.range=[Number(tokens[0]),Number(tokens[1])];
}
if (obj.range) {
// Get a random port from range
obj.snd.port=Comp.random.interval(obj.range[0],obj.range[1]);
if (self.verbose>0)
self.out('Trying link to ' + addr2url(obj.snd));
if (self.mode&AMMode.AMO_MULTICAST) self.link(obj.snd);
else self.link();
obj.tries++;
if (obj.tries < options.TRIES) self.watchdog(true);
else {
obj.snd={},obj.tries=0,obj.range=undefined;
}
} else if (obj.snd.port && typeof obj.snd.port == 'number') {
// Try link to specified remote endpoint obj.snd
if (self.verbose>0 && obj.tries==0)
self.out('Trying link to ' + addr2url(obj.snd));
if (self.mode&AMMode.AMO_MULTICAST) self.link(obj.snd);
else self.link();
obj.tries++;
if (obj.tries < options.TRIES) self.watchdog(true);
else {
self.out('Giving up to link '+addr2url(obj.snd));
self.emit('error','link',addr2url(obj.snd,true));
obj.snd={},obj.tries=0;
}
}
break;
// AMP Broker P2P Control and Management
case AMState.AMS_RENDEZVOUS:
obj.next=Aios.time()+options.REGTMO;
obj.interval=options.REGTMO;
self.send(
{type:'register',name: self.rcv.name, linfo: self.rcv},
self.broker,
function () {}
);
self.watchdog(true);
break;
case AMState.AMS_REGISTERED:
if (obj.snd && obj.snd.name && obj.tries < options.TRIES) {
obj.tries++;
self.send(
{type:'pair', from:self.rcv.name, to: obj.snd.name},
self.broker,
function () {}
);
// self.watchdog(true);
} else if (options.REGTMO && Aios.time() > obj.next) {
// Update registration periodically; messages can be lost
obj.interval *= 2;
obj.interval = Math.min(obj.interval,options.REGTMO*8);
obj.next=Aios.time()+obj.interval;
self.send(
{type:'register',name: self.rcv.name, linfo: self.rcv},
self.broker,
function () {}
);
}
self.watchdog(true);
break;
}
}
for(var p in self.links) if (self.links[p]) handle(self.links[p],p);
self.inwatchdog=false;
},immed?0:options.TIMER);
};
};
BundleModuleCode['jam/ampTCP']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 02-11-18 by sbosse.
** $RCS: $Id: ampTCP.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.19.1
**
** $INFO:
**
** JAM Agent Management Port (AMP) over TCP
** Three modes:
** (A) Pair of ad-hoc connections for each message
** (B) Pair of permanent connections with message stream; keepAlive:true
** (C) Single permanent connection from "client" to "server", i.e., an AMP handler for each socket, sharedSocket:true
** => for client behind NATs?
**
** Supports:
**
** - Unicast link
** - Multicast link
**
** Default mode A: A TCP connection is opened each time a message has to be sent and closed after transfer is finished.
** (Workaround for wine+wineserver - WIN32 node.js / nw.js bug, probably a libuv wine bug)
**
** Simplified data transfer: No requirement for data fragmentation!
**
** Events out: 'error','route+','route-'
**
** TODO:
** - Garbage collection
**
** New:
** - No size limit of data transfers
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Lz = Require('os/lz-string');
var Comp = Require('com/compat');
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var Sec = Require('jam/security');
var Command = Net.Command;
var Status = Net.Status;
var current=none;
var Aios=none;
var CBL = Require('com/cbl');
var COM = Require('jam/ampCOM'),
AMMode=COM.AMMode,
AMMessageType=COM.AMMessageType,
AMState=COM.AMState,
amp=COM.amp,
options=COM.options,
url2addr=COM.url2addr,
addr2url=COM.addr2url,
obj2url=COM.obj2url,
addrequal=COM.addrequal,
resolve=COM.resolve,
ipequal=COM.ipequal,
doUntilAck=COM.doUntilAck,
getNetworkIP=COM.getNetworkIP,
magic=COM.options.magic;
module.exports.current=function (module) { current=module.current; Aios=module; };
var net = require('net');
var dgram = Require('dgram');
/** AMP port using TCP
* ==================
*
* type url = string
* type amp.tcp = function (options:{rcv:address,snd?:address,verbose?,logging?,out?:function,log?:number,broker?:address})
* type address = {address:string,port:number}
*/
amp.tcp = function (options) {
var self=this;
options=checkOptions(options,{});
this.options=options;
this.proto = 'tcp';
// Some sanity checks
if (options.oneway && options.multicast) this.err('Invalid: Both ONEWAY and MULTICAST modes enabled!');
this.verbose=checkOption(options.verbose,0);
if (!options.rcv) options.rcv=url2addr('localhost:*');
if (this.verbose>2) console.log(options);
this.dir = options.dir; // attached to JAM port
this.rcv = options.rcv; // IP (this side) address
this.broker = options.broker; // IP (rendezvous broker) address
this.node = options.node; // Attached to this node
this.port = options.port||Net.uniqport(); // This AMP Port
this.secure = this.options.secure;
this.client = {};
this.keepAlive = checkOption(options.keepAlive,false);
this.sharedSocket = checkOption(options.sharedSocket,false);
if (this.broker && !this.broker.port)
Io.err('[AMP] No broker port specified!');
this.out = function (msg) {
Aios.print('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] '+msg);
}
this.err = function (msg) {
Aios.print('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] Error: '+msg);
throw 'AMP';
}
// Virtual link table
// MULTICAST (P2N) mode: link cache [{addr,live,state}] of all connected links
// UNICAST (P2P): One link only, remebered by this.url!
this.links={};
if (options.snd) {
url=addr2url(options.snd,true);
this.links[url]={
snd:options.snd,
tries:0,
state:this.broker?AMState.AMS_AWAIT:AMState.AMS_NOTCONNECTED,
live:COM.AMC_MAXLIVE
};
if (this.verbose>0) this.out('Added destiantion route '+url+', '+Io.inspect(this.links[url]));
if (!options.multicast) this.url=url; // Remember this link
}
if (this.broker) {
// Create a root path that handles all brokerag and pairing
url='*';
this.links[url]={
tries:0,
state:AMState.AMS_RENDEZVOUS,
live:COM.options.AMC_MAXLIVE,
queue:{pairing:[],lookup:[]}
};
if (this.verbose>0) this.out('Added default registration route '+url);
}
this.rcv.name=options.name; // Optional name of this port
this.mode = options.multicast?AMMode.AMO_MULTICAST:AMMode.AMO_UNICAST;
if (options.oneway) this.mode |= AMMode.AMO_ONEWAY; // Oneway: No link negotation; no ack.
this.sock = undefined; // Receiver socket
this.count={rcv:0,snd:0,lnk:0,png:0};
this.timer=undefined;
this.inwatchdog=false;
this.events = [];
this.transactions = Comp.hashtbl.create();
this.logs=[];
this.logging=options.logging||false;
if (this.logging) {
setInterval(function () { self.LOG('print') },5000);
}
};
amp.tcp.prototype.LOG = amp.man.prototype.LOG;
amp.tcp.prototype.addTransaction = amp.man.prototype.addTransaction;
amp.tcp.prototype.checkState = amp.man.prototype.checkState;
amp.tcp.prototype.deleteTransaction = amp.man.prototype.deleteTransaction;
amp.tcp.prototype.emit = amp.man.prototype.emit;
amp.tcp.prototype.findTransaction = amp.man.prototype.findTransaction;
amp.tcp.prototype.handle = amp.man.prototype.handle;
amp.tcp.prototype.on = amp.man.prototype.on;
amp.tcp.prototype.status = amp.man.prototype.status;
/** Initialize AMP
*
*/
amp.tcp.prototype.init = function(callback) {
if (callback) callback();
};
/** Negotiate (create) a virtual communication link (peer-to-peer).
*
* The link operation can be called externally establishing a connection or internally.
*
* In oneway mode only a destination endpoint is set and it is assumed the endpoint can receive messages a-priori!
*
* typeof @snd = address
* typeof @callback = function
* typeof @connect = boolean is indicating an initial connect request and not an acknowledge
* typeof @key = string
* +------------+
* VCMessageType (int16)
* Connection Port (port)
* Node ID (string)
* Receiver IP Port (int32)
* +------------+
*
*/
amp.tcp.prototype.link=function(snd,connect,key) {
var self = this,
url = addr2url(snd,true),
buf = Buf.Buffer();
if (this.verbose>1) this.out('amp.link: to '+(snd && snd.address) + ':' + ((snd && snd.port) || '*'));
snd=this.updateLinkTable(snd||(this.url && this.links[this.url].snd) ,connect,key);
if (snd && snd.parameter && snd.parameter.secure) key=snd.parameter.secure;
if (!snd) return this.watchdog(true);
// Create sockets here for permanent TCP sessions
if ((this.keepAlive || this.sharedSocket) &&
(!this.client[url] || !this.client[url].socket)) {
if (!this.client[url]) this.client[url]={busy:false,id:Math.random(),connected:false,queue:[]}
if (!this.client[url].socket) {
if (self.verbose>1) console.log('amp.link Creating keepAlive socket for client '+url);
this.client[url].socket=new net.Socket();
this.client[url].connected=false;
this.client[url].socket.on('close', function () {
console.log('close',url);
delete self.client[url];
});
this.client[url].socket.on('error', function (err) {
if (self.verbose>1) console.log('error',url,err)
delete self.client[url];
});
}
if (this.sharedSocket && !this.rcv.port) {
// TODO bind receiver
if (self.verbose>1) console.log('amp.link Creating sharedSocket receiver for client '+url);
this.receiverSocket(this.client[url].socket);
if (!this.inwatchdog) {
this.watchdog(true);
}
}
this.client[url].busy=true;
console.log('Connecting to client '+url);
this.client[url].socket.connect(snd.port,snd.address, function () {
var rcv=self.sharedSocket?self.client[url].socket.address():self.rcv;
if (self.verbose>1) console.log('amp.link.connect:',url,rcv)
self.client[url].rcv=rcv;
self.client[url].busy=false;
send(self.client[url].rcv.port);
});
this.client[url].connected=true;
return;
}
function send(rcvport) {
if (self.verbose>1) self.out('amp.link.send: '+url+' rcvport='+rcvport);
Buf.buf_put_int16(buf, magic);
Buf.buf_put_int16(buf, AMMessageType.AMMLINK);
Buf.buf_put_port(buf,self.port); // This AMP id
Buf.buf_put_string(buf,self.node?self.node.id:'*');
Buf.buf_put_string(buf,key?key:'');
Buf.buf_put_int32(buf, rcvport);
// Buf.buf_put_int32(buf, this.rcv.port);
self.count.snd += Buf.length(buf);
self.count.lnk++;
self.write(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) {
// self.close();
self.emit('error',err);
}
});
}
send(this.sharedSocket && this.client[url] && this.client[url].rcv?this.client[url].rcv.port:this.rcv.port);
};
// Ask broker for registered hosts/nodes
amp.tcp.prototype.lookup = function(path,callback) {
var link=this.links['*'];
if (!link && callback) return callback([]);
if (callback) link.queue.lookup[path]=callback;
this.send(
{type:'lookup',name: this.rcv.name, linfo: this.rcv, data:path },
this.broker,
function () {}
);
}
// Return link for destination
amp.tcp.prototype.lookupLinkTable=function(snd) {
if (this.url) return this.links[this.url]; // Unicast mode
if (!snd) return;
var url = obj2url(snd);
return this.links[url];
}
/**
*
* typeof @snd = address
* typeof @callback = function
*
* +------------+
* AMMessageType (int16)
* Connection Port (port)
* Receiver IP Port (int32)
* +------------+
*/
amp.tcp.prototype.ping=function(snd) {
var self = this,
buf = Buf.Buffer();
Buf.buf_put_int16(buf, magic);
Buf.buf_put_int16(buf, AMMessageType.AMMPING);
Buf.buf_put_port(buf,this.port);
if (!this.sharedSocket)
Buf.buf_put_int32(buf,this.rcv.port); // For reply
else // from this.client[url]
Buf.buf_put_int32(buf,this.client[url] && this.client[url].rcv?this.client[url].rcv.port:this.rcv.port); // For reply
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
}
if (snd == undefined) this.err('ping: snd=null');
// Buf.buf_put_int32(buf, self.rcv.port);
if (this.verbose>1) this.out('amp.ping: to '+addr2url(snd));
this.count.snd += Buf.length(buf);
this.count.png++;
this.write(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) {
// self.close();
self.emit('error',err);
}
});
};
/**
*
* typeof @snd = address
* typeof @callback = function
* +------------+
* AMMessageType (int16)
* Connection Port (port)
* Receiver IP Port (int32)
* +------------+
*/
amp.tcp.prototype.pong=function(snd) {
var self = this,
buf = Buf.Buffer();
Buf.buf_put_int16(buf, magic);
Buf.buf_put_int16(buf, AMMessageType.AMMPONG);
Buf.buf_put_port(buf,this.port);
if (!this.sharedSocket)
Buf.buf_put_int32(buf,this.rcv.port); // For reply
else // from this.client[url]
Buf.buf_put_int32(buf,this.client[url] && this.client[url].rcv?this.client[url].rcv.port:this.rcv.port); // For reply
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
}
if (snd == undefined) this.err('pong: snd=null');
// Buf.buf_put_int32(buf, this.rcv.port);
if (this.verbose>1) this.out('amp.pong: to '+addr2url(snd));
this.count.snd += Buf.length(buf);
this.count.png++;
this.write(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) {
// self.close();
self.emit('error',err);
}
});
};
/*
** Set-up a message receiver.
**
** Message structure
**
** msgtyp msgtyp 2
** =AMMRPCHEAD =AMMRPCDATA
** tid tid 2
** remoteport 2
** cmd 2
** size 2
** frags 2
** off 4
** size 2
** more 2
** buf *
**
**
*/
/** Message receiver and handler.
* typeof @rcv=address|undefined
*/
amp.tcp.prototype.receiverHandler = function handler (message,remote) {
var self=this,handler,dfrags,dlist,msgtyp,tid,ipport,discard,off,size,
thisnum,transaction,more,port,addr,url,data,msg,
url0=addr2url(remote,true);
// console.log('handler',message.length);
var buf = Buf.Buffer();
Buf.buf_init(buf);
Buf.buf_of_str(buf,message);
self.count.rcv += message.length;
msg={};
if (message.length >= 10) {
msg.magic=Buf.buf_get_int16(buf);
// consistency check
if (msg.magic!=magic) return;
msg.type=Buf.buf_get_int16(buf);
discard=false;
if (self.verbose>1) {
url=addr2url(remote,true);
self.out('receiver: Receiving Message from '+ url + ' [' + message.length+'] '+
AMMessageType.print(msg.type));
}
switch (msg.type) {
case AMMessageType.AMMRPCHEADDATA: // single message transfers only
msg.tid = Buf.buf_get_int16(buf);
msg.port = Buf.buf_get_port(buf);
msg.sndport = Buf.buf_get_int32(buf);
remote.port=msg.sndport; // reply to this port!
url=addr2url(remote,true);
if (self.verbose>1)self.out('receiver: Receiving Message in state '+
(self.mode&AMMode.AMO_MULTICAST?(self.links[url] && AMState.print(self.links[url].state)):
(self.links[self.url] && AMState.print(self.links[self.url].state))));
if (!self.checkState(AMState.AMS_CONNECTED,remote)) return;
msg.cmd=Buf.buf_get_int16(buf);
msg.size=Buf.buf_get_int32(buf);
msg.data = buf;
msg.frags=0;
msg.more=false;
self.handle(msg,remote);
break;
case AMMessageType.AMMPING:
msg.port = Buf.buf_get_port(buf);
msg.sndport = Buf.buf_get_int32(buf);
remote.port=msg.sndport; // reply to this port!
url=addr2url(remote,true);
if (self.verbose>1)self.out('receiver: Receiving Message in state '+
(self.mode&AMMode.AMO_MULTICAST?(self.links[url] && AMState.print(self.links[url].state)):
(self.links[self.url] && AMState.print(self.links[self.url].state))));
self.handle(msg,remote);
break;
case AMMessageType.AMMPONG:
msg.port = Buf.buf_get_port(buf);
msg.sndport = Buf.buf_get_int32(buf);
remote.port=msg.sndport; // reply to this port!
url=addr2url(remote,true);
if (self.verbose>1)self.out('receiver: Receiving Message in state '+
(self.mode&AMMode.AMO_MULTICAST?(self.links[url] && AMState.print(self.links[url].state)):
(self.links[self.url] && AMState.print(self.links[self.url].state))));
self.handle(msg,remote);
break;
case AMMessageType.AMMLINK:
msg.port = Buf.buf_get_port(buf);
msg.node = Buf.buf_get_string(buf);
msg.secure = Buf.buf_get_string(buf);
if (msg.secure!='') msg.secure=Sec.Port.ofString(msg.secure);
msg.sndport = Buf.buf_get_int32(buf);
var oldport = remote.port;
remote.port=msg.sndport; // reply to this port!
url=addr2url(remote,true);
if (remote.port!=oldport) {
// client behind NAT? IP port changed
if (self.verbose>1) console.log('LINK NAT fix',remote.port,oldport,url,url0,typeof self.client[url0])
if (self.client[url0]) {
// migrate client entry to new url
self.client[url]={address:remote.address,port:remote.port,
busy:self.client[url0].busy,
connected:self.client[url0].connected,
queue:self.client[url0].queue,
socket:self.client[url0].socket};
}
}
if (self.verbose>1)self.out('receiver: Receiving Message in state '+
(self.mode&AMMode.AMO_MULTICAST?(self.links[url] && AMState.print(self.links[url].state)):
(self.links[self.url] && AMState.print(self.links[self.url].state))));
self.handle(msg,remote);
break;
case AMMessageType.AMMUNLINK:
msg.port = Buf.buf_get_port(buf);
msg.sndport = Buf.buf_get_int32(buf);
remote.port=msg.sndport; // reply to this port!
url=addr2url(remote,true);
self.handle(msg,remote);
break;
// optional rendezvous brokerage
case AMMessageType.AMMCONTROL:
// Control message;
msg.port = Buf.buf_get_port(buf);
msg.data = Buf.buf_get_string(buf);
self.handle(msg,remote);
break;
}
}
};
amp.tcp.prototype.receiverSocket = function (sock) {
var self=this,chunks,remote,expect=0,url;
// Remote connect and request
sock.on('data', function (data) {
var pending;
// console.log(data);
if (!remote) {
remote = {address:sock.remoteAddress.replace(/^::ffff:/,''),port:sock.remotePort};
url = addr2url(remote);
// console.log(remote,url)
if (self.sharedSocket && !self.client[url]) {
self.client[url]={busy:false,connected:true,socket:sock,queue:[]};
}
}
if (self.keepAlive || self.sharedSocket) {
// still broken
do {
if (pending) { data=pending; pending=null };
if (data.length==0) process.exit();
// message stream connectioN; first 4 bytes: length of message
if (data.length==4) { expect=data.readInt32LE(0); return; }
else if (expect==0) {
if (data.length<4) return console.log('uff',data.length);
var dataLength = data.slice(0,4);
data=data.slice(4);
expect=dataLength.readInt32LE(0);
}
if (expect) {
if (chunks && (data.length+chunks.length) > expect ||
data.length > expect) {
var diff = (data.length+(chunks?chunks.length:0)) - expect,
need = expect-(chunks?chunks.length:0);
// console.log('mess',expect,diff,need,chunks && chunks.length,data.length)
pending=data.slice(need);
data=data.slice(0,need);
// console.log(data.length,pending.length);
}
}
if (!chunks) chunks=data;
else chunks=Buffer.concat([chunks,data]);
if (expect && chunks.length == expect) {
// console.log(chunks)
self.receiverHandler(Buffer(chunks),remote);
expect=0;
chunks=null;
}
} while (pending && pending.length)
} else {
if (!chunks) chunks=data;
else chunks=Buffer.concat([chunks,data]);
}
});
sock.on('end', function () {
if (chunks) self.receiverHandler(Buffer(chunks),remote);
});
if (!this.sharedSocket) {
// Add a 'close' event handler to this instance of socket
sock.on('close', function(data) {
// console.log('CLOSED: ' + sock.remoteAddress +' '+ sock.remotePort);
if (self.sharedSocket && self.client[url]) delete self.client[url];
});
sock.on('error', function(data) {
// console.log('CLOSED: ' + sock.remoteAddress +' '+ sock.remotePort);
if (self.sharedSocket && self.client[url]) delete self.client[url];
});
}
}
amp.tcp.prototype.receiver = function (callback,rcv) {
var self = this;
if (rcv == undefined || rcv.address==undefined) rcv={},rcv.address=this.rcv.address;
if (rcv.port==undefined) rcv.port=this.rcv.port;
if (callback) this.callback=callback;
if (this.sharedSocket && rcv.port==undefined) {
if (this.verbose) this.out('IP port * (proto '+this.options.proto+') SS');
return; // client side socket receiver, installed later on each link request
}
this.sock=net.createServer({keepAlive:this.keepAlive,noDelay:true},this.receiverSocket.bind(this));
this.sock.on('listening', function () {
var address = self.sock.address();
if (!rcv.port) self.rcv.port=rcv.port=address.port;
if (self.verbose>1) self.out('TCP receiver listening on ' + addr2url(rcv));
if (self.dir.ip=='*') self.dir=Aios.DIR.IP(self.rcv.port);
// Try to get network IP address of this host
getNetworkIP(undefined,function (err,ip) {
if (!err) self.rcv.address=ip;
if (self.verbose) self.out('IP port '+addr2url(self.rcv)+ ' (proto '+self.options.proto+') '+
(self.keepAlive?'KA ':'')+
(self.sharedSocket?'SS':''));
if (err) return self.out("! Unable to obtain network connection information: "+err);
});
});
this.sock.on('error', function (err) {
Io.out('[AMP] TCP error: '+err);
self.sock.close();
});
};
/** Send a request message to a remote node endpoint
*
* function (cmd:integer,msg:Buffer,snd?:address)
*/
amp.tcp.prototype.request = function (cmd,msg,snd) {
var self=this,
buf = Buf.Buffer(),
size = msg.data.length,
url = snd?addr2url(snd,true):'',
tid = msg.tid||Comp.random.int(65536/2);
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
}
if (snd == undefined) this.err('request: request=null');
Buf.buf_put_int16(buf,magic);
Buf.buf_put_int16(buf,AMMessageType.AMMRPCHEADDATA);
Buf.buf_put_int16(buf,tid); // Transaction Message ID
Buf.buf_put_port(buf,this.port);
if (!this.sharedSocket)
Buf.buf_put_int32(buf,this.rcv.port); // For reply
else // from this.client[url]
Buf.buf_put_int32(buf,this.client[url] && this.client[url].rcv?this.client[url].rcv.port:this.rcv.port); // For reply
Buf.buf_put_int16(buf,cmd);
Buf.buf_put_int32(buf,size);
Buf.buf_put_buf(buf, msg, 0, size);
if (self.verbose>1) self.out('Send AMMRPCHEAD tid='+tid+' @'+Comp.pervasives.mtime());
this.count.snd += Buf.length(buf);
this.write(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (self.verbose>1) self.out('Send AMMRPCHEADDATA tid='+tid+'. Done @'+Comp.pervasives.mtime());
if (err) {
if (self.verbose>1) self.out('AMMRPCHEADDATA Error: '+err);
if (callback) callback(Status.STD_IOERR,err);
}
});
};
/** Reply to a request (msg.tid contains request tid)
*/
amp.tcp.prototype.reply = function (cmd,msg,snd) {
this.request(cmd,msg,snd);
}
// Send a short control message
// typeof @msg : {type:string,..}
// typeof @snd : address
amp.tcp.prototype.send = function (msg, snd) {
var buf = Buf.Buffer(),
data = JSON.stringify(msg);
this.LOG('snd',msg);
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
}
if (snd == undefined) this.err('send: snd=null');
if (this.verbose>1) this.out('amp.send: to '+addr2url(snd)+': '+data);
Buf.buf_put_int16(buf,magic);
Buf.buf_put_int16(buf,AMMessageType.AMMCONTROL);
Buf.buf_put_port(buf,this.port);
Buf.buf_put_string(buf,data);
this.count.snd += Buf.length(buf);
this.write(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) self.emit('error',err);
});
};
// Start AMP watchdog and receiver
amp.tcp.prototype.start = function(callback) {
var self=this,link,startwatch=false,
s=this.secure?' (security port '+Sec.Port.toString(this.secure)+')':'';
if (this.verbose>0) this.out('Starting ' + (this.rcv.name?(this.rcv.name+' '):'')+ addr2url(this.rcv)+
(this.mode&AMMode.AMO_UNICAST && this.url?(' -> '+this.url):'')+
' ['+AMMode.print(this.mode)+'] (proto '+this.proto+') '+s);
if (this.mode&AMMode.AMO_UNICAST) {
if (this.url) {
link=this.links[this.url];
link.state = this.broker?AMState.AMS_AWAIT:AMState.AMS_NOTCONNECTED;
if (link.snd && !(this.mode&AMMode.AMO_ONEWAY))
startwatch=true;
if (link.snd && (this.mode&AMMode.AMO_ONEWAY))
this.emit('route+',addr2url(link.snd,true));
if (this.broker) this.pairing(link);
}
}
if (startwatch) this.watchdog(true);
if (!this.sock && !this.sharedSocket && this.rcv.port) {
// restart listener
this.receiver();
}
if (this.sock) {
this.sock.listen(this.rcv.port, undefined /*this.rcv.address*/);
}
if (callback) callback();
}
// Stop AMP
amp.tcp.prototype.stop = function(callback) {
if (this.mode&AMMode.AMO_MULTICAST)
for(var p in this.links) {
if (this.links[p]) {
// Try to unlink remote endpoint
this.unlink(this.links[p].snd);
this.links[p].state=AMState.AMS_NOTCONNECTED;
}
}
else
this.links.state = AMState.AMS_NOTCONNECTED;
if (this.timer) clearTimeout(this.timer),this.timer=undefined;
// TODO
if (this.sock) this.sock.close(),this.sock=undefined;
for (var p in this.client) {
if (this.client[p] && this.client[p].socket) {
this.client[p].socket.destroy();
delete this.client[p];
}
}
if (callback) callback();
}
// Unlink remote endpoint
amp.tcp.prototype.unlink=function(snd) {
var self = this,
buf = Buf.Buffer(),
url = snd?addr2url(snd,true):null;
if (!this.links[url||this.url] || this.links[url||this.url].state!=AMState.AMS_CONNECTED) return;
this.emit('route-',addr2url(snd,true));
if (this.mode&AMMode.AMO_ONEWAY) return;
Buf.buf_put_int16(buf, magic);
Buf.buf_put_int16(buf, AMMessageType.AMMUNLINK);
Buf.buf_put_port(buf,this.port);
if (!this.sharedSocket)
Buf.buf_put_int32(buf,this.rcv.port); // For reply
else // from this.client[url]
Buf.buf_put_int32(buf,this.client[url] && this.client[url].rcv?this.client[url].rcv.port:this.rcv.port); // For reply
if (this.mode&AMMode.AMO_UNICAST) {
if (snd==undefined || snd.address==undefined) snd={},snd.address=this.links[this.url].snd.address;
if (snd.port==undefined) snd.port=this.links[this.url].snd.port;
url=this.url;
}
if (snd == undefined) this.err('unlink: no destination');
// Buf.buf_put_int32(buf, this.rcv.port);
if (this.verbose>1) this.out('amp.unlink: to '+addr2url(snd));
this.count.snd += Buf.length(buf);
this.write(buf.data,0,Buf.length(buf),snd.port,snd.address,function (err) {
if (err) {
self.emit('error',err)
}
});
this.links[url].state=AMState.AMS_NOTCONNECTED;
if (!this.links[url].snd.connect) this.links[url].snd={}; // Invalidate link - or remove it from table?
if (this.broker) {
// Special case: brokerage! Remove link entry entirely!?
this.links[url]=undefined;
if (this.url) this.url=undefined;
}
if (this.client[url]) { this.client[url].socket.destroy(); delete this.client[url] };
if (this.verbose) this.out('Unlinked ' + url);
};
// Update link table, add new entry, and return snd address (or none if the watchdog should handle the messaging)
amp.tcp.prototype.updateLinkTable=function(snd,connect) {
var link;
if (!snd) this.err('link: no destinataion set');
url=addr2url(snd,true);
// Add new link to link table if not already existing
if (this.broker && !snd.port && !this.links[url]) {
// Initial broker rendezvous delivering endpoint ip address and port
link=this.links[url]={
state:AMState.AMS_AWAIT,
tries:0,
connect:connect,
live:options.AMC_MAXLIVE,
snd:{name:snd.address} // Watchdog will send link messages initially to broker if address is resolved
};
if (connect) link.snd.connect=true;
if (this.mode&AMMode.AMO_UNICAST) this.url=url; // Remember this link
this.pairing(link);
// Let watchdog handle rendezvous and connect request messages
return;
} else if (this.mode&AMMode.AMO_UNICAST) {
// UNICAST mode
if (!this.links[url]) link=this.links[url]={state:AMState.AMS_NOTCONNECTED};
else link=this.links[url];
if (snd != undefined && snd.address!=undefined && snd.port!=undefined && !link.snd)
link.snd=snd;
if (snd != undefined && snd.address!=undefined && snd.port!=undefined && snd.port!='*' && link.snd.address==undefined)
link.snd.address=snd.address;
if (snd != undefined && snd.port!=undefined && link.snd.port==undefined)
link.snd.port=snd.port;
if (connect) link.snd.connect=true;
// Nothing to do or let watchdog handle link messages?
if ((link.state && link.state!=AMState.AMS_NOTCONNECTED && link.state!=AMState.AMS_PAIRED) ||
this.mode&AMMode.AMO_ONEWAY) return;
// Random port range p0-p1? Let watchdog do the work
if (typeof link.snd.port == 'string') return;
// Send link message
if (snd==undefined || snd.address==undefined) snd={},snd.address=link.snd.address;
if (snd.port==undefined) snd.port=link.snd.port;
this.url=url; // Remember this link
} else {
// MULTICAST mode
url=addr2url(snd,true);
if (!this.links[url] || !this.links[url].snd.address)
this.links[url]={
snd:snd,
state:AMState.AMS_NOTCONNECTED,
tries:0,
connect:connect,
live:options.AMC_MAXLIVE
};
// Let watchdog handle connect request link messages
if (!this.inwatchdog && connect && !this.sharedSocket) {
this.watchdog(true);
return;
}
// if (this.verbose>1) this.out('send link '+Io.inspect(snd));
}
return snd;
}
/** Install a watchdog timer.
*
* 1. If link state is AMS_NOTCONNECTED, retry link request if this.links[].snd is set.
* 2. If link state is AMS_CONNECTED, check link end point.
* 3, If link state is AMS_RENDEZVOUS, get remote endpoint connectivity via broker
*
* @param run
*/
amp.tcp.prototype.watchdog = function(run,immed) {
var self=this;
if (this.timer) clearTimeout(self.timer),this.timer=undefined;
if (this.verbose>1) this.out('Starting watchdog run='+run+' immed='+immed);
if (run) this.timer=setTimeout(function () {
var con,to,tokens;
if (!self.timer || (!self.sock && !self.sharedSocket) || self.inwatchdog) return; // stopped or busy?
self.timer = undefined;
self.inwatchdog=true;
function handle(obj,url) {
if (self.verbose>1) self.out('Watchdog: handle link '+
url+(obj.snd?('('+obj2url(obj.snd)+')'):'')+' in state '+AMState.print(obj.state)+
(obj.tries!=undefined?('[#'+obj.tries+']'):''));
switch (obj.state) {
case AMState.AMS_CONNECTED:
if (obj.live == 0) {
// No PING received, disconnect...
if (self.verbose>0)
self.out('(TCP) Endpoint ' + addr2url(obj.snd) +
' not responding, propably dead. Unlinking...');
// self.emit('route-',addr2url(obj.snd)) .. done in unlink
if (self.mode&AMMode.AMO_MULTICAST) self.unlink(obj.snd);
else self.unlink();
obj.state = AMState.AMS_NOTCONNECTED;
if (!obj.snd.connect) obj.snd={};
if (self.broker) {
// Re-register on broker for rendezvous ...
self.watchdog(true);
if (self.links['*']) {
self.links['*'].state=AMState.AMS_RENDEZVOUS;
}
}
} else {
obj.tries=0;
obj.live--;
self.watchdog(true);
if (self.mode&AMMode.AMO_MULTICAST) self.ping(obj.snd);
else self.ping();
}
break;
case AMState.AMS_NOTCONNECTED:
if (!obj.snd) return;
if (obj.snd.port && typeof obj.snd.port == 'string') {
// Random port connection from a port range p0-p1; save it and start with first
// random selection
tokens=obj.snd.port.split('-');
if (tokens.length==2) obj.range=[Number(tokens[0]),Number(tokens[1])];
}
if (obj.range) {
// Get a random port from range
obj.snd.port=Comp.random.interval(obj.range[0],obj.range[1]);
if (self.verbose>0)
self.out('Trying link to ' + addr2url(obj.snd));
if (self.mode&AMMode.AMO_MULTICAST) self.link(obj.snd);
else self.link();
obj.tries++;
if (obj.tries < options.TRIES) self.watchdog(true);
else {
obj.snd={},obj.tries=0,obj.range=undefined;
}
} else if (obj.snd.port && typeof obj.snd.port == 'number') {
// Try link to specified remote endpoint obj.snd
if (self.verbose>0 && obj.tries==0)
self.out('(TCP) Trying link to ' + addr2url(obj.snd));
if (self.mode&AMMode.AMO_MULTICAST) self.link(obj.snd);
else self.link();
obj.tries++;
if (obj.tries < options.TRIES) self.watchdog(true);
else {
self.out('(TCP) Giving up to link '+addr2url(obj.snd));
self.emit('error','link',addr2url(obj.snd,true));
obj.snd={},obj.tries=0;
}
}
break;
// AMP Broker P2P Control and Management
case AMState.AMS_RENDEZVOUS:
obj.next=Aios.time()+options.REGTMO;
obj.interval=options.REGTMO;
self.send(
{type:'register',name: self.rcv.name, linfo: self.rcv },
self.broker,
function () {}
);
self.watchdog(true);
break;
case AMState.AMS_REGISTERED:
if (obj.snd && obj.snd.name && obj.tries < options.TRIES) {
obj.tries++;
self.send(
{type:'pair', from:self.rcv.name, to: obj.snd.name },
self.broker,
function () {}
);
// self.watchdog(true);
} else if (options.REGTMO && Aios.time() > obj.next) {
// Update registration periodically; messages can be lost
obj.interval *= 2;
obj.interval = Math.min(obj.interval,options.REGTMO*8);
obj.next=Aios.time()+obj.interval;
self.send(
{type:'register',name: self.rcv.name, linfo: self.rcv },
self.broker,
function () {}
);
}
self.watchdog(true);
break;
}
}
for(var p in self.links) if (self.links[p]) handle(self.links[p],p);
self.inwatchdog=false;
},immed?0:options.TIMER);
};
/** Write message data to remote endpoint address:port over a temporary TCP connection
*
*/
amp.tcp.prototype.write = function(data,off,len,port,address,cb) {
var self=this,
url=addr2url({address:address,port:port});
if (off!=0) this.err('tpc.socket.write: buffer offset <> 0');
if (this.keepAlive || this.sharedSocket) {
// reuse TCP session and connection
if (!this.client[url]) return; // closed?
// keep tcp connection alive and send messages as a stream
// if (!this.client[url]) this.client[url]={busy:false,connected:false,queue:[]}
if (this.client[url].busy) {
// console.log('enqueue');
this.client[address+':'+port].queue.push([data,off,len,port,address,cb]);
return;
}
this.client[url].busy=true;
function send() {
if (!self.client[url]) return; // closed?
// console.log('send',data.length);
// 1. send data length
var dataLength = Buffer(4);
dataLength.writeInt32LE(data.length,0);
try {
self.client[url].socket.write(dataLength, function () {
if (!self.client[url]) return; // closed?
// 2. send data payload
self.client[url].socket.write(data, function () {
if (!self.client[url]) return;
// console.log('write done',self.client[address+':'+port].queue.length)
if (cb) cb();
self.client[url].busy=false;
if (self.client[url].queue.length) {
// console.log('dequeue');
self.client[url].socket.write.apply(self,self.client[url].shift());
}
})
});
} catch (e) { if (self.verbose) self.out(url+': '+e); delete self.client[url]; }
}
/*
if (!this.client[url].socket) {
this.client[url].socket=new net.Socket();
console.log('write.connect',url);
this.client[url].socket.connect(port,address, send);
this.client[url].connected=true;
this.client[url].socket.on('close', function () {
console.log('close',url);
delete self.client[url];
});
this.client[url].socket.on('error', function (err) {
console.log('error',url,err)
delete self.client[url];
});
} else*/
if (!this.client[url].connected) {
console.log('write.connect',url);
this.client[url].busy=true;
this.client[url].socket.connect(port,address, function () {
self.client[url].busy=false;
send();
});
this.client[url].connected=true;
} else {
send();
}
} else {
// for each message a new ad-hoc connection
var client = new net.Socket();
client.on('error',function (e) { if (cb) cb(e.toString()) })
client.connect(port,address, function () {
client.write(data,function () {
client.destroy();
// console.log(len)
if (cb) cb();
});
});
}
}
};
BundleModuleCode['jam/ampStream']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2018 bLAB
** $CREATED: 09-02-16 by sbosse.
** $RCS: $Id: ampStream.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.11.2
**
** $INFO:
**
** JAM Agent Management Port (AMP) over streams
**
**
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Lz = Require('os/lz-string');
var Comp = Require('com/compat');
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var Command = Net.Command;
var Status = Net.Status;
var current=none;
var Aios=none;
var CBL = Require('com/cbl');
var COM = Require('jam/ampCOM'),
AMMode=COM.AMMode,
AMMessageType=COM.AMMessageType,
AMState=COM.AMState,
amp=COM.amp,
options=COM.options,
url2addr=COM.url2addr,
addr2url=COM.addr2url,
addrequal=COM.addrequal,
resolve=COM.resolve,
ipequal=COM.ipequal,
getNetworkIP=COM.getNetworkIP;
module.exports.current=function (module) { current=module.current; Aios=module; };
/** AMP port using streams
* ======================
*
* Note: Process streams transfer objects not data!
* No negotiation is performed. Data transfer can be fragmented.
*
* type amp.stream = function (options:{sock:stream,verbose?,logging?,out?:function,log?,mode?:'buffer'|'object'})
*/
amp.stream = function (options) {
var self=this;
options=checkOptions(options,{});
this.options=options;
this.verbose=checkOption(options.verbose,0);
this.dir=options.dir; // Logical direction
this.mode = AMMode.AMO_UNICAST |
AMMode.AMO_STATIC | // No link change
(options.mode=='object'? AMMode.AMO_OBJECT:AMMode.AMO_BUFFER); // Transfer data buffers or objects?
this.port = options.port||Net.uniqport(); // Connection Link Port (this side)
this.id = Net.Print.port(this.port);
this.links={state:AMState.AMS_NOTCONNECTED};
// Stream socket; can be a process object!
this.sock = options.sock;
this.dlimit = options.dlimit||512;
this.out = function (msg) {
Aios.print('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] '+msg);
}
this.state = AMState.AMS_INIT;
this.events = [];
this.logs=[];
this.logging=options.logging||false;
if (this.logging) {
setInterval(function () { self.LOG('print') },5000);
}
if (this.mode & AMMode.AMO_OBJECT)
this.receiver=this.receiverObj,
this.request=this.requestObj;
};
amp.stream.prototype.LOG = amp.udp.prototype.LOG;
amp.stream.prototype.emit = amp.udp.prototype.emit;
amp.stream.prototype.init = amp.udp.prototype.init;
amp.stream.prototype.on = amp.udp.prototype.on;
amp.stream.prototype.receiver=function (callback,rcv) {
var self = this;
if (rcv == undefined || rcv.address==undefined) rcv={},rcv.address=this.rcv.address;
if (rcv.port==undefined) rcv,port=this.rcv.port;
var cache = Comp.hashtbl.create();
var buf = Buf.Buffer();
var sock = this.sock; // rcv_sock;
sock.on('message', function (message, remote) {
var handler,dfrags,dlist,msgtyp,tid,ipport,discard,off,size,thisnum,transaction,more,port,ip,data,msg;
handler={};
Buf.buf_init(buf);
Buf.buf_of_str(buf,message);
self.count.rcv += message.length;
if (message.length >= 12) {
msgtyp=Buf.buf_get_int16(buf);
discard=false;
if (self.verbose>1)
self.out('receiver: Receiving Message [' + message.length+'] '+AMMessageType.print(msgtyp));
switch (msgtyp) {
case AMMessageType.AMMRPCHEAD:
tid = Buf.buf_get_int16(buf);
port = Buf.buf_get_port(buf);
handler.tid=tid;
handler.remote=remote.address+':'+Buf.buf_get_int16(buf);
handler.cmd=Buf.buf_get_int16(buf);
handler.size=Buf.buf_get_int16(buf);
handler.frags=Buf.buf_get_int16(buf);
handler.buf=Buf.Buffer();
// console.log(handler)
if (handler.size>0) {
dlist = Comp.array.range(0, handler.frags - 1);
// Add transaction to cache for pending data
Comp.hashtbl.add(cache, handler.tid, [handler,dlist,1000]);
} else {
callback(handler);
}
break;
case AMMessageType.AMMRPCDATA:
tid = Buf.buf_get_int16(buf);
port = Buf.buf_get_port(buf);
off = Buf.buf_get_int32(buf);
size = Buf.buf_get_int16(buf);
more = Buf.buf_get_int16(buf);
thisnum = off/self.dlimit;
transaction = Comp.hashtbl.find(cache,tid);
if (transaction!=undefined) {
handler=transaction[0];
if (self.verbose>1)
self.out('receiver: adding data num='+
thisnum+' off='+off+' size='+size+' dlist='+transaction[1]);
Buf.buf_get_buf(buf,handler.buf,off,size);
transaction[1]=Comp.array.filter(transaction[1],function(num) {return (num!=thisnum)});
if (Comp.array.empty(transaction[1])) {
if (self.verbose>1) self.out('[AMP] receiver: finalize '+addr2url(remote));
// Io.out(handler.data.toString());
// Deliver
callback(handler);
Comp.hashtbl.remove(cache,tid);
}
handler=undefined;
}
break;
}
}
});
};
// Object transfer mode (process streams)
// Message format: {cmd:*,msg:buffer}
amp.stream.prototype.receiverObj=function (callback,rcv) {
this.sock.on('message', function (obj) {
var handler={cmd:obj.cmd,buf:Buf.Buffer(obj.msg)};
self.count.rcv += obj.msg.length;
if (callback) callback(handler);
});
}
amp.stream.prototype.request=amp.udp.prototype.request;
// Object transfer mode (process streams)
// function (cmd:integer,msg:{pos:number,data:buffer},callback:function)
amp.stream.prototype.requestObj=function (cmd,msg,callback) {
// Sync. operation
this.count.snd += msg.data.length;
this.sock.send({cmd:cmd,msg:msg.data});
if (callback) callback()
}
amp.stream.prototype.start = function(callback) {
if (this.verbose) this.out('Stream link '+Aios.DIR.print(this.dir)+' started.');
this.links.state=AMState.AMS_CONNECTED;
if (callback) callback()
};
amp.stream.prototype.stop = function(callback) { if (callback) callback()};
amp.stream.prototype.status=amp.udp.prototype.status;
};
BundleModuleCode['jam/ampHTTP']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: 09-02-16 by sbosse.
** $RCS: $Id: ampHTTP.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.14.7
**
** $INFO:
**
** JAM Agent Management Port (AMP) over HTTP
** Only Mulitcast IP(*) mode is supported!
**
** Events out: 'error','route-'
**
** TODO: Garbage collection
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Lz = Require('os/lz-string');
var Comp = Require('com/compat');
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var Command = Net.Command;
var Status = Net.Status;
var current=none;
var Aios=none;
var CBL = Require('com/cbl');
var Bas64 = Require('os/base64');
var Sec = Require('jam/security')
var JSONfn = Require('jam/jsonfn')
var options = {
version:"1.14.7"
}
var COM = Require('jam/ampCOM'),
AMMode=COM.AMMode,
AMMessageType=COM.AMMessageType,
AMState=COM.AMState,
amp=COM.amp,
options=COM.options,
url2addr=COM.url2addr,
addr2url=COM.addr2url,
addrequal=COM.addrequal,
resolve=COM.resolve,
ipequal=COM.ipequal,
isLocal=COM.isLocal,
getNetworkIP=COM.getNetworkIP
magic=COM.options.magic;
var debug = false;
module.exports.current=function (module) { current=module.current; Aios=module; };
/*
** Parse query string '?attr=val&attr=val... and return parameter record
*/
function parseQueryString( url ) {
var queryString = url.substring( url.indexOf('?') + 1 );
if (queryString == url) return [];
var params = {}, queries, temp, i, l;
// Split into key/value pairs
queries = queryString.split("&");
// Convert the array of strings into an object
for ( i = 0, l = queries.length; i < l; i++ ) {
temp = queries[i].split('=');
if (temp[1]==undefined) temp[1]='true';
params[temp[0]] = temp[1].replace('%20',' ');
}
return params;
}
/*
** Format a query string from a parameter record
*/
function formatQueryString (msg) {
var path= '/?';
path += "magic="+msg.magic;
path += "&type="+AMMessageType.print(msg.type);
if (msg.cmd) path += '&cmd='+msg.cmd;
if (msg.tid) path += '&tid='+msg.tid;
if (msg.port) path += '&port='+Net.port_to_str(msg.port);
if (msg.timeout) path += '&timeout='+msg.timeout;
if (msg.node) path += '&node='+msg.node.replace(' ','%20');
if (msg.index) path += '&index='+msg.index;
if (msg.secure) path += '&secure='+(msg.secure.length==8?Net.port_to_str(msg.secure):msg.secure);
return path;
}
function msg2JSON(msg) {
if (msg.port) msg.port=Net.port_to_str(msg.port);
if (msg.msg && msg.msg.length) Comp.array.iter(msg.msg,function (msg) {
if (msg.port) msg.port=Net.port_to_str(msg.port);
});
return JSONfn.stringify(msg);
}
function JSON2msg(data) {
var msg=JSONfn.parse(data);
if (msg.port) msg.port=Net.port_of_str(msg.port);
if (msg.msg && msg.msg.length) Comp.array.iter(msg.msg,function (msg) {
if (msg.port) msg.port=Net.port_of_str(msg.port);
});
return msg;
}
/** Get XML data
*
*/
function getData(data) {
if (data==undefined) return undefined;
else if (data.val!='') return data.val;
else return data.children.toString();
}
function is_error(data,err) {
if (data==undefined) return true;
if (err==undefined)
return (data.length > 0 && Comp.string.get(data,0)=='E');
else
return (Comp.string.equal(data,err));
};
/** AMP port using HTTP
* ===================
*
* No negotiation is performed. Data transfer can be fragmented.
* Each time a remote endpoint sends a GET/PUT request, we stall the request until
* a timeout occurs or we have to send data to the remote endpoint. A link is established.
* The routing table is refreshed each time the same client send a
* GET/PUT request again. If the client do not send requests anymore after a timeout, it is considered to be
* unlinked and the route is removed.
*
* type amp.http = function (options:{rcv:address,snd?:address,verbose?,logging?,out?:function,log?})
*/
var http = Require('http');
amp.http = function (options) {
var self=this;
this.proto = 'http';
this.options=checkOptions(options,{});
this.verbose=checkOption(this.options.verbose,0);
this.dir = options.dir; // attached to JAM port
this.rcv = options.rcv; // Local HTTP Server Port; Server Mode
this.mode = AMMode.AMO_MULTICAST; // We can handle multiple links at once
this.node = options.node; // Attached to this node
if (options.nodeid) this.node={id:options.nodeid}; // Different public node id
if (options.rcv && options.rcv.address!='*' && options.rcv.port) this.mode |= AMMode.AMO_SERVER;
else this.mode |= AMMode.AMO_CLIENT;
this.options.keepalive=checkOption(options.keepAlive,true);
this.secure = this.options.secure;
this.port = options.port||Net.uniqport(); // Connection Link Port (this side)
this.id = Net.Print.port(this.port);
// Stream socket; can be a process object!
this.out = function (msg,async) {
(async?Aios.logAsync:Aios.log)
('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] '+msg);
}
this.debug = function (msg) {
Aios.logAsync
('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] '+msg);
}
this.err = function (msg,async) {
(async?Aios.logAsync:Aios.log)
('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] Error: '+msg);
throw 'AMP';
}
this.events = [];
// typeof linkentry = {snd:address,tries:number,state:amstate,collect?,collecting?,msgqueue?:{} []}
this.links = {};
this.count={rcv:0,snd:0,lnk:0,png:0};
if (options.snd) {
url=addr2url(options.snd,true);
this.links[url]={snd:options.snd,tries:0,state:AMState.AMS_NOTCONNECTED,live:options.AMC_MAXLIVE};
//this.out(url)
}
// Collector thread collecting messages from server (AMO_CLIENT mode)
this.collector=undefined;
this.logs=[];
this.logging=options.logging||false;
if (this.logging) {
setInterval(function () { self.LOG('print') },5000);
}
this.index=0;
};
amp.http.prototype.LOG = amp.man.prototype.LOG;
amp.http.prototype.checkState = amp.man.prototype.checkState;
amp.http.prototype.config = amp.man.prototype.config;
amp.http.prototype.emit = amp.man.prototype.emit;
amp.http.prototype.on = amp.man.prototype.on;
amp.http.prototype.handle = amp.man.prototype.handle;
amp.http.prototype.status = amp.man.prototype.status;
/** Acknowledge reply
*
*/
amp.http.prototype.ack=function(snd,status) {
this.response(snd,{type:AMMessageType.AMMACK,status:status||"EOK",
port:this.port,node:this.node?this.node.id:'*'});
}
/** Callback from ampMAN handler to inform about remote unlink event
*
*/
amp.http.prototype.cleanup=function(url,keep) {
// Cleanup link
var obj=this.links[url];
if (!obj) return;
obj.state=AMState.AMS_NOTCONNECTED
if (obj.collect) clearTimeout(obj.collect), obj.collect=undefined;
if (obj.collecting) this.response(obj.collecting,{status:'ENOENTRY'}),obj.collecting=undefined;
// Link was initiated on remote side
// Remove link!
if (!keep) {
obj.snd={};
this.links[url]=undefined;
}
}
/** Collect request
*
*/
amp.http.prototype.collect=function(snd) {
var self=this,
url=addr2url(snd,true),
msg={type:AMMessageType.AMMCOLLECT,port:this.port,index:this.index++,magic:magic};
if (this.links[url] && this.links[url].state==AMState.AMS_CONNECTED)
this.send(snd,msg,function (reply) {
var err=is_error(reply);
if (err) return; // self.cleanup(url,true);
if (reply.msg) Comp.array.iter(reply.msg,function (msg) {
self.handle(msg,snd);
});
if (!self.links[url]) return; // unlinked?
self.links[url].collect=setTimeout(function () {
self.collect(snd);
},0);
});
}
/** Service collect request
*
*/
amp.http.prototype.collecting=function(msg,remote,response) {
var url;
if (this.verbose>2) this.debug('handle AMMCOLLECT from '+addr2url(remote));
url=addr2url(remote,true); // ipport or remote.port??
if (this.links[url] && this.links[url].msgqueue && this.links[url].msgqueue.length) {
this.response(response,{msg:this.links[url].msgqueue});
this.links[url].msgqueue=[];
}
else if (this.links[url]) this.links[url].collecting=response;
else this.response(response,{status:'ENOENTRY'});
}
/** HTTP GET request to send a messageto the server broker returning data on reply.
*
* @param path
* @param callback
*/
amp.http.prototype.get = function (snd,path,callback) {
var body,req,
self=this;
if (this.verbose>2) this.debug('get '+addr2url(snd)+ path);
this.count.snd = this.count.snd + path.length;
if (!http.xhr) {
req = http.request({
host: snd.address,
port: snd.port,
path: path,
method: 'GET',
keepAlive: this.options.keepalive,
headers: {
}
} , function(res) {
if (self.verbose>2) self.debug('got '+addr2url(snd)+ path);
if (res.setEncoding != null) res.setEncoding('utf8');
body = '';
res.on('data', function (chunk) {
body = body + chunk;
});
res.once('end', function () {
self.count.rcv += body.length;
if (callback) callback(body);
});
});
req.once('error', function(err) {
if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' '+path+' failed: '+err,true);
self.emit('error',err);
if (callback) callback();
});
req.end();
} else {
// XHR Browser
http.request({
host: snd.address,
port: snd.port,
path:path,
proto:'http',
keepAlive: this.options.keepalive,
method: 'GET',
headers: {
}
} , function(err,xhr,body) {
if (err) {
if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' '+path+' failed: '+err,true);
self.emit('error',err);
if (callback) callback();
} else {
self.count.rcv += body.length;
if (callback) callback(body);
}
});
}
};
/** Initialize AMP
*
*/
amp.http.prototype.init = function(callback) {
if (callback) callback();
};
/** Negotiate a virtual communication link (peer-to-peer).
* In oneway mode only a destination endpoint is set and it is assumed the endpoint can receive messages a-priori!
*
* typeof @snd = address
* typeof @callback = function
* typeof @connect = boolean is indicating an initial connect request and not an acknowledge
* typeof @key = private
* typeof @response = object
*
* +------------+
* VCMessageType (int16)
* Connection Port (port)
* Node ID (string)
* // Receiver IP Port (int32)
* +------------+
*
*/
amp.http.prototype.link=function(snd,connect,key,response) {
var self = this,
msg,
url;
if (this.verbose>1) this.out('amp.link: to '+addr2url(snd),true);
// MULTICAST mode
// Add new link to cache of links
if (!snd) this.err(true,'link: no destinataion set in MULTICAST mode');
if (snd.parameter && snd.parameter.secure) key=snd.parameter.secure;
url=addr2url(snd,true);
if (!this.links[url] || !this.links[url].snd.address) {
if (connect) snd.connect=true;
this.links[url]={
snd:snd,
state:AMState.AMS_NOTCONNECTED,
tries:0,
connect:connect,
live:options.AMC_MAXLIVE};
}
// Let watchdog handle connect request link messages
if (!this.inwatchdog && connect)
return this.watchdog(true);
// if (this.verbose>1) this.debug('send link '+Io.inspect(snd));
msg={
type:AMMessageType.AMMLINK,
port:this.port,
node:this.node?this.node.id:'*',
index:this.index++,
magic:magic,
remote:snd.address,
};
if (key) msg.secure=key;
this.count.lnk++;
if (response)
this.response(response,msg);
else this.send(snd,msg,function (reply) {
if (is_error(reply)) return; // error
// start message collector thread after first link reply!
if ((self.mode & AMMode.AMO_CLIENT) && !self.links[url].collect) {
self.links[url].collect=setTimeout(function () {
self.collect(snd);
},0);
}
// handle reply
self.handle(reply,snd);
});
};
amp.http.prototype.ping=function(snd,response) {
var self = this,msg={};
msg.type=AMMessageType.AMMPING;
msg.port=this.port;
msg.index=this.index++;
msg.magic=magic;
if (this.verbose>1) this.debug('amp.ping'+(response?'in response':'')+': to '+addr2url(snd));
this.count.png++;
if (response)
this.response(response,msg);
else this.send(snd,msg,function (reply) {
if (is_error(reply)) return; // error
// handle reply
self.handle(reply,snd);
});
}
amp.http.prototype.pong=function(snd,response) {
var self = this,msg={};
msg.type=AMMessageType.AMMPONG;
msg.port=this.port;
msg.index=this.index++;
msg.magic=magic;
if (this.verbose>1) this.debug('amp.pong '+(response?'in response':'')+': to '+addr2url(snd));
this.count.png++;
if (response)
this.response(response,msg);
else this.send(snd,msg,function (reply) {
if (is_error(reply)) {
self.emit('error',reply);
}
});
}
/** HTTP PUT request to send a message and data to the AMP HTTP server.
*
* @param path
* @param data
*/
amp.http.prototype.put = function (snd,path,data) {
var self=this,
req,body;
this.count.snd = this.count.snd + path.length + data.length;
if (!http.xhr) {
req = http.request({
host: snd.address,
port: snd.port,
path: path,
method: 'POST',
keepAlive: this.options.keepalive,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
} , function(res) {
if (res.setEncoding != null) res.setEncoding('utf8');
// TODO body=+chunk, res.on('end') ..??
res.once('data', function (chunk) {
// TODO
});
});
req.once('error', function(err) {
self.out('Warning: request to '+addr2url(snd)+' failed: '+err,true);
self.emit('error',err);
});
// write data to request body
req.write(data);
req.end();
} else {
// XHR Browser
http.request({
host: snd.address,
port: snd.port,
path: path,
method: 'POST',
body:data,
keepAlive: this.options.keepalive,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
} , function(err,xhr,body) {
if (err) {
if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' failed: '+err,true);
self.emit('error',err);
}
// TODO
})
}
};
amp.http.prototype.receiver = function (callback,rcv) {
var self = this;
if (callback) this.callback=callback;
if (this.mode & AMMode.AMO_SERVER) {
// Only if this is a public or locally visible network node this node
// should provide a server port!
if (rcv == undefined || rcv.address==undefined) rcv={},rcv.address=this.rcv.address;
if (rcv.port==undefined) rcv.port=this.rcv.port;
this.server=http.createServer(function (request,response) {
if(parseQueryString(request.url).length==0) return response.end('EINVALID'); // accidental access by WEB browser
// console.log(request.connection.remoteAddress);
var i,body,
msg = parseQueryString(request.url),
remote = {address:request.connection.remoteAddress.replace(/^::ffff:/,'').replace(/^::1/,'localhost'),
port:'['+msg.port.replace(/:/g,'')+']' /* unique remote identifier */};
if (self.verbose>2)
console.log(request.method,request.url,msg,addr2url(remote),url2addr(addr2url(remote)));
// consistency check
if (msg.magic!=magic) return;
self.count.rcv += 1;
msg.type=AMMessageType[msg.type];
if (msg.secure) msg.secure=Net.port_of_str(msg.secure);
if (debug) console.log(Io.Time(),msg)
response.origin=request.headers.origin||request.headers.Origin;
Comp.string.match(request.method,[
['GET',function() {
if (msg.type==AMMessageType.AMMCOLLECT)
self.collecting(msg,remote,response);
else
self.handle(msg,remote,response);
}],
['POST',function() {
body = '';
request.on('data', function (chunk) {
body = body + chunk;
});
request.on('end', function () {
msg.data=Buffer(body,'hex');
self.count.rcv += msg.data.length;
if (msg.cmd) msg.cmd=Number(msg.cmd);
self.handle(msg,remote,response);
});
}]
])
});
this.server.on("connection", function (socket) {
socket.setNoDelay(true);
});
this.server.on("error", function (err) {
self.out('Warning: receiver failed: '+err,true);
if (err) self.err(true,err);
});
this.server.listen(rcv.port,function (err) {
// Try to get network IP address of this host
if (!err) getNetworkIP(undefined,function (err,ip) {
if (!err) self.rcv.address=isLocal(ip)?options.localhost:ip;
if (self.verbose) self.out('IP port '+addr2url(self.rcv)+ ' (proto '+self.options.proto+')',true);
if (err) return self.out("! Unable to obtain network connection information: "+err,true);
});
if (callback) callback(err);
});
}
if (this.mode & AMMode.AMO_CLIENT) {
// If this is a hidden node (e.g., inside a WEB browser), we have to connect to a remote public server
// by using stalled GET requests.
if (callback) this.callback=callback;
}
}
/** Reply to a request (msg.tid contains request tid)
*/
amp.http.prototype.reply = function (cmd,msg,snd) {
this.request(cmd,msg,snd);
}
/** Send a response reply for a pending HTTP GET/PUT request (AMO_SERVER)
*
*/
amp.http.prototype.response = function (response,msg) {
var data=msg2JSON(msg), header;
if (response.origin!=undefined)
header={'Access-Control-Allow-Origin': response.origin,
'Access-Control-Allow-Credentials': 'true',
'Content-Type': 'text/plain'};
else
header={'Content-Type': 'text/plain'};
if (this.options.keepalive) header["Connection"]="keep-alive";
response.writeHead(200,header);
response.write(data);
if (debug) console.log(Io.Time(),msg)
response.end();
}
/** Send a request message to a remote node endpoint
*
* function (cmd:integer,msg:Buffer,snd:address)
*/
amp.http.prototype.request = function (cmd,msg,snd) {
var self=this,req={},
size = msg.data.length,
tid = msg.tid||Comp.random.int(65536/2);
if (snd==undefined) this.err(true,'request: snd=null');
req.type=AMMessageType.AMMRPC;
req.tid=tid; // Transaction Message ID
req.port=this.port; // This AMP id
req.cmd=cmd;
req.size=size;
req.magic=magic;
req.data=msg.data;
this.send(snd,req);
}
amp.http.prototype.scan=function(snd,response,callback) {
var self = this,msg={};
msg.type=response?AMMessageType.AMMACK:AMMessageType.AMMSCAN;
msg.port=this.port;
msg.magic=magic;
if (response) msg.info=snd.info;
if (this.verbose>1 && snd) this.debug('amp.scan: to '+addr2url(snd));
if (response)
this.response(response,msg);
else
this.send(snd,msg,function (reply) {
callback(reply)
});
}
/** Main entry for requests with JSON interface. Multiplexer for HTTP GET/PUT.
*
* msg: JSON
* callback : function (reply:object)
*/
amp.http.prototype.send = function (snd,msg,callback) {
var path,
url,
body,
self=this;
// Create query selector
path = formatQueryString(msg);
if (typeof snd.port == 'string') {
url=addr2url(snd,true);
// If Pending get from client
// Else queue message, client will collect them later (or never)
if (this.links[url]) {
if (!this.links[url].msgqueue) this.links[url].msgqueue=[];
if (this.links[url].collecting) {// pending AMMCOLLECT request
if (this.verbose>1) this.debug('REPLY msg '+AMMessageType.print(msg.type)+' to '+url);
this.response(this.links[url].collecting,{msg:[msg]});
this.links[url].collecting=undefined;
} else {
if (this.verbose>1) this.debug('QUEUE msg '+AMMessageType.print(msg.type)+' for '+url);
this.links[url].msgqueue.push(msg);
}
}
} else if (msg.data!=undefined) {
// Convert buffer data to hex formatted string
body=msg.data.toString('hex');
this.put(snd,path,body,function (body) {
if (is_error(body)) self.emit('error',body);
else if (!is_status(body)) self.emit('error','EINVALID');
// No reply expected!
});
} else {
this.get(snd,path,function (body) {
var xml,i,
reply;
if (!body || is_error(body)) {
self.emit('error','EINVALID');
} else {
reply=JSON2msg(body);
// { status:string,reply:*,msg?:{}[],..}
}
if (callback) callback(reply);
});
}
}
// Start AMP watchdog and receiver
amp.http.prototype.start = function(callback) {
var self=this,
s=this.secure?' (security port '+Sec.Port.toString(this.secure)+')':'';
if (this.verbose>0 && this.mode & AMMode.AMO_SERVER)
this.out('Starting ' + addr2url(this.rcv)+' ['+AMMode.print(this.mode)+'] (proto '+this.proto+')'+s);
if (this.verbose>0 && this.mode & AMMode.AMO_CLIENT)
this.out('Starting ['+AMMode.print(this.mode)+'] (proto http)');
this.watchdog(true);
if (!this.server && this.mode & AMMode.AMO_SERVER) {
// After stop? Restart receiver.
this.receiver();
}
if (callback) callback();
}
// Stop AMP
amp.http.prototype.stop = function(callback) {
if (this.links) for(var p in this.links) {
if (this.links[p]) {
// Try to unlink remote endpoint
if (this.links[p].collect) clearTimeout(this.links[p].collect),this.links[p].collect=undefined;
this.unlink(this.links[p].snd);
if (this.links[p]) this.links[p].state=AMState.AMS_NOTCONNECTED;
}
}
if (this.verbose>0 && this.mode & AMMode.AMO_SERVER)
this.out('Stopping ' + addr2url(this.rcv)+' ['+AMMode.print(this.mode)+'] (proto '+this.proto+')'+s);
if (this.verbose>0 && this.mode & AMMode.AMO_CLIENT)
this.out('Stopping ['+AMMode.print(this.mode)+'] (proto http)');
if (this.timer) clearTimeout(this.timer),this.timer=undefined;
if (this.server) this.server.close(),this.server=undefined;
if (callback) callback();
}
// Unlink remote endpoint
amp.http.prototype.unlink=function(snd) {
var self = this,msg,
url = snd?addr2url(snd,true):null;
if (this.mode&AMMode.AMO_MULTICAST) {
if (!this.links[url] || this.links[url].state!=AMState.AMS_CONNECTED) return;
} else {
if (this.links.state!=AMState.AMS_CONNECTED) return;
}
msg={type:AMMessageType.AMMUNLINK,port:this.port,node:this.node?this.node.id:'*',index:this.index++,magic:magic};
this.send(snd,msg,function (reply) {
// handle reply
if (reply) {}
});
this.emit('route-',addr2url(snd,true));
if (this.mode&AMMode.AMO_MULTICAST) {
this.links[url].state=AMState.AMS_NOTCONNECTED;
if (!this.links[url].snd.connect) this.links[url].snd={};
} else {
this.links.state=AMState.AMS_NOTCONNECTED;
if (!this.links.snd.connect) this.links.snd={};
}
this.cleanup(url);
}
/** Install a watchdog timer.
*
* 1. If link state is AMS_NOTCONNECTED, retry link request if this.links[].snd is set.
* 2. If link state is AMS_CONNECTED, check link end point.
* 3, If link state is AMS_RENDEZVOUS, get remote endpoint connectivity via broker
*
* @param run
*/
amp.http.prototype.watchdog = function(run,immedOrDelay) {
var self=this;
if (this.timer) clearTimeout(self.timer),this.timer=undefined;
if (run) self.timer=setTimeout(function () {
if (!self.timer || self.inwatchdog) return; // stopped or busy?
self.timer = undefined;
self.inwatchdog=true;
function handle(obj,url) {
if (self.verbose>1) self.debug('Watchdog: handle link ('+url+') '+
(obj.snd?addr2url(obj.snd):'')+' in state '+
AMState.print(obj.state)+' live '+obj.live,true);
switch (obj.state) {
case AMState.AMS_CONNECTED:
if (obj.live == 0) {
// No PING received, disconnect...
if (self.verbose>0)
self.out('Endpoint ' + addr2url(obj.snd) +
' not responding, propably dead. Unlinking...',true);
obj.state = AMState.AMS_NOTCONNECTED;
self.emit('route-',addr2url(obj.snd,true));
self.cleanup(url,obj.snd.connect);
if (obj.snd.connect) self.watchdog(true,2000);
} else {
obj.tries=0;
obj.live--;
self.watchdog(true);
if (self.mode&AMMode.AMO_MULTICAST) self.ping(obj.snd);
else self.ping();
}
break;
case AMState.AMS_NOTCONNECTED:
case AMState.AMS_PAIRED:
if (obj.snd.port && typeof obj.snd.port == 'number') {
// Try link to specified remote endpoint obj.snd
if (self.verbose>0 && obj.tries==0)
self.out('Trying link to ' + addr2url(obj.snd),true);
self.link(obj.snd);
obj.tries++;
if (obj.tries < options.TRIES) self.watchdog(true);
else {
self.out('Giving up to link '+addr2url(obj.snd),true);
self.emit('error','link',addr2url(obj.snd));
obj.snd={},obj.tries=0;
}
}
break;
// AMP P2P Control
case AMState.AMS_RENDEZVOUS:
obj.send(
{type:'register',name: self.rcv.name, linfo: self.rcv},
self.broker,
function () {}
);
self.watchdog(true);
break;
case AMState.AMS_REGISTERED:
if (obj.tries < options.TRIES && obj.snd.name) {
obj.tries++;
self.send(
{type:'pair', from:self.rcv.name, to: obj.snd.name},
self.broker,
function () {
}
);
}
if (obj.tries < options.TRIES) self.watchdog(true);
break;
}
}
for(var p in self.links) if (self.links[p]) handle(self.links[p],p);
self.inwatchdog=false;
},immedOrDelay==true?0:immedOrDelay||options.TIMER);
};
};
BundleModuleCode['jam/jsonfn']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Vadim Kiryukhin, Stefan Bosse
** $INITIAL: (C) 2006-2017 Vadim Kiryukhin
** $MODIFIED: by sbosse.
** $RCS: $Id: jsonfn.js,v 1.1 2017/05/20 15:56:53 sbosse Exp $
** $VERSION: 1.3.3X
**
** $INFO:
**
** JSONfn - javascript (both node.js and browser) plugin to stringify,
** parse and clone objects with embedded functions in an optional masked context (mask).
** - supported data types: number, boolean, string, array, buffer, typedarray, function, regex
**
** browser:
** JSONfn.stringify(obj);
** JSONfn.parse(str[, date2obj]);
** JSONfn.clone(obj[, date2obj]);
**
** nodejs:
** var JSONfn = require('path/to/json-fn');
** JSONfn.stringify(obj);
** JSONfn.parse(str[, date2obj]);
** JSONfn.clone(obj[, date2obj]);
**
**
** @obj - Object;
** @str - String, which is returned by JSONfn.stringify() function;
** @mask - Environment Mask (optional)
**
** $ENDOFINFO
*/
var current=null;
function typedarrayTObase64(ta,ftyp) {
var b,i;
if (ta.buffer instanceof ArrayBuffer) {
b=Buffer(ta.buffer);
if (b.length>0) return b.toString('base64');
}
// Fall-back conversion
switch (ftyp) {
case Float32Array:
b = Buffer(ta.length*4);
for(i=0;i<ta.length;i++) b.writeFloatLE(ta[i],i*4);
return b.toString('base64');
case Float64Array:
b = Buffer(ta.length*8);
for(i=0;i<ta.length;i++) b.writeDoubleLE(ta[i],i*8);
return b.toString('base64');
case Int16Array:
b = Buffer(ta.length*2);
for(i=0;i<ta.length;i++) b.writeInt16LE(ta[i],i*2);
return b.toString('base64');
case Int32Array:
b = Buffer(ta.length*4);
for(i=0;i<ta.length;i++) b.writeInt32LE(ta[i],i*4);
return b.toString('base64');
}
return ta.toString();
}
function base64TOtypedarray(buff,ftyp) {
var i,ta;
if (buff.buffer instanceof ArrayBuffer) {
switch (ftyp) {
case Float32Array: return new Float32Array((new Uint8Array(buff)).buffer);
case Float64Array: return new Float64Array((new Uint8Array(buff)).buffer);
case Int16Array: return new Int16Array((new Uint8Array(buff)).buffer);
case Int32Array: return new Int32Array((new Uint8Array(buff)).buffer);
}
} else if (typeof Uint8Array.from != 'undefined') {
switch (ftyp) {
case Float32Array: return new Float32Array(Uint8Array.from(buff).buffer);
case Float64Array: return new Float64Array(Uint8Array.from(buff).buffer);
case Int16Array: return new Int16Array(Uint8Array.from(buff).buffer);
case Int32Array: return new Int32Array(Uint8Array.from(buff).buffer);
}
} else {
// Fall-back conversion
switch (ftyp) {
case Float32Array:
ta=new Float32Array(buff.length/4);
for(i=0;i<ta.length;i++)
ta[i]=buff.readFloatLE(i*4);
return ta;
case Float64Array:
ta=new Float64Array(buff.length/8);
for(i=0;i<ta.length;i++)
ta[i]=buff.readDoubleLE(i*8);
return ta;
case Int16Array:
ta=new Int16Array(buff.length/2);
for(i=0;i<ta.length;i++)
ta[i]=buff.readInt16LE(i*2);
return ta;
case Int32Array:
ta=new Int32Array(buff.length/4);
for(i=0;i<ta.length;i++)
ta[i]=buff.readInt32LE(i*4);
return ta;
}
}
}
(function (exports) {
function stringify (obj) {
return JSON.stringify(obj, function (key, value) {
if (value instanceof Function || typeof value == 'function')
return '_PxEnUf_' +Buffer(value.toString(true)).toString('base64'); // try minification (true) if supported
if (value instanceof Buffer)
return '_PxEfUb_' +value.toString('base64');
if (typeof Float64Array != 'undefined' && value instanceof Float64Array)
return '_PxE6Lf_' + typedarrayTObase64(value,Float64Array);
if (typeof Float32Array != 'undefined' && value instanceof Float32Array)
return '_PxE3Lf_' + typedarrayTObase64(value,Float32Array);
if (typeof Int16Array != 'undefined' && value instanceof Int16Array)
return '_PxE1Ni_' + typedarrayTObase64(value,Int16Array);
if (typeof Int32Array != 'undefined' && value instanceof Int32Array)
return '_PxE3Ni_' + typedarrayTObase64(value,Int32Array);
if (value instanceof RegExp)
return '_PxEgEr_' + value;
return value;
});
};
function parse(str, mask) {
var code;
try {
with (mask||{}) {
code= JSON.parse(str, function (key, value) {
var prefix;
try {
if (typeof value != 'string') {
return value;
}
if (value.length < 8) {
return value;
}
prefix = value.substring(0, 8);
if (prefix === '_PxEnUf_') {
var code = value.slice(8);
// TODO: arrow function support (missing own this object fix)
// must be addressed in higher-level code (code.js)
if (code.indexOf('function')==0) // Backward comp.
return eval('(' + code + ')');
else
return eval('(' + Buffer(code,'base64').toString() + ')');
}
if (prefix === '_PxEfUb_')
return Buffer(value.slice(8),'base64');
if (prefix === '_PxE6Lf_')
return base64TOtypedarray(Buffer(value.slice(8),'base64'),Float64Array);
if (prefix === '_PxE3Lf_')
return base64TOtypedarray(Buffer(value.slice(8),'base64'),Float32Array);
if (prefix === '_PxE1Ni_')
return base64TOtypedarray(Buffer(value.slice(8),'base64'),Int16Array);
if (prefix === '_PxE3Ni_')
return base64TOtypedarray(Buffer(value.slice(8),'base64'),Int32Array);
if (prefix === '_PxEgEr_')
return eval(value.slice(8));
return value;
} catch (e) {
throw {error:e,value:value};
}
});
};
} catch (e) {
throw e.error||e;
}
return code;
};
exports.clone = function (obj, date2obj) {
return exports.parse(exports.stringify(obj), date2obj);
};
exports.current = function (module) { current=module.current; };
exports.serialize = stringify;
exports.stringify = stringify;
exports.deserialize = parse;
exports.parse = parse;
/* Remove any buffer toJSON bindings */
if (typeof Buffer != 'undefined' && Buffer.prototype.toJSON) delete Buffer.prototype.toJSON;
if (typeof buffer == 'object' && buffer.Buffer) delete buffer.Buffer.prototype.toJSON;
}(typeof exports === 'undefined' ? (window.JSONfn = {}) : exports));
};
BundleModuleCode['jam/ampHTTPS']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: 31-05-20 by sbosse.
** $RCS: $Id: ampHTTPS.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.14.7
**
** $INFO:
**
** JAM Agent Management Port (AMP) over HTTPS
** Only Mulitcast IP(*) mode is supported!
**
** Events out: 'error','route-'
**
** TODO: Garbage collection
**
** Requires cert.pem and key.pem strings (options.pem.key/cert) and builtin https/crypto!
** Letsencrypt files:
** SSLCertificateFile /etc/letsencrypt/live/<domain>/fullchain.pem
** SSLCertificateKeyFile /etc/letsencrypt/live/<domain>/privkey.pem
**
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Lz = Require('os/lz-string');
var Comp = Require('com/compat');
var Buf = Require('dos/buf');
var Net = Require('dos/network');
var Command = Net.Command;
var Status = Net.Status;
var current=none;
var Aios=none;
var CBL = Require('com/cbl');
var Bas64 = Require('os/base64');
var Sec = Require('jam/security')
var JSONfn = Require('jam/jsonfn')
var options = {
version:"1.14.7",
}
var COM = Require('jam/ampCOM'),
AMMode=COM.AMMode,
AMMessageType=COM.AMMessageType,
AMState=COM.AMState,
amp=COM.amp,
options=COM.options,
url2addr=COM.url2addr,
addr2url=COM.addr2url,
addrequal=COM.addrequal,
resolve=COM.resolve,
ipequal=COM.ipequal,
isLocal=COM.isLocal,
getNetworkIP=COM.getNetworkIP,
pem=COM.options.pem,
magic=COM.options.magic;
var debug = false;
module.exports.current=function (module) { current=module.current; Aios=module; };
/*
** Parse query string '?attr=val&attr=val... and return parameter record
*/
function parseQueryString( url ) {
var queryString = url.substring( url.indexOf('?') + 1 );
if (queryString == url) return [];
var params = {}, queries, temp, i, l;
// Split into key/value pairs
queries = queryString.split("&");
// Convert the array of strings into an object
for ( i = 0, l = queries.length; i < l; i++ ) {
temp = queries[i].split('=');
if (temp[1]==undefined) temp[1]='true';
params[temp[0]] = temp[1].replace('%20',' ');
}
return params;
}
/*
** Format a query string from a parameter record
*/
function formatQueryString (msg) {
var path= '/?';
path += "magic="+msg.magic;
path += "&type="+AMMessageType.print(msg.type);
if (msg.cmd) path += '&cmd='+msg.cmd;
if (msg.tid) path += '&tid='+msg.tid;
if (msg.port) path += '&port='+Net.port_to_str(msg.port);
if (msg.timeout) path += '&timeout='+msg.timeout;
if (msg.node) path += '&node='+msg.node.replace(' ','%20');
if (msg.index) path += '&index='+msg.index;
if (msg.secure) path += '&secure='+(msg.secure.length==8?Net.port_to_str(msg.secure):msg.secure);
return path;
}
function msg2JSON(msg) {
if (msg.port) msg.port=Net.port_to_str(msg.port);
if (msg.msg && msg.msg.length) Comp.array.iter(msg.msg,function (msg) {
if (msg.port) msg.port=Net.port_to_str(msg.port);
});
return JSONfn.stringify(msg);
}
function JSON2msg(data) {
var msg=JSONfn.parse(data);
if (msg.port) msg.port=Net.port_of_str(msg.port);
if (msg.msg && msg.msg.length) Comp.array.iter(msg.msg,function (msg) {
if (msg.port) msg.port=Net.port_of_str(msg.port);
});
return msg;
}
/** Get XML data
*
*/
function getData(data) {
if (data==undefined) return undefined;
else if (data.val!='') return data.val;
else return data.children.toString();
}
function is_error(data,err) {
if (data==undefined) return true;
if (err==undefined)
return (data.length > 0 && Comp.string.get(data,0)=='E');
else
return (Comp.string.equal(data,err));
};
/** AMP port using HTTP
* ===================
*
* No negotiation is performed. Data transfer can be fragmented.
* Each time a remote endpoint sends a GET/PUT request, we stall the request until
* a timeout occurs or we have to send data to the remote endpoint. A link is established.
* The routing table is refreshed each time the same client send a
* GET/PUT request again. If the client do not send requests anymore after a timeout, it is considered to be
* unlinked and the route is removed.
*
* type amp.https = function (options:{pem?:{key,cert}, rcv:address,snd?:address,verbose?,logging?,out?:function,log?})
*/
var https;
var http = Require('http');
amp.https = function (options) {
var self=this;
this.proto = 'http';
this.options = checkOptions(options,{});
this.verbose = checkOption(this.options.verbose,0);
if (global.TARGET!= 'browser' && !https) try {
https=require('https');
} catch (e) {
throw 'amp.https: no https/crypto support ('+e+')';
}
this.dir = options.dir; // attached to JAM port
this.rcv = options.rcv; // Local HTTP Server Port; Server Mode
this.mode = AMMode.AMO_MULTICAST; // We can handle multiple links at once
this.node = options.node; // Attached to this node
if (options.rcv && options.rcv.address!='*' && options.rcv.port) this.mode |= AMMode.AMO_SERVER;
else this.mode |= AMMode.AMO_CLIENT;
if (!options.pem) this.options.pem=pem;
if ((this.mode & AMMode.AMO_CLIENT)==0 &&
(!this.options.pem || !this.options.pem.key || !this.options.pem.cert))
throw "amp.https: no pem certificate and key provided like pem:{key,cert}";
this.options.keepalive=checkOption(options.keepAlive,true);
this.secure = this.options.secure;
this.port = options.port||Net.uniqport(); // Connection Link Port (this side)
this.id = Net.Print.port(this.port);
// Stream socket; can be a process object!
this.out = function (msg,async) {
(async?Aios.logAsync:Aios.log)
('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] '+msg);
}
this.debug = function (msg) {
Aios.logAsync
('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] '+msg);
}
this.err = function (msg,async) {
(async?Aios.logAsync:Aios.log)
('[AMP '+Net.Print.port(self.port)+
(self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] Error: '+msg);
throw 'AMP';
}
this.events = [];
// typeof linkentry = {snd:address,tries:number,state:amstate,collect?,collecting?,msgqueue?:{} []}
this.links = {};
this.count={rcv:0,snd:0,lnk:0,png:0};
if (options.snd) {
url=addr2url(options.snd,true);
this.links[url]={snd:options.snd,tries:0,state:AMState.AMS_NOTCONNECTED,live:options.AMC_MAXLIVE};
//this.out(url)
}
// Collector thread collecting messages from server (AMO_CLIENT mode)
this.collector=undefined;
this.logs=[];
this.logging=options.logging||false;
if (this.logging) {
setInterval(function () { self.LOG('print') },5000);
}
this.index=0;
};
amp.https.prototype.LOG = amp.man.prototype.LOG;
amp.https.prototype.checkState = amp.man.prototype.checkState;
amp.https.prototype.config = amp.man.prototype.config;
amp.https.prototype.emit = amp.man.prototype.emit;
amp.https.prototype.on = amp.man.prototype.on;
amp.https.prototype.handle = amp.man.prototype.handle;
amp.https.prototype.status = amp.man.prototype.status;
/** Acknowledge reply
*
*/
amp.https.prototype.ack=function(snd,status) {
this.response(snd,{type:AMMessageType.AMMACK,status:status||"EOK",
port:this.port,node:this.node?this.node.id:'*'});
}
/** Callback from ampMAN handler to inform about remote unlink event
*
*/
amp.https.prototype.cleanup=function(url,keep) {
// Cleanup link
var obj=this.links[url];
if (!obj) return;
obj.state=AMState.AMS_NOTCONNECTED
if (obj.collect) clearTimeout(obj.collect), obj.collect=undefined;
if (obj.collecting) this.response(obj.collecting,{status:'ENOENTRY'}),obj.collecting=undefined;
// Link was initiated on remote side
// Remove link!
if (!keep) {
obj.snd={};
this.links[url]=undefined;
}
}
/** Collect request
*
*/
amp.https.prototype.collect=function(snd) {
var self=this,
url=addr2url(snd,true),
msg={type:AMMessageType.AMMCOLLECT,port:this.port,index:this.index++,magic:magic};
if (this.links[url] && this.links[url].state==AMState.AMS_CONNECTED)
this.send(snd,msg,function (reply) {
var err=is_error(reply);
if (err) return; // self.cleanup(url,true);
if (reply.msg) Comp.array.iter(reply.msg,function (msg) {
self.handle(msg,snd);
});
if (!self.links[url]) return; // unlinked?
self.links[url].collect=setTimeout(function () {
self.collect(snd);
},0);
});
}
/** Service collect request
*
*/
amp.https.prototype.collecting=function(msg,remote,response) {
var url;
if (this.verbose>2) this.debug('handle AMMCOLLECT from '+addr2url(remote));
url=addr2url(remote,true); // ipport or remote.port??
if (this.links[url] && this.links[url].msgqueue && this.links[url].msgqueue.length) {
this.response(response,{msg:this.links[url].msgqueue});
this.links[url].msgqueue=[];
}
else if (this.links[url]) this.links[url].collecting=response;
else this.response(response,{status:'ENOENTRY'});
}
/** HTTP GET request to send a messageto the server broker returning data on reply.
*
* @param path
* @param callback
*/
amp.https.prototype.get = function (snd,path,callback) {
var body,req,
self=this;
if (this.verbose>2) this.debug('get '+addr2url(snd)+ path);
this.count.snd = this.count.snd + path.length;
if (https) {
req = https.request({
host: snd.address,
port: snd.port,
path: path,
method: 'GET',
keepAlive: this.options.keepalive,
headers: {
}
} , function(res) {
if (self.verbose>2) self.debug('got '+addr2url(snd)+ path);
if (res.setEncoding != null) res.setEncoding('utf8');
body = '';
res.on('data', function (chunk) {
body = body + chunk;
});
res.once('end', function () {
self.count.rcv += body.length;
if (callback) callback(body);
});
});
req.once('error', function(err) {
if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' '+path+' failed: '+err,true);
self.emit('error',err);
if (callback) callback();
});
req.end();
} else {
// XHR Browser
http.request({
host: snd.address,
port: snd.port,
path: path,
proto:'https',
method: 'GET',
headers: {
}
} , function(err,xhr,body) {
if (err) {
if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' '+path+' failed: '+err,true);
self.emit('error',err);
if (callback) callback();
} else {
self.count.rcv += body.length;
if (callback) callback(body);
}
});
}
};
/** Initialize AMP
*
*/
amp.https.prototype.init = function(callback) {
if (callback) callback();
};
/** Negotiate a virtual communication link (peer-to-peer).
* In oneway mode only a destination endpoint is set and it is assumed the endpoint can receive messages a-priori!
*
* typeof @snd = address
* typeof @callback = function
* typeof @connect = boolean is indicating an initial connect request and not an acknowledge
* typeof @key = private
* typeof @response = object
*
* +------------+
* VCMessageType (int16)
* Connection Port (port)
* Node ID (string)
* // Receiver IP Port (int32)
* +------------+
*
*/
amp.https.prototype.link=function(snd,connect,key,response) {
var self = this,
msg,
url;
if (this.verbose>1) this.debug('amp.link: to '+addr2url(snd));
// MULTICAST mode
// Add new link to cache of links
if (!snd) this.err('link: no destinataion set in MULTICAST mode');
if (snd.parameter && snd.parameter.secure) key=snd.parameter.secure;
url=addr2url(snd,true);
if (!this.links[url] || !this.links[url].snd.address) {
if (connect) snd.connect=true;
this.links[url]={
snd:snd,
state:AMState.AMS_NOTCONNECTED,
tries:0,
connect:connect,
live:options.AMC_MAXLIVE};
}
// Let watchdog handle connect request link messages
if (!this.inwatchdog && connect)
return this.watchdog(true);
// if (this.verbose>1) this.out('send link '+Io.inspect(snd));
msg={
type:AMMessageType.AMMLINK,
port:this.port,
node:this.node?this.node.id:'*',
index:this.index++,
magic:magic,
remote:snd.address,
};
if (key) msg.secure=key;
this.count.lnk++;
if (response)
this.response(response,msg);
else this.send(snd,msg,function (reply) {
if (is_error(reply)) return; // error
// start message collector thread after first link reply!
if ((self.mode & AMMode.AMO_CLIENT) && !self.links[url].collect) {
self.links[url].collect=setTimeout(function () {
self.collect(snd);
},0);
}
// handle reply
self.handle(reply,snd);
});
};
amp.https.prototype.ping=function(snd,response) {
var self = this,msg={};
msg.type=AMMessageType.AMMPING;
msg.port=this.port;
msg.index=this.index++;
msg.magic=magic;
if (this.verbose>1) this.debug('amp.ping'+(response?'in response':'')+': to '+addr2url(snd));
this.count.png++;
if (response)
this.response(response,msg);
else this.send(snd,msg,function (reply) {
if (is_error(reply)) return; // error
// handle reply
self.handle(reply,snd);
});
}
amp.https.prototype.pong=function(snd,response) {
var self = this,msg={};
msg.type=AMMessageType.AMMPONG;
msg.port=this.port;
msg.index=this.index++;
msg.magic=magic;
if (this.verbose>1) this.debug('amp.pong '+(response?'in response':'')+': to '+addr2url(snd));
this.count.png++;
if (response)
this.response(response,msg);
else this.send(snd,msg,function (reply) {
if (is_error(reply)) {
self.emit('error',reply);
}
});
}
/** HTTP PUT request to send a message and data to the AMP HTTP server.
*
* @param path
* @param data
*/
amp.https.prototype.put = function (snd,path,data) {
var self=this,
req,body;
this.count.snd = this.count.snd + path.length + data.length;
if (https) {
req = https.request({
host: snd.address,
port: snd.port,
path: path,
method: 'POST',
keepAlive: this.options.keepalive,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
} , function(res) {
if (res.setEncoding != null) res.setEncoding('utf8');
// TODO body=+chunk, res.on('end') ..??
res.once('data', function (chunk) {
// TODO
});
});
req.once('error', function(err) {
self.out('Warning: request to '+addr2url(snd)+' failed: '+err,true);
self.emit('error',err);
});
// write data to request body
req.write(data);
req.end();
} else {
// XHR Browser
http.request({
host: snd.address,
port: snd.port,
path: path,
proto: 'https',
method: 'POST',
body:data,
keepAlive: this.options.keepalive,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
} , function(err,xhr,body) {
if (err) {
if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' failed: '+err,true);
self.emit('error',err);
}
// TODO
})
}
};
amp.https.prototype.receiver = function (callback,rcv) {
var self = this;
if (callback) this.callback=callback;
if (this.mode & AMMode.AMO_SERVER) {
// Only if this is a public or locally visible network node this node
// should provide a server port!
if (rcv == undefined || rcv.address==undefined) rcv={},rcv.address=this.rcv.address;
if (rcv.port==undefined) rcv.port=this.rcv.port;
var _options = {
key : this.options.pem.key,
cert: this.options.pem.cert,
};
this.server=https.createServer(_options, function (request,response) {
if(parseQueryString(request.url).length==0) return response.end('EINVALID'); // accidental access by WEB browser
// console.log(request.connection.remoteAddress);
var i,body,
msg = parseQueryString(request.url),
remote = {address:request.connection.remoteAddress.replace(/^::ffff:/,'').replace(/^::1/,'localhost'),
port:'['+msg.port.replace(/:/g,'')+']' /* unique remote identifier */};
if (self.verbose>2)
console.log(request.method,request.url,msg,addr2url(remote),url2addr(addr2url(remote)));
// consistency check
if (msg.magic!=magic) return;
self.count.rcv += msg.length;
msg.type=AMMessageType[msg.type];
if (msg.secure) msg.secure=Net.port_of_str(msg.secure);
if (debug) console.log(Io.Time(),msg)
response.origin=request.headers.origin||request.headers.Origin;
Comp.string.match(request.method,[
['GET',function() {
if (msg.type==AMMessageType.AMMCOLLECT)
self.collecting(msg,remote,response);
else
self.handle(msg,remote,response);
}],
['POST',function() {
body = '';
request.on('data', function (chunk) {
body = body + chunk;
});
request.on('end', function () {
msg.data=Buffer(body,'hex');
self.count.rcv += msg.data.length;
if (msg.cmd) msg.cmd=Number(msg.cmd);
self.handle(msg,remote,response);
});
}]
])
});
this.server.on("connection", function (socket) {
socket.setNoDelay(true);
});
this.server.on("error", function (err) {
self.out('Warning: receiver failed: '+err,true);
if (err) self.err(err);
});
this.server.listen(rcv.port,function (err) {
// Try to get network IP address of this host
if (!err) getNetworkIP(undefined,function (err,ip) {
if (!err) self.rcv.address=isLocal(ip)?options.localhost:ip;
if (self.verbose) self.out('IP port '+addr2url(self.rcv)+ ' (proto '+self.options.proto+')',true);
if (err) return self.out("! Unable to obtain network connection information: "+err,true);
});
if (callback) callback(err);
});
}
if (this.mode & AMMode.AMO_CLIENT) {
// If this is a hidden node (e.g., inside a WEB browser), we have to connect to a remote public server
// by using stalled GET requests.
if (callback) this.callback=callback;
}
}
/** Reply to a request (msg.tid contains request tid)
*/
amp.http.prototype.reply = function (cmd,msg,snd) {
this.request(cmd,msg,snd);
}
/** Send a response reply for a pending HTTP GET/PUT request (AMO_SERVER)
*
*/
amp.https.prototype.response = function (response,msg) {
var data=msg2JSON(msg), header;
if (response.origin!=undefined)
header={'Access-Control-Allow-Origin': response.origin,
'Access-Control-Allow-Credentials': 'true',
'Content-Type': 'text/plain'};
else
header={'Content-Type': 'text/plain'};
if (this.options.keepalive) header["Connection"]="keep-alive";
response.writeHead(200,header);
response.write(data);
if (debug) console.log(Io.Time(),msg)
response.end();
}
/** Send a request message to a remote node endpoint
*
* function (cmd:integer,msg:Buffer,snd:address)
*/
amp.https.prototype.request = function (cmd,msg,snd) {
var self=this,req={},
size = msg.data.length,
tid = msg.tid||Comp.random.int(65536/2);
if (snd==undefined) this.err('request: snd=null');
req.type=AMMessageType.AMMRPC;
req.tid=tid; // Transaction Message ID
req.port=this.port; // This AMP id
req.cmd=cmd;
req.size=size;
req.magic=magic;
req.data=msg.data;
this.send(snd,req);
}
amp.https.prototype.scan=function(snd,response,callback) {
var self = this,msg={};
msg.type=response?AMMessageType.AMMACK:AMMessageType.AMMSCAN;
msg.port=this.port;
msg.magic=magic;
if (response) msg.info=snd.info;
if (this.verbose>1 && snd) this.debug('amp.scan: to '+addr2url(snd)+' '+(response?'R':''));
if (response)
this.response(response,msg);
else
this.send(snd,msg,function (reply) {
callback(reply)
});
}
/** Main entry for requests with JSON interface. Multiplexer for HTTP GET/PUT.
*
* msg: JSON
* callback : function (reply:object)
*/
amp.https.prototype.send = function (snd,msg,callback) {
var path,
url,
body,
self=this;
// Create query selector
path = formatQueryString(msg);
if (typeof snd.port == 'string') {
url=addr2url(snd,true);
// If Pending get from client
// Else queue message, client will collect them later (or never)
if (this.links[url]) {
if (!this.links[url].msgqueue) this.links[url].msgqueue=[];
if (this.links[url].collecting) {// pending AMMCOLLECT request
if (this.verbose>1) this.debug('REPLY msg '+AMMessageType.print(msg.type)+' to '+url);
this.response(this.links[url].collecting,{msg:[msg]});
this.links[url].collecting=undefined;
} else {
if (this.verbose>1) this.debug('QUEUE msg '+AMMessageType.print(msg.type)+' for '+url);
this.links[url].msgqueue.push(msg);
}
}
} else if (msg.data!=undefined) {
// Convert buffer data to hex formatted string
body=msg.data.toString('hex');
this.put(snd,path,body,function (body) {
if (is_error(body)) self.emit('error',body);
else if (!is_status(body)) self.emit('error','EINVALID');
// No reply expected!
});
} else {
this.get(snd,path,function (body) {
var xml,i,
reply;
if (!body || is_error(body)) {
self.emit('error','EINVALID');
} else {
reply=JSON2msg(body);
// { status:string,reply:*,msg?:{}[],..}
}
if (callback) callback(reply);
});
}
}
// Start AMP watchdog and receiver
amp.https.prototype.start = function(callback) {
var self=this,
s=this.secure?' (security port '+Sec.Port.toString(this.secure)+')':'';
if (this.verbose>0 && this.mode & AMMode.AMO_SERVER)
this.out('Starting ' + addr2url(this.rcv)+' ['+AMMode.print(this.mode)+'] (proto '+this.proto+')'+s);
if (this.verbose>0 && this.mode & AMMode.AMO_CLIENT)
this.out('Starting ['+AMMode.print(this.mode)+'] (proto http)');
this.watchdog(true);
if (!this.server && this.mode & AMMode.AMO_SERVER) {
// After stop? Restart receiver.
this.receiver();
}
if (callback) callback();
}
// Stop AMP
amp.https.prototype.stop = function(callback) {
if (this.verbose>0 && this.mode & AMMode.AMO_SERVER)
this.out('Stopping ' + addr2url(this.rcv)+' ['+AMMode.print(this.mode)+'] (proto '+this.proto+')'+s);
if (this.verbose>0 && this.mode & AMMode.AMO_CLIENT)
this.out('Stopping ['+AMMode.print(this.mode)+'] (proto http)');
if (this.links) for(var p in this.links) {
if (this.links[p]) {
// Try to unlink remote endpoint
this.unlink(this.links[p].snd);
this.links[p].state=AMState.AMS_NOTCONNECTED;
if (this.links[p].collect) clearTimeout(this.links[p].collect),this.links[p].collect=undefined;
}
}
if (this.timer) clearTimeout(this.timer),this.timer=undefined;
if (this.server) this.server.close(),this.server=undefined;
if (callback) callback();
}
// Unlink remote endpoint
amp.https.prototype.unlink=function(snd) {
var self = this,msg,
url = snd?addr2url(snd,true):null;
if (this.mode&AMMode.AMO_MULTICAST) {
if (!this.links[url] || this.links[url].state!=AMState.AMS_CONNECTED) return;
} else {
if (this.links.state!=AMState.AMS_CONNECTED) return;
}
msg={type:AMMessageType.AMMUNLINK,port:this.port,node:this.node?this.node.id:'*',index:this.index++,magic:magic};
this.send(snd,msg,function (reply) {
// handle reply
if (reply) {}
});
this.emit('route-',addr2url(snd,true));
if (this.mode&AMMode.AMO_MULTICAST) {
this.links[url].state=AMState.AMS_NOTCONNECTED;
if (!this.links[url].snd.connect) this.links[url].snd={};
} else {
this.links.state=AMState.AMS_NOTCONNECTED;
if (!this.links.snd.connect) this.links.snd={};
}
this.cleanup(url);
}
/** Install a watchdog timer.
*
* 1. If link state is AMS_NOTCONNECTED, retry link request if this.links[].snd is set.
* 2. If link state is AMS_CONNECTED, check link end point.
* 3, If link state is AMS_RENDEZVOUS, get remote endpoint connectivity via broker
*
* @param run
*/
amp.https.prototype.watchdog = function(run,immedOrDelay) {
var self=this;
if (this.timer) clearTimeout(self.timer),this.timer=undefined;
if (run) self.timer=setTimeout(function () {
if (!self.timer || self.inwatchdog) return; // stopped or busy?
self.timer = undefined;
self.inwatchdog=true;
function handle(obj,url) {
if (self.verbose>1) self.debug('Watchdog: handle link ('+url+') '+
(obj.snd?addr2url(obj.snd):'')+' in state '+
AMState.print(obj.state)+' live '+obj.live);
switch (obj.state) {
case AMState.AMS_CONNECTED:
if (obj.live == 0) {
// No PING received, disconnect...
if (self.verbose>0)
self.out('Endpoint ' + addr2url(obj.snd) +
' not responding, propably dead. Unlinking...',true);
obj.state = AMState.AMS_NOTCONNECTED;
self.emit('route-',addr2url(obj.snd,true));
self.cleanup(url,obj.snd.connect);
if (obj.snd.connect) self.watchdog(true,2000);
} else {
obj.tries=0;
obj.live--;
self.watchdog(true);
if (self.mode&AMMode.AMO_MULTICAST) self.ping(obj.snd);
else self.ping();
}
break;
case AMState.AMS_NOTCONNECTED:
case AMState.AMS_PAIRED:
if (obj.snd.port && typeof obj.snd.port == 'number') {
// Try link to specified remote endpoint obj.snd
if (self.verbose>0 && obj.tries==0)
self.out('Trying link to ' + addr2url(obj.snd),true);
self.link(obj.snd);
obj.tries++;
if (obj.tries < options.TRIES) self.watchdog(true);
else {
self.out('Giving up to link '+addr2url(obj.snd),true);
self.emit('error','link',addr2url(obj.snd));
obj.snd={},obj.tries=0;
}
}
break;
// AMP P2P Control
case AMState.AMS_RENDEZVOUS:
obj.send(
{type:'register',name: self.rcv.name, linfo: self.rcv},
self.broker,
function () {}
);
self.watchdog(true);
break;
case AMState.AMS_REGISTERED:
if (obj.tries < options.TRIES && obj.snd.name) {
obj.tries++;
self.send(
{type:'pair', from:self.rcv.name, to: obj.snd.name},
self.broker,
function () {
}
);
}
if (obj.tries < options.TRIES) self.watchdog(true);
break;
}
}
for(var p in self.links) if (self.links[p]) handle(self.links[p],p);
self.inwatchdog=false;
},immedOrDelay==true?0:immedOrDelay||options.TIMER);
};
};
BundleModuleCode['db/db']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2018 bLAB
** $CREATED: 09/02/16 by sbosse.
** $VERSION: 1.6.3
**
** $INFO:
**
** JavaScript AIOS: SQL Service API
**
** Sqlc: SQLD Database Client Module using named FIFO pipes OR built-in SQL server (see below)
**
** path_in: the write to SQLD channel (SQLD input)
** path_out: the read from SQLD channel (SQLD output)
**
**
** Sqld: SQL Database Server Module (and client interface)
** -------------------------------------------------------
**
** Using built-in sqlite3 module accessing a database file (jx: embedded, node.js: native module)
**
** Example:
**
** db = sql('/var/db/sensors.sql',{mode:'r+'}); // or in memory
** db = sql(':memory:',{mode:'r+'})
**
** db.init();
** db.createTable('sensors',{name:'',value:0, unit:''});
** db.insertTable('sensors',{name:'current',value:1.0, unit:'A'});
** db.insertTable('sensors',{name:'voltage',value:12.1, unit:'V'});
** db.readTable('sensors',function (res) {
** print('callback',res);
** });
** print(db.readTable('sensors'));
** print(db.select('sensors','*'));
** print('finished')
**
** Synchronous Version (using blocking IO, readFileSync)
**
** TODO: Merge with dbS!!!!!
** Options: Sync/Async/Server ...
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var Fs = Require('fs');
var current={};
var Aios=none;
var NL='\n';
var options = {
version:'1.6.3'
}
function await() {
if (jxcore) jxcore.utils.jump();
}
function wakeup() {
if (jxcore) jxcore.utils.continue();
}
function exists(path) {
try {
var fd=Fs.openSync(path,'r');
Fs.close(fd);
return true;
} catch (e) { return false }
}
/* Some hacks for wine/win32 node.js/nw.js */
var Buffer = require('buffer').Buffer;
function tryReadSync(fd, buffer, pos, len) {
var threw = true;
var bytesRead;
try {
bytesRead = Fs.readSync(fd, buffer, pos, len);
threw = false;
} finally {
if (threw) fs.closeSync(fd);
}
return bytesRead;
}
function readFileSync(path,encoding) {
var fd = Fs.openSync(path, 'r', 666),
bytesRead,buffer,pos=0,
buffers=[];
do {
buffer = Buffer(8192);
bytesRead = tryReadSync(fd, buffer, 0, 8192);
if (bytesRead !== 0) {
buffers.push(buffer.slice(0, bytesRead));
}
pos += bytesRead;
} while (bytesRead !== 0);
Fs.closeSync(fd);
buffer = Buffer.concat(buffers, pos);
if (encoding) buffer = buffer.toString(encoding);
return buffer;
}
function sleep(time) {
var stop = new Date().getTime();
while(new Date().getTime() < stop + time) {
;
}
}
function writeSync(path,str) {
var n=0;
var fd = Fs.openSync(path,'r+');
n=Fs.writeSync(fd, str);
Fs.closeSync(fd);
return n;
}
/******************* SQLC ************************/
var sqlc = function (path,chan) {
this.path=path;
// Client -> Server
this.input=none;
// Server -> Client
this.output=none;
this.chan=chan;
this.id='SQLC';
this.log=function (msg) {
((Aios && Aios.print)||Io.log)('[SQLC] '+msg);
}
this.todo=[];
}
/** Return string list of comma seperated value list
* + Strip string delimeters ''
*
*/
sqlc.prototype.args = function (msg) {
var args=Comp.string.split(',',msg);
return Comp.array.filtermap(args, function (arg) {
if (arg=='') return none;
else if (Comp.string.get(arg,0)=="'") return Comp.string.trim(arg,1,1);
else return arg;
});
};
/** Create a numeric matrix
*
* typeof @header = * [] is type interface provider for all rows;
*
*/
sqlc.prototype.createMatrix = function (matname,header,callback) {
var repl,
intf='', line='', sep='', self=this;
if (!this.connected) return callback?callback(false):false;
if (header.length==0) return false;
Comp.array.iter(header,function (col,i) {
intf += (sep+'c'+(i+1)+(Comp.obj.isNumber(col)?' integer':' varchar(32)')); sep=',';
});
sep='';
function done(_repl) {
repl=_repl[0];
if (!repl || self.err(repl)) {current.error=repl;return 0;}
else return 1;
}
return this.seq([
['exec','drop table if exists '+matname, done],
['exec','create table '+matname+' ('+intf+')', done]
],callback);
}
/** Create a generic table
*
* typeof @header = {} is type and name interface provider for all rows;
*
*/
sqlc.prototype.createTable = function (tabname,header,callback) {
var repl,
intf='', sep='', self=this;
if (!this.connected) return callback?callback(false):false;
// if (header.length==0) return false;
Comp.obj.iter(header,function (attr,key) {
intf += (sep+key+(Comp.obj.isNumber(attr)?' integer':' varchar(32)')); sep=',';
});
function done(_repl) {
repl=_repl[0];
if (!repl || self.err(repl)) {current.error=repl;return 0;}
else return 1;
}
return this.seq([
['exec','drop table if exists '+tabname, done],
['exec','create table '+tabname+' ('+intf+')', done],
],callback);
}
/** Check end message
*
*/
sqlc.prototype.end = function (msg) { return msg.indexOf('END') == 0 }
/** Check error message
*
*/
sqlc.prototype.err = function (msg) {
if (!msg) return false;
if (Comp.obj.isObject(msg)) return msg.errno != undefined;
if (Comp.obj.isString(msg)) return msg.indexOf('ERR') == 0 || msg.indexOf('Error') == 0;
return false;
}
/** Execute: send a request and wait for reply.
*/
sqlc.prototype.exec = function (cmd,callback) {
var n,str='',fd;
if (!this.connected) return callback?callback(none):none;
n = Fs.writeSync(this.input,cmd+NL);
if (n<=0) {
if (callback) { callback(none); return;}
else return none;
}
str = readFileSync(this.output,'utf8');
if (callback) callback(str?Comp.array.filter(Comp.string.split('\n',str),function(line) {return line!=''}):none);
else return str?Comp.array.filter(Comp.string.split('\n',str),function(line) {return line!=''}):none;
};
/** GET ROW operation
* @fmt: {}|none
* @callback: function () -> string [] | * [] | none
*
*/
sqlc.prototype.get = function (fmt,callback) {
var row,self=this,repl;
if (!this.connected) return callback?callback(none):none;
function done(_repl) {
repl=_repl?_repl[0]:none;
if (!repl || self.err(repl)) {current.error=repl; row=none; return 0;}
else return 1;
}
this.seq([
['exec','get row',function (_repl) {
var cols,i,p;
if (!row) row=[];
if (done(_repl) && !self.end(repl)) {
repl=Comp.string.replace_all('\n','',repl);
cols=Comp.string.split(',',repl);
if (fmt) {
i=0;row=[];
for(p in fmt) {
switch (fmt[p]) {
case 'string':
row.push(cols[i]);
break;
case 'number':
row.push(Number(cols[i]));
break;
default:
row.push(cols[i]);
}
i++;
}
} else row=cols;
}
return 1;
}]
], callback?function (res) { if (res) callback(row); else callback(none);}:wakeup);
if (callback) return; else if (row) return row; else { await(); return row }
}
sqlc.prototype.getError = function () { var err=current.error; current.error=undefined; return current.error || 'OK'}
/** Setup client-server connection.
* Only the input stream is opened (used for sending data to the SQLD server).
*
*/
sqlc.prototype.init = function (callback) {
var path_in = Comp.printf.sprintf("%sI%s%d",this.path,this.chan<10?'0':'',this.chan),
path_out = Comp.printf.sprintf("%sO%s%d",this.path,this.chan<10?'0':'',this.chan),
stat,repl,self=this;
this.id = Comp.printf.sprintf("[SQLC%s%d]",this.chan<10?'0':'',this.chan);
this.log (Comp.printf.sprintf("%s Connecting to server channel %s...",
this.id,path_in));
if (!exists(path_in) || !exists(path_out)) {
this.log (Comp.printf.sprintf("%s Connecting to server channel %s failed: %s",
this.id,path_in,'No such file(s)'));
if (callback) {callback(false); return;}
else return false;
}
try {
this.input = Fs.openSync(path_in,'r+');
} catch (e) {
this.log (Comp.printf.sprintf("%s Connecting to server channel %s failed: %s",
this.id,path_in,e));
if (callback) {callback(false); return;} else return false;
}
// this.input = path_in;
this.output = path_out;
this.connected = true;
return this.seq([
['set','nl',function (repl) {return (repl && !self.err(repl[0]))}],
['set','csv',function (repl) {return (repl && !self.err(repl[0]))}]
], callback);
}
/** Insert operation
*
*/
sqlc.prototype.insert = function (tbl,row,callback) {
var repl,
line='',
sep='',
self=this;
if (!this.connected) return callback?callback(false):false;
if (Comp.obj.isArray(row))
Comp.array.iter(row,function (col,i) {line += sep+(Comp.obj.isNumber(col)?int(col):"'"+col+"'"); sep=',';});
else if (Comp.obj.isObject(row))
Comp.obj.iter(row,function (attr,key) {line += sep+(Comp.obj.isNumber(attr)?int(attr):"'"+attr+"'"); sep=',';});
else
throw 'sql.insert: row neither array nor object!';
function done(_repl) {
repl=_repl[0];
if (!repl || self.err(repl)) {current.error=repl;return 0;}
else return 1;
}
return this.seq([
['exec','insert into '+tbl+' values ('+line+')', done]
], callback);
}
/** Insert a matrix row
*
*/
sqlc.prototype.insertMatrix = function (matname,row,callback) {
var repl,
line='',
sep='',
self=this;
if (this.connected) return callback?callback(false):false;
Comp.array.iter(row,function (col,i) {line += sep+(Comp.obj.isNumber(col)?int(col):"'"+col+"'"); sep=',';});
function done(_repl) {
repl=_repl[0];
if (!repl || self.err(repl)) {current.error=repl;return 0;}
else return 1;
}
return this.seq([
['exec','insert into '+matname+' values ('+line+')', done]
], callback);
}
sqlc.prototype.insertTable = sqlc.prototype.insert;
/** Read a matrix; return [][]
*
*/
sqlc.prototype.readMatrix = function (matname,callback) {
var mat,repl,cols, self=this;
if (!this.connected) return callback?callback(none):none;
function done(_repl) {
repl=_repl[0];
if (!repl || self.err(repl)) {current.error=repl; mat=none; return 0;}
else return 1;
}
function doneSelect(_repl,_rows) {
repl=_repl[0];
if (_rows) { mat=_rows; return 2 }; // Maybe we got already all rows?
if (!repl || self.err(repl)) {current.error=repl; tbl=none; return 0;}
else return 1;
}
this.seq([
['exec','select * from '+matname,doneSelect],
['exec','get row',function (_repl) {
if (done(_repl)) {
if (!mat) mat=[];
if (rows) { print('done'); mat=rows; return true /* all done */ };
if (!self.end(repl)) {
cols=_;
if (typeof repl == 'string') {
repl=Comp.string.replace_all('\n','',repl);
var cols=Comp.array.map(Comp.string.split(',',repl),function (col) {
return Number(col);
});
} else if (Comp.obj.isArray(repl)) {
// Array
cols=repl; // !!!
}
mat.push(cols);
return -1;
} else return 1;
} else return 0;
}]
], callback?function (res) { if (res) callback(mat); else callback(none);}:wakeup);
if (callback) return; else if (mat) return mat; else { await(); return mat };
}
/** Read a generic table; return {}[]
*
*/
sqlc.prototype.readTable = function (tblname,callback) {
var tbl,intf=[],repl,cols, self=this;
if (!this.connected) return callback?callback(none):none;
function done(_repl) {
repl=_repl[0];
if (!repl || self.err(repl)) {current.error=repl; tbl=none; return false;}
else return true;
}
function doneSelect(_repl,_rows) {
repl=_repl[0];
// Maybe we got already all rows?
if (_rows) { tbl=_rows; return 2};
if (!repl || self.err(repl)) {current.error=repl; tbl=none; return 0;}
else return 1;
}
this.seq([
// TODO: Check SQLC API; this is only valid for SQLD!
['get',"select * from sqlite_master where type='table' and name='"+tblname+"'",function (_repl) {
var tokens;
if (done(_repl)) {
if (repl.sql) {
tokens=repl.sql.match(/\((.+)\)/)[1].split(',');
tokens.forEach(function (token) {
var cols=token.split(' ');
if (cols.length==2) {
intf.push({tag:cols[0],type:self.sqlType2native(cols[1])});
} else intf.push(null);
});
return !Comp.obj.isEmpty(intf)?1:0;
}
} else return 0;
}],
['exec','select * from '+tblname,doneSelect],
['exec','get row',function (_repl) {
var o={};
if (!tbl) tbl=[];
if (done(_repl)) {
if (!self.end(repl)) {
cols=_;
if (typeof repl == 'string') {
repl=Comp.string.replace_all('\n','',repl);
repl.split(',').forEach(function (e,i) {
var io=intf[i];
if (io) o[io.tag]=io.type=='number'?Number(e):e;
});
} else if (Comp.obj.isArray(repl)) {
// Array
repl.forEach(function (e,i) {
var io=intf[i];
if (io) o(io.tag)=io.type=='number'?Number(e):e;
});
}
tbl.push(o);
return -1;
} else return 1;
} else return 0;
}]
], callback?function (res) { if (res) callback(tbl); else callback(none);}:wakeup);
if (callback) return; else if (tbl) return tbl; else { await(); return tbl }
}
/** SELECT operation
* tbl: string
* vars?: string | string []
* cond: string
* callback: function () -> status string
*
*/
sqlc.prototype.select = function (tbl,vars,cond,callback) {
var self=this,repl,stat;
function done(_repl) {
repl=_repl?_repl[0]:none;
if (!repl || self.err(repl)) {current.error=repl; return 0;}
else return 1;
}
if (vars==undefined) vars='*';
stat = this.seq([
['exec',Comp.printf.sprintf('select %s from %s%s',
Comp.obj.isArray(vars)?Comp.printf.list(vars):vars,
tbl,
cond?(' '+cond):''),done]
],callback?callback:wakeup);
if (!callback) await();
return stat;
}
/** Execute a SQL operation sequence
** todo format: [op: string,
** args?: string,
** result: function returning boolean (false: break (error), true: next, _:loop)]
** callback: function () -> status
*/
sqlc.prototype.seq = function (todo,callback) {
var l=todo,self=this,status,res,
next;
if (callback) { // Async non.block.
function Todo() {
if (self.todo.length>0) {
var f = Comp.array.head(self.todo);
self.error=undefined;
f(_,function () {
self.todo = Comp.array.tail(self.todo);
Todo();
});
}
}
next=function (loop,finalize) {
if (l.length==0 && !loop) { callback(status); if (finalize) finalize() }
else {
var hd;
if (!loop) {
hd= Comp.array.head(l);
l = Comp.array.tail(l);
} else hd=loop;
switch (hd[0]) {
case 'set':
self.set(hd[1],function (repl) {
status=hd[2](repl);
if (status==1) next(_,finalize); else callback(status);
});
break;
case 'exec':
self.exec(hd[1],function (repl) {
status=hd[2](repl);
if (status==1) next(_,finalize);
else if (status==-1) next(hd,finalize);
else callback(status);
});
break;
}
}
}
self.todo.push(next);
if (self.todo.length==1) Todo();
return;
} else { // Sync block.
next=function (loop) {
if (l.length==0 && !loop) return status;
else {
if (!loop) {
hd= Comp.array.head(l);
l = Comp.array.tail(l);
} else hd=loop;
switch (hd[0]) {
case 'set':
status=self.set(hd[1]);
if (status==1) next(); else return status;
break;
case 'exec':
res=self.exec(hd[1]);
status=hd[2](res);
if (status==1) next(); else if (status==-1) next(hd); else return status;
break;
}
}
}
return next();
}
};
/** Set a SQLD flag (nl,csv,..).
*
*/
sqlc.prototype.set = function (flag,callback) {
var n,fd,str='';
if (!this.connected) return callback?callback(false):false;
n=Fs.writeSync(this.input, 'set '+flag);
if (n<=0) {
if (callback) { callback(none); return;}
else return none;
}
str = readFileSync(this.output,'utf8');
if (callback) callback(str?Comp.array.filter(Comp.string.split('\n',str),function(line) {return line!=''}):none);
else return str?Comp.array.filter(Comp.string.split('\n',str),function(line) {return line!=''}):none;
};
sqlc.prototype.sqlType2native = function (str) {
if (str=='integer') return 'number';
if (str.indexOf('varchar')==0) return 'string';
}
/** Write a matrix [][] (create + insert values)
*
*/
sqlc.prototype.writeMatrix = function (matname,matrix,callback) {
var repl, line='', self=this,
intf='', sep='', i=0, row;
if (!this.connected) return callback?callback(false):false;
if (matrix.length==0) return false;
Comp.array.iter(matrix[0],function (col,i) {
intf += sep+'c'+(i+1)+(Comp.obj.isNumber(col)?' integer':' varchar(32)'); sep=',';
});
row=matrix[0];
Comp.array.iter(row,function (col,i) {
line += sep+(Comp.obj.isNumber(col)?int(col):"'"+col+"'"); sep=',';
});
function done(_repl) {
repl=_repl[0];
if (!repl || self.err(repl)) {current.error=repl;return 0;}
else return 1;
}
seq=[
['exec','drop table if exists '+matname,done],
['exec','create table '+matname+' ('+intf+')',done]
];
for(i=0;i<matrix.length;i++) {
row=matrix[i];
line='',sep='';
Comp.array.iter(row,function (col,i) {
line += sep+(Comp.obj.isNumber(col)?int(col):"'"+col+"'"); sep=',';
});
seq.push(['exec','insert into '+matname+' values ('+line+')',done]);
}
return this.seq(seq,callback);
}
/** Write a table {}[] (create + insert values)
*
*/
sqlc.prototype.writeTable = function (tblname,tbl,callback) {
var repl, line='', self=this,
intf='', sep='', i=0, row;
if (!this.connected) return callback?callback(false):false;
if (matrix.length==0) return false;
Comp.obj.iter(tbl[0],function (attr,key) {
intf += sep+key+(Comp.obj.isNumber(attr)?' integer':' varchar(32)'); sep=',';
});
row=matrix[0];
Comp.obj.iter(row,function (attr,key) {
line += sep+(Comp.obj.isNumber(attr)?int(attr):"'"+attr+"'"); sep=',';
});
function done(_repl) {
repl=_repl[0];
if (!repl || self.err(repl)) {current.error=repl;return 0;}
else return 1;
}
seq=[
['exec','drop table if exists '+tblname,done],
['exec','create table '+tblname+' ('+intf+')',done]
];
for(i=0;i<matrix.length;i++) {
row=tbl[i];
line='',sep='';
Comp.array.iter(row,function (col,i) {
line += sep+(Comp.obj.isNumber(col)?int(col):"'"+col+"'"); sep=',';
});
seq.push(['exec','insert into '+tblname+' values ('+line+')',done]);
}
return this.seq(seq,callback);
}
sqlc.prototype.setLog = function (f) {
this.log=f;
}
var Sqlc = function (path,chan) {
var obj = new sqlc(path,chan);
return obj;
};
/******************* SQLD ************************/
var sqld = function (file,options) {
this.file=file||':memory:';
this.options=options||{};
this.id='SQLD';
this.todo=[];
this.log=function (msg) {
((Aios && Aios.print)||Io.log)('[SQLD] '+msg);
}
try {
// Bulit-in SQL3 server module?
this.sqlite3=require('sqlite3');
} catch (e) {
throw e;
}
if (this.options.mode == 'r+')
this.mode = this.sqlite3.OPEN_READWRITE;
else if (this.options.mode == 'w+')
this.mode = this.sqlite3.OPEN_READWRITE | this.sqlite3.OPEN_CREATE;
else
this.mode = this.sqlite3.OPEN_READONLY
}
/** Create a generic table
*
* typeof @header = {} is type and name interface for all rows;
*
*/
sqld.prototype.createTable = sqlc.prototype.createTable;
/** Check end message
*
*/
sqld.prototype.end = sqlc.prototype.end;
/** Check error message
*
*/
sqld.prototype.err = sqlc.prototype.err;
sqld.prototype.getError = sqlc.prototype.getError;
/** Initialize and create DB instance
*
*/
sqld.prototype.init = function () {
this.db = new this.sqlite3.Database(this.file,this.mode);
this.db.serialize();
this.log('Opened DB '+this.file+' in mode '+(this.mode & this.sqlite3.OPEN_READWRITE?'RW':'R')+
(this.mode & this.sqlite3.OPEN_CREATE?'C':''));
this.connected = true;
}
sqld.prototype.insertMatrix = sqlc.prototype.insertMatrix;
sqld.prototype.insertTable = sqlc.prototype.insert;
/** Read a matrix; return number [][]
*
* method (matname:string,callback:function)
*
*/
sqld.prototype.readMatrix = sqlc.prototype.readMatrix;
/** Read a generic table; return {}[]
*
*/
sqld.prototype.readTable = sqlc.prototype.readTable;
/** Execute a SQL operation sequence
** todo format: [op: string,
** args?: string,
** result: function returning boolean (false: break (error), true: next, _:loop)]
** callback: function () -> status
*/
sqld.prototype.seq = function (todo,callback) {
var l=todo,self=this,status,res,row,rows,cols,
next;
if (callback) { // Async non.block.
function Todo() {
if (self.todo.length>0) {
var f = Comp.array.head(self.todo);
f(_,function () {
self.todo = Comp.array.tail(self.todo);
Todo();
});
}
}
next=function (loop,finalize) {
var p;
if (l.length==0 && !loop) { callback(status); if (finalize) finalize()}
else {
var hd;
if (!loop) {
hd= l.shift();
} else hd=loop;
switch (hd[0]) {
case 'exec':
if (hd[1]=='get row') {
if (rows && rows.length) {
row=rows.shift();
cols=[];
for(p in row) cols.push(row[p]);
status=hd[2]([cols.join(',')]);
} else status=hd[2](['END']);
if (status==1) next(_,finalize);
else if (status==-1) next(hd,finalize);
else callback(status);
} else if (hd[1].indexOf('select') == 0) {
self.db.all(hd[1],[],function (repl,_rows) {
rows=_rows;
if (repl!=null && !Comp.obj.isArray(repl)) repl=[repl];
if (repl==null) repl=['OK'];
status=hd[2](repl,_rows);
if (status==1) next(_,finalize);
else if (status==-1) next(hd,finalize);
else if (status==2) { l=[]; next(_,finalize) }
else callback(status);
});
} else {
self.db.run(hd[1],[],function (repl) {
rows=_rows;
if (repl!=null && !Comp.obj.isArray(repl)) repl=[repl];
if (repl==null) repl=['OK'];
status=hd[2](repl);
if (status==1) next(_,finalize);
else if (status==-1) next(hd,finalize);
else if (status==2) { l=[]; next(_,finalize) }
else callback(status);
});
}
break;
case 'get':
self.db.get(hd[1], function (err, table) {
var repl;
if (err) repl=[err];
else repl=[table];
status=hd[2](repl);
if (status==1) next(_,finalize);
else if (status==-1) next(hd,finalize);
else callback(status);
});
break;
}
}
}
self.todo.push(next);
if (self.todo.length==1) Todo();
return;
} else { // Sync block., limited usability
next=function (loop) {
if (l.length==0 && !loop) return status;
else {
if (!loop) {
hd=l.shift();
} else hd=loop;
switch (hd[0]) {
case 'exec':
// w/o callback we get no status immediately!
res='OK';
self.db.run(hd[1], function (err) {
if (err) self.log(err)
});
status=hd[2](res);
if (status==1) return next();
else if (status==-1) return next(hd);
else if (status==2) { l=[]; next() }
else return status;
break;
}
}
}
return next();
}
};
/** SELECT operation returning rows (implicit GET ROW)!!!!
* tbl: string
* vars?: string | string []
* cond: string
* callback: function () -> status string
*
*/
sqld.prototype.select = function (tbl,vars,cond,callback) {
var self=this,repl,stat,rows;
function done(_repl,_rows) {
repl=_repl?_repl[0]:none;
rows=_rows;
if (!repl || self.err(repl)) {current.error=repl; return 0;}
else return 1;
}
if (vars==undefined) vars='*';
stat = this.seq([
['exec',Comp.printf.sprintf('select %s from %s%s',
Comp.obj.isArray(vars)?Comp.printf.list(vars):vars,
tbl,
cond?(' '+cond):''),done]
],callback?callback:wakeup);
if (!callback) await();
return rows||stat;
}
sqld.prototype.sqlType2native = sqlc.prototype.sqlType2native;
/** Write a matrix [][] (create + insert values)
*
*/
sqld.prototype.writeMatrix = sqlc.prototype.writeMatrix;
var Sqld = function (file,options) {
var obj = new sqld(file,options);
return obj;
};
module.exports = {
current:function (module) { current=module.current; Aios=module; },
Sqlc:Sqlc,
Sqld:Sqld
};
};
BundleModuleCode['shell/shell']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 1-3-18 by sbosse.
** $VERSION: 1.36.1
**
** $INFO:
**
** JAM Shell Interpreter (Back end)
**
** Highly customizable command shell interpreter for JAMLIB
**
**
** typeof @options= {
** echo?: boolean,
** modules? : {
** csp? : Require('csp/csp'),
** csv? : Require('parser/papaparse')
** db? :
** des48? :
** doc? : Require('doc/doc'),
** http? : Require('http'),
** https? : Require('https'),
** httpserv? : Require('http/https'),
** logic?: Require('logic/prolog'),
** ml?: Require('ml/ml'),
** nn?: Require('nn/nn'),
** numerics?: Require('numerics/numerics'),
** sat?: Require('sat/sat'),
** readline? : Require('com/readline'),
** readlineSync? : Require('term/readlineSync'),
** sip? : Require('top/rendezvous'),
** sql? : Require('db/db'),
** },
** nameopts? : {length:8, memorable:true, lowercase:true},
** Nameopts? : {length:8, memorable:true, uppercase:true},
** output? : function, // AIOS output (for all, except if defined ..)
** outputAgent? : function, // Agent output
** outputAsync? : function, // AIOS/generic async output
** outputPrint? : function, // jamsh print output
** renderer? : renderer,
** server? : boolean,
** }
**
** $ENDOFINFO
*/
Require('os/polyfill');
var JamLib = Require('top/jamlib');
var util = Require('util');
var Comp = Require('com/compat');
var Io = Require('com/io');
var Sat = Require('dos/ext/satelize');
var Cluster = Require('shell/cluster');
var Sec = Require('jam/security');
var Rpc = Require('rpc/rpc');
var Esprima = Require("parser/esprima");
var Json = Require('jam/jsonfn');
var options = {
verbose : JamLib.environment.verbose||1,
version : '1.36.1',
}
Cluster.current(JamLib.Aios);
// Utils
if (typeof print == 'undefined') print=console.log;
DIR = JamLib.Aios.DIR;
var NET = Require('jam/ampCOM'),
url2addr=NET.url2addr,
addr2url=NET.addr2url;
function format(line) {
var msg;
switch (typeof line) {
case 'boolean': msg=line.toString(); break;
case 'string': msg=line; break;
case 'number': msg=line.toString(); break;
case 'function': msg=line.toString(); break;
case 'object': msg=Io.inspect(line); break;
default: msg='';
}
return msg;
}
/** Shell Interpreter Object
*
*/
function Shell (_options) {
if (!(this instanceof Shell)) return new Shell(_options);
this.options=Comp.obj.extend(options,_options);
this.modules=options.modules||{};
this.events = {};
this.env = {};
this.modules.forEach(function (mod,name) {
switch (name) {
case 'des48':
JamLib.Aios[name]=mod;
break;
case 'ml':
case 'nn':
case 'csp':
case 'sat':
case 'numerics':
mod.current(JamLib.Aios);
JamLib.Aios[name]=mod.agent;
JamLib.Aios.aios1[name]=mod.agent;
JamLib.Aios.aios2[name]=mod.agent;
JamLib.Aios.aios3[name]=mod.agent;
break;
case 'nlp':
case 'logic':
JamLib.Aios[name]=mod;
JamLib.Aios.aios1[name]=mod;
JamLib.Aios.aios2[name]=mod;
JamLib.Aios.aios3[name]=mod;
break;
}
})
if (!this.options.renderer) {
if (this.modules.doc) this.options.renderer=this.modules.doc.Renderer({lazy:true});
else
this.options.renderer = function (text) {
return text.replace(/\n:/g,'\n ');
}
}
}
Shell.prototype.cmd = function () { return this.env }
Shell.prototype.emit = function (ev,arg1,arg2,arg3,arg4) {
if (this.events[ev]) this.events[ev](arg1,arg2,arg3,arg4);
}
Shell.prototype.help = function() {
return this.options.renderer([
'# Usage',
' jamsh [-v] [script.js] [-- <script args>]',
'# Shell Commands',
'The following shell commands are avaiable:',
'',
'add({x,y})\n: Add a new logical (virtual) node',
'agent(id,proc?:boolean)\n: Returns the agent object (or process)',
'agents\n: Get all agents (id list) of current node',
'args\n: Script arguments',
'ask(question:string,choices: string [])\n: Ask a question and read answer. Available only in script mode.',
'array(n,init)\n: Creates initialised array',
'assign(src,dst)\n: Copy elements of objects',
'broker(ip)\n: Start a UDP rendezvous broker server',
'Capability\n: Create a security capability object',
'clock(ms)\n: Returns system time (ms or hh:mm:ss format)',
'cluster(desc)\n:Create a worker process cluster',
'config(options)\n: Configure JAM. Options: _print_, _printAgent_,_TSTMO_',
'configs\n: Get configuration of JAM AIOS',
'connect({x,y},{x,y})\n: Connect two logical nodes (DIR.NORTH...)',
'connect({to:dir)\n: Connect to physical node',
'connected(to:dir)\n: Check connection between two nodes',
'compile(function)\n: Compile an agent class constructor function',
'concat(a,b)->c\n: Concatenate two values',
'contains(a,v)->boolean\n: Check if array or object contains a value or oen in an array of values',
'copy(o)\n: Returns copy of record or array',
'create(ac:string|function,args:*[]|{},level?:number,node?)\n: Create an agent from class @ac with given arguments @args and @level',
'csp?\n: Constraint Solving Programming',
'csv?\n: CSV file reader and writer (read,write)',
'des48?\n: DES48 Encryption',
'disconnect({x,y},{x,y})\n: Disconnect two logical nodes',
'disconnect({to:dir)\n: Diconnect remote endpoint',
'env\n: Shell environment including command line arguments a:v',
'empty(a)->boolean\n: Test empty string, array, or object',
'exec(cnd:string)\n: Execute a jam shell command',
'exit\n: Exit shell',
'extend(level:number|number[],name:string,function,argn?:number|number[])\n: Extend AIOS',
'filter(a,f)->b\n: Filter array or object',
'http.get(url:string,path:string,callback?:function)\n: Serve HTTP get request',
'http.put(url:string,path:string,data,callback?:function)\n: Serve HTTP put request',
'http.GET(url:string,params:{},callback?:function)\n: Serve HTTP JSON get request',
'http.PUT(url:string,params:{},data,data,callback?:function)\n: Serve HTTP JSON put request',
'http.server(ip:string,dir:string,index?:string)\n: Create and start a HTTP file server',
'info(kind:"node"|"version"|"host",id?:string)->info {}\n: Return information (node)',
'inp(pattern:[],all?:boolean)\n: Read and remove (a) tuple(s) from the tuple space',
'Json\n:JSON+ serializer',
'kill(id:string|number)\n: Kill an agent (id="*": kill all) or a task (started by later)',
'last(object|array)\n: Return last element of array, string, or object',
'later(ms:number,callback:function(id,counter)->booleabn)\n: Execute a function later. If fucntion returns true, next cycle is started.',
'load(path:string,mimetype?)\n: Load a JSON or CSV file (autodetect) or custom',
'lookup(pattern:string,callback:function (string [])\n: Ask broker for registered nodes',
'locate(callback?:function)\n: Try to estimate node location (geo,IP,..)',
'log(msg)\n: Agent logger function',
'logic??\n: Generic predicate (pro)logic framework module',
'mark(tuple:[],millisec)\n: Store a tuple with timeout in the tuple space',
'merge\n: Add a column (array) to a matrix (array array)',
'ml?\n: Generic machine Learning framework module',
'name("node"|"world")\n: Return name of current node or wolrd',
'nlp?\n: Natural language processing framework module',
'node\n: Get or set current vJAM node (default: root) either by index or id name',
'nodes\n: Get all vJAM nodes',
'numerics?\n: Numerics module (fft, vector, matrix, ..)',
'nn?\n: Neural Network framework module',
'neg(v)->v\n: Negate number, array or object of numebrs',
'object(s)\n: Convert string to object',
'ofJSON(s)\n: Convert JSON to object including functions',
'on(event:string,handler:function)\n: Install an event handler. Events: "agent+","agent-","signal+","signal","link+","link-","exit"',
'open(file:string,verbose?:number)\n: Open an agent class file',
'out(tuple:[])\n: Store a tuple in the tuple space',
'os?\n: OS utils module',
'pluck(table,column)\n:Extracts a column of a table (array array or object array)',
'port(dir,options:{proto,secure},node)\n: Create a new physical communication port',
'Port\n: Create a security port',
'Private\n: Create a security private object',
'provider(function)\n: Register a tuple provider function',
'random(a,b)\n: Returns random number or element of array/object',
'rd(pattern:[],all?:boolean)\n: Read (a) tuple(s) from the tuple space',
'reverse(a)->b\n: Reverse array or string',
'rm(pattern:[],all?:boolean)\n: Remove (a) tuple(s) from the tuple space',
'sat?\n: Logic (SAT) Solver module',
'save(path:string,data:string,csv?:boolean)\n: Save a JSON or CSV file',
'script(file:string)\n: Load and execute a jam shell script',
'select(arr,a,b)\n: Split matrix (array array) by columns [a,b] or [a]',
'setlog(<flag>,<on>)\n: Enable/disable logging attributes',
'signal(to:aid,sig:string|number,arg?:*)\n: Send a signal to specifid agent',
'sleep(millisec)?\n:Suspend entire shell for seconds',
'start()\n: start JAM',
'stats(kind:"process"|"node"|"vm"|"conn")\n: Return statistics',
'stop()\n: stop JAM',
'sql(filename:string)\n: Open or create an SQL database. A memory DB can be created with _filename_=":memory:". Requires native sqlite3 plug-in.',
'Std.info/status/age/\n: Standard RPC AMP API',
'table?\n: Terminal table formatter',
'test(pattern:[])\n: Test existence of tuple',
'ts(pattern:[],callback:function(tuple)->tuple)\n: Update a tuple in the space (atomic action) - non-blocking',
'time()\n: print AIOS time',
'toJSON(o)\n: Convert object including functions to JSON',
'verbose(level:number)\n: Set verbosity level',
'versions()\n: Returns JAM shell and library version',
'uniqid\n: Returns a random name',
'utime()\n: Returns system time in nanoseconds',
'UI?\n: User Interface Toolkit',
'without(a,b)\n: Returns "a" without "b"',
'world\n: Returns world object',
].join('\n'));
}
/* Set-up the Interpreter
*
*/
Shell.prototype.init = function(callback) {
var self=this;
if (!this.options.server && this.modules.readline) {
this.rl = this.modules.readline.createInterface({
input: process.stdin,
output: process.stdout,
completer : function (cmdline) {
var args = Array.filter(String.split(' ', cmdline), function (str) {
return str != '';
});
var completed=cmdline;
var choices=[];
return [choices,completed];
}
});
this.rl.on('line', function (line) {
self.cmdline = line;
self.process(line)
self.rl.prompt();
});
this.rl.on('close', function () {
});
if (this.modules.doc)
this.output(this.modules.doc.Colors.bold('JAM Shell. Version '+this.options.version+' (C) Dr. Stefan Bosse'));
else
this.output('JAM Shell. Version '+this.options.version+' (C) Dr. Stefan Bosse');
this.rl.setPrompt('> ');
this.rl.prompt();
} else if (this.options.verbose) this.output('JAM Shell. Version '+this.options.version+' (c) Dr. Stefan Bosse');
this.jam=JamLib.Jam({
print: this.output.bind(this),
printAgent: this.outputAgent.bind(this),
printAsync: this.outputAsync.bind(this),
nameopts: this.options.nameopts,
Nameopts: this.options.Nameopts,
verbose: this.options.verbose,
type: this.options.type||'shell' // tyoe of root node
});
this.jam.init();
function error(msg) {
self.output('Error: '+msg);
}
self.log=self.output;
this.tasks = [];
this.env = {
Aios: this.jam.Aios,
DIR: this.jam.Aios.DIR,
add: this.jam.addNode.bind(this.jam),
agent: function (id,proc) {
var node = self.jam.world.nodes[self.jam.getCurrentNode()];
if (node) {
return proc?node.getAgentProcess(id):node.getAgent(id);
}
},
get agents () {
var node = self.jam.world.nodes[self.jam.getCurrentNode()];
if (node) {
return node.processes.table.map( function (pro) { return pro.agent.id });
}
},
angle: JamLib.Aios.aios0.angle,
args: options.args,
array : JamLib.Aios.aios0.array,
assign: JamLib.Aios.aios0.assign,
broker: function (ip) {
if (!self.modules.sip) return;
if (self.broker) self.broker.stop();
if (!ip) ip='localhost';
var ipport=10001,tokens = ip.toString().split(':');
if (tokens.length==2) ip=tokens[0],ipport=Number(tokens[1]);
self.broker=sip.Broker({ip:{address:ip,port:ipport},log:self.output.bind(self)});
self.broker.init();
self.broker.start();
},
Capability: JamLib.Aios.Sec.Capability,
clock: this.jam.clock.bind(this.jam),
cluster : function (options) {
return new Cluster(options,self.env);
},
compile: function (constr,name,options) {
try {
if (typeof name == 'object') // options?
return self.jam.compileClass(undefined,constr,name)
else
return self.jam.compileClass(name,constr,options||{verbose:self.options.verbose})
} catch (e) {
error(e)
}
},
config: function (options) {
JamLib.Aios.config(options);
},
get configs () {
return JamLib.Aios.configGet();
},
connect: function (n1,n2) {
if (n1 && n2)
return self.jam.connectNodes([n1,n2])
else
return self.jam.connectTo(n1)
},
connected: this.jam.connected.bind(this.jam),
concat: JamLib.Aios.aios0.concat,
contains: JamLib.Aios.aios0.contains,
copy: JamLib.Aios.aios0.copy,
create: this.jam.createAgent.bind(this.jam),
csp: JamLib.Aios.csp,
get current () { return JamLib.Aios.current },
delta: JamLib.Aios.aios0.delta,
des48: JamLib.Aios.des48,
disconnect: function (n1,n2) {
if (n1 && n2) {}
// TODO
else
return self.jam.disconnect(n1)
},
distance : JamLib.Aios.aios0.distance,
env: this.jam.environment,
empty: JamLib.Aios.aios0.empty,
extend: this.jam.extend.bind(this.jam),
exec: this.process.bind(this),
get exit () { process.exit() },
get help () { return self.help() },
filter:function (a,f) {
var res=[],len,len2,i,j,found;
if (Comp.obj.isArray(a) && Comp.obj.isFunction(f)) {
res=[];
len=a.length;
for(i=0;i<len;i++) {
var element=a[i];
if (f(element,i)) res.push(element);
}
return res;
} else if (Comp.obj.isArray(a) && Comp.obj.isArray(f)) {
res=[];
len=a.length;
len2=f.length;
for(i=0;i<len;i++) {
var element=a[i];
found=false;
for (j=0;j<len2;j++) if(element==f[j]){found=true; break;}
if (!found) res.push(element);
}
return res;
} else return undefined;
},
flatten: JamLib.Aios.aios0.flatten,
geoip : self.modules.geoip,
Json:Json,
ignore: function () { },
info: this.jam.info.bind(this.jam),
inspect: util.inspect,
inp: this.jam.inp.bind(this.jam),
kill: function (id) {
if (typeof id == 'string') self.jam.kill(id);
else if (typeof id == 'number' && id >= 0) {
if (self.tasks[id]) clearInterval(self.tasks[id]);
self.tasks[id]=null;
}
},
last : JamLib.Aios.aios0.last,
later: function (timeout,callback) {
var counter=0,id=self.tasks.length;
var timer=setInterval(function () {
try {
var res=callback(id,counter);
} catch (e) {
error(e);
res=0;
}
counter++;
if (!res) {
clearInterval(timer);
self.tasks[id]=null;
}
},timeout)
self.tasks[id]=timer;
return id;
},
load: function (file,mimetype) {
var obj,text = Io.read_file(file);
if (!text) return;
if (!mimetype && file.match(/\.js$/)) mimetype='JS';
if (!mimetype && file.match(/\.json$/)) mimetype='JSON';
switch (mimetype && mimetype.replace(/application\//,'')) {
case 'text':
return text;
};
var scanner = text.replace(/\/\/[^\n]*/g,'');
if (scanner.match(/^\s*{/)||scanner.match(/^\s*\[\s*{/)||scanner.match(/^\s*\[\s*\[/)||scanner.match(/^\s*\[\s*\{/)) {
switch (mimetype && mimetype.replace(/application\//,'')) {
case 'JS':
case 'JSOB':
eval('"use strict"; obj = '+text);
break;
case 'JSON':
default:
obj=self.env.ofJSON(text);
};
} else if (self.env.csv && self.env.csv.detect(text))
obj=self.env.csv.read(text,false,true);
return obj;
},
locate : this.jam.locate.bind(this.jam),
lookup: this.jam.lookup.bind(this.jam),
log: this.jam.log.bind(this.jam),
logic: JamLib.Aios.logic,
map : function (a,f) {
var res,i,p;
if (Comp.obj.isArray(a) && Comp.obj.isFunction(f)) {
res=[];
for (i in a) {
v=f(a[i],i);
if (v!=undefined) res.push(v);
}
return res;
} else if (Comp.obj.isObject(a) && Comp.obj.isFunction(f)) {
// Objects can be filtered (on first level), too!
res={};
for(p in a) {
v=f(a[p],p);
if (v != undefined) res[p]=v;
}
return res;
} else return undefined;
},
mark: this.jam.mark.bind(this.jam),
merge : function (a,b) {
if (Comp.obj.isMatrix(a) && Comp.obj.isArray(b)) {
a=a.map(function (row,i) { var _row=row.slice(); _row.push(b[i]); return _row })
}
return a
},
ml: JamLib.Aios.ml,
name: function (of,arg) {
switch (of) {
case 'node': return self.jam.getNodeName(arg);
case 'world': return self.jam.world.id;
}
},
neg: JamLib.Aios.aios0.neg,
get node () { return self.jam.getCurrentNode(true) },
set node (n) { return self.jam.setCurrentNode(n) },
get nodes () { return self.jam.world.nodes.map(function (node) { return node.id }) },
nlp: JamLib.Aios.nlp,
nn: JamLib.Aios.nn,
numerics: JamLib.Aios.numerics,
object: JamLib.Aios.aios0.object,
ofJSON: function (s) {
return self.jam.Aios.Code.Jsonf.parse(s,{})
},
on: function (ev,handler) {
switch (ev) {
case 'exit':
process.on('exit',handler); process.on('SIGINT',function () { process.exit() });
break;
default:
self.jam.on(ev,handler);
}
},
open: function (file,verbose) {
if (verbose==undefined) verbose=1;
return self.jam.readClass(file,{verbose:verbose}) },
os: self.modules.os,
out: this.jam.out.bind(this.jam),
pluck: function (table,column) {
var res=[];
for(var i in table) {
res.push(table[i][column]);
}
return res;
},
port: function (dir,options,node) {
if (typeof options == 'string') options={proto:options};
else options=options||{};
if (options.verbose==undefined) options.verbose=self.options.verbose;
if (options.multicast == undefined) options.multicast=!options.broker;
if (options.secure && options.secure.length!=Sec.PORT_SIZE) options.secure=Sec.Port.ofString(options.secure);
var port=self.jam.createPort(dir,options,node);
self.emit('port',dir,options);
return port;
},
Port: JamLib.Aios.Sec.Port,
print : function () {
if (arguments.length>1)
self.outputPrint(Array.prototype.slice.call(arguments).map(Io.inspect).join(' '));
else
self.outputPrint(arguments[0])
},
Private: JamLib.Aios.Sec.Private,
provider: function (provider) {
self.jam.world.nodes[0].ts.register(provider)
},
random: JamLib.Aios.aios.random,
rd: this.jam.rd.bind(this.jam),
reduce: JamLib.Aios.aios0.reduce,
reverse: JamLib.Aios.aios0.reverse,
Rights: JamLib.Aios.Sec.Rights,
rm: this.jam.rm.bind(this.jam),
Rpc: Rpc,
sat: JamLib.Aios.sat,
save : function (file,o,csv) {
if (csv && self.env.csv) {
self.env.csv.write(file,o[0],o.slice(1));
} else Io.write_file(file,self.env.toJSON(o))
},
schedule : this.jam.schedule.bind(this.jam),
script: function (file) {
var text=Io.read_file(file);
if (typeof text != 'string') text=text.toString();
self.process(text);
},
select : function (data,a,b) {
if (b==undefined) {
return data.map(function(object) {
return object[a];
});
} else {
return data.map(function(object) {
return object.slice(a,b+1);
});
}
},
setlog: function (attr,on) { self.jam.Aios.config(on?{'log+':attr}:{'log-':attr}) },
signal: this.jam.signal.bind(this.jam),
start: this.jam.start.bind(this.jam),
start0: this.jam.start0.bind(this.jam),
stats: this.jam.stats.bind(this.jam),
Std : JamLib.Aios.Amp.Rpc.Std,
step: this.jam.step.bind(this.jam),
stop: this.jam.stop.bind(this.jam),
time: this.jam.time.bind(this.jam),
Time: Io.Time,
test: this.jam.test.bind(this.jam),
toJSON: function (o) {
// return self.jam.Aios.Code.minimize(
return self.jam.Aios.Code.Jsonf.stringify(o)
},
ts: this.jam.ts.bind(this.jam),
uniqid: function (options) { return self.jam.Aios.aidgen(options) },
UI: self.modules.UI,
url: {
toAddr: self.jam.Aios.Amp.url2addr,
fromAddr: self.jam.Aios.Amp.addr2url,
},
utime : function () { hr=process.hrtime(); return hr[0]*1E9+hr[1] },
verbose: function (l) { self.jam.Aios.config({verbose:l}); self.options.verbose=l; },
versions: function () { return {shell:options.version,lib:JamLib.options.version, aios:JamLib.Aios.options.version} },
without: JamLib.Aios.aios0.without,
get world () { return self.jam.world },
}
if (this.options.extensions) {
for(var p in this.options.extensions) this.env[p]=this.options.extensions[p];
}
if (this.modules.table) this.env.Table = this.modules.table;
// Module dependent commands
// HTTP
if (this.modules.http) this.env.http = {
get: function (url,path,callback) {
var snd=url2addr(url),
proto = snd.proto || 'http';
if (proto == 'https' && !self.modules.https) throw ('http.get: unsupported HTTPS');
if (!snd.port) snd.port=proto=='http'?80:443;
if (!path) path='';
else if (path.charAt(0)!='/') path = '/'+path;
if (!self.modules.http.xhr) {
req = (proto=='https'?self.modules.https.request:self.modules.http.request)({
host: snd.address,
port: snd.port,
path: path,
method: 'GET',
keepAlive: true,
headers: {
'User-Agent': 'Mozilla/5.0 (X11; SunOS i86pc; rv:45.0) Gecko/20100101 Firefox/45.0',
}
} , function(res) {
if (res.setEncoding != null) res.setEncoding('utf8');
var body = '';
res.on('data', function (chunk) {
body = body + chunk;
});
res.once('end', function () {
if (callback) callback(body);
});
});
req.once('error', function(err) {
print('Warning: request to '+addr2url(snd)+' failed: '+err);
if (callback) callback(null,err);
});
req.end();
} else {
// XHR Browser
self.modules.http.request({
host: snd.address,
port: snd.port,
path:path,
proto: proto,
method: 'GET',
keepAlive: true,
headers: {
}
} , function(err,xhr,body) {
if (err) {
print('Warning: request to '+addr2url(snd)+' failed: '+err);
if (callback) return callback(null,err);
}
if (callback) callback(body);
});
}
},
GET : function (url,params,callback) {
var tokens=url.match(/(http[s]*)?([:\/\/]*)?([^\/]+)\/?(.+)?/);
if (!tokens) throw "http.GET: Invalid URL";
var proto = tokens[1]||'http',
ip = tokens[3],
path = tokens[4]||'',
sep='';
if (params) {
path += '?';
Object.keys(params).forEach(function (param) {
path += (sep+param+'='+escape(params[param]));
sep = '&';
});
}
return self.env.http.get(proto+'://'+ip,path,function (result,err) {
if (err || Comp.obj.isError(result)) {
if (callback) callback(err || result);
return;
}
try {
result=JSON.parse(result);
callback(result);
} catch (e) {
if (e.toString().indexOf('SyntaxError')!=-1 && callback)
callback(e.toString()+'\n'+result);
else callback(e);
}
});
},
put: function (url,path,data,callback) {
var snd=url2addr(url),
proto = snd.proto || 'http';
if (proto == 'https' && !self.modules.https) throw ('http.put: unsupported HTTPS');
if (!snd.port) snd.port=80;
if (!path) path='';
else if (path.charAt(0)!='/') path = '/'+path;
if (!self.modules.http.xhr) {
req = (proto=='https'?self.modules.https.request:self.modules.http.request)({
host: snd.address,
port: snd.port,
path: path,
method: 'POST',
keepAlive: self.env.http.options.keepalive,
headers: {
'User-Agent': 'Mozilla/5.0 (X11; SunOS i86pc; rv:45.0) Gecko/20100101 Firefox/45.0',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
} , function(res) {
if (res.setEncoding != null) res.setEncoding('utf8');
var body = '';
res.on('data', function (chunk) {
body = body + chunk;
});
res.once('end', function () {
if (callback) callback(body);
});
});
req.once('error', function(err) {
print('Warning: request to '+addr2url(snd)+' failed: '+err);
if (callback) callback(err);
});
// write data to request body
req.write(data);
req.end();
} else {
// XHR Browser
self.modules.http.request({
host: snd.address,
port: snd.port,
path: path,
method: 'POST',
body:data,
keepAlive: self.env.http.options.keepalive,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
} , function(err,xhr,body) {
if (err) {
print('Warning: request to '+addr2url(snd)+' failed: '+err);
if (callback) callback(err);
return;
}
if (callback) callback(body);
})
}
},
POST : function (url,data,callback,params) {
var tokens=url.match(/(http[s]*)?([:\/\/]*)?([^\/]+)\/?(.+)?/);
if (!tokens) throw "http.GET: Invalid URL";
var proto = tokens[1]||'http',
ip = tokens[3],
path = tokens[4]||'',
sep='';
if (params) {
path += '?';
Object.keys(params).forEach(function (param) {
path += (sep+param+'='+escape(params[param]));
sep = '&';
});
}
try {
data=JSON.stringify(data);
} catch (e) {
if (callback) callback(e);
return;
}
// console.log(ip,path,data)
return self.env.http.put(proto+'://'+ip,path,data,function (result,err) {
if (err || Comp.obj.isError(result)) {
if (callback) callback(err || result);
return;
}
try {
result=JSON.parse(result);
if (callback) callback(result);
} catch (e) {
if (e.toString().indexOf('SyntaxError')!=-1 && callback)
callback(e.toString()+'\n'+result);
else if (callback) callback(e);
}
});
},
options : {
keepalive:true,
}
}
if (this.modules.httpserv) {
if (!this.env.http) this.env.http = {};
this.env.http.server = function (ip,dir,index) {
if (!self.modules.httpserv) return;
if (!dir) return;
if (ip==undefined) ip="localhost:8080";
if (self.httpSrv) self.httpSrv.stop();
var ipport=8080,tokens = ip.toString().split(':');
if (tokens.length==2) ip=tokens[0],ipport=Number(tokens[1]);
else if (typeof ip == 'number') ipport=ip;
self.httpSrv=self.modules.httpserv.HTTPSrv({ip:ip,ipport:ipport,dir:dir,index:index,log:self.output.bind(self)});
self.httpSrv.init();
self.httpSrv.start();
}
if (self.modules.https) {
if (!this.env.https) this.env.https = {};
this.env.https.server = function (ip,dir,index) {
if (!self.modules.httpserv) return;
if (!dir) return;
if (ip==undefined) ip="localhost:8080";
if (self.httpSrv) self.httpSrv.stop();
var ipport=8080,tokens = ip.toString().split(':');
if (tokens.length==2) ip=tokens[0],ipport=Number(tokens[1]);
else if (typeof ip == 'number') ipport=ip;
self.httpSrv=self.modules.httpserv.HTTPSrv({ip:ip,ipport:ipport,proto:'https',dir:dir,index:index,log:self.output.bind(self)});
self.httpSrv.init();
self.httpSrv.start();
}
}
}
if (this.modules.nlp) this.env.nlp = this.modules.nlp;
if (this.modules.sql) this.env.sql = function (filename,options) {
return self.modules.sql.Sqld(filename,options);
}
if (this.modules.csv) {
this.env.csv = {
detect : function (text) {
return self.modules.csv.detect(text);
},
read: function (file,convert,isString) {
var data,text;
if (self.options.verbose) self.log('CSV: Reading from '+(isString?'string':file));
try {
text=isString?file:Io.read_file(file);
if (!text) throw 'CSV File read error: '+file;
if (self.options.verbose) self.log('CSV: Parsing '+(isString?'string':file));
self.modules.csv.parse(text,{
skipEmptyLines: true,
dynamicTyping: true,
complete: function(results) {
if (self.options.verbose)
self.log('CSV parsed with DEL="'+results.meta.delimiter+
'" TRUNC='+results.meta.truncated+
' ABORT='+results.meta.aborted);
data=results.data;
if (convert) { // first line must be header
header=data.shift();
data=data.map(function (row) {
var r={};
header.forEach(function (col,i) { r[col]=row[i] });
return r;
})
}
}
});
if (data && data[0].length==1) data=data.map(function (row) { return row[0] });
return data;
} catch (e) {
return e;
}
},
write: function (file,header,data,sep) {
var d1=false,fd,i,convert=!Comp.obj.isArray(data[0])&&Comp.obj.isObj(data[0]);
if (!sep) sep=',';
d1 = typeof data[0] != 'object';
if (!header || header.length==0) {
if (!convert)
header=d1?['0']:data[0].map(function (x,i) { return String(i) });
else {
header=[];
for (var p in data[0]) {
header.push(p);
}
}
}
try {
if (self.options.verbose) self.log('CSV: Wrting to '+file);
fd=Io.open(file,'w+');
Io.write_line(fd,header.join(sep));
if (!d1)
for(i in data) {
if (!convert)
Io.write_line(fd,data[i].join(sep));
else
Io.write_line(fd,header.map(function (col) { return data[i][col]}).join(sep));
}
else
for(i in data) {
if (!convert)
Io.write_line(fd,data[i]);
else
Io.write_line(fd,data[i][header[0]]);
};
Io.close(fd);
return data.length
} catch (e) {
return e;
}
}
}
}
if (!this.rl && this.modules.readlineSync) {
// we can implement ask
this.env.ask = function (msg,choices) {
var answer;
while (choices.indexOf(answer)==-1)
answer = self.modules.readlineSync.question(msg+'? ['+choices.join(',')+'] ');
return answer;
}
}
if (this.options.script) this.env.script(this.options.script);
if (this.options.exec) this.process(this.options.exec);
return this;
}
Shell.prototype.on = function (event,handler) {
var self=this;
if (this.events[event]) {
// Implement callback function chain
var funorig=events[event];
this.events[event]=function () {
funorig.apply(this,arguments);
handler.apply(this,arguments);
};
} else {
this.events[event]=handler;
this.jam.Aios.on(event,function (arg1,arg2,arg3,arg4) { self.emit(event,arg1,arg2,arg3,arg4)});
}
}
// Generic output
Shell.prototype.output = function (line) {
var msg=format(line);
if (this.options.output && msg.length) this.options.output(msg);
if (this.rl && msg.length) this.rl.insertOutput(msg);
if (msg.length) this.emit('output',msg);
}
// Agent output
Shell.prototype.outputAgent = function (line) {
var msg=format(line);
if (this.options.outputAgent && msg.length) this.options.outputAgent(msg);
else if (this.options.output && msg.length) this.options.output(msg);
if (this.rl && msg.length) this.rl.insertOutput(msg);
if (msg.length) this.emit('output',msg);
}
// Shell output
Shell.prototype.outputPrint = function (line) {
var msg=format(line);
if (this.options.outputPrint && msg.length) this.options.outputPrint(msg);
else if (this.options.output && msg.length) this.options.output(msg);
if (this.rl && msg.length) this.rl.insertOutput(msg);
if (msg.length) this.emit('output',msg);
}
// Async AIOS/generic output (from callbacks)
Shell.prototype.outputAsync = function (line) {
var msg=format(line);
if (this.options.outputAsync && msg.length) this.options.outputAsync(msg);
else if (this.options.output && msg.length) this.options.output(msg);
if (this.rl && msg.length) this.rl.insertOutput(msg);
if (msg.length) this.emit('output',msg);
}
Shell.prototype.process = function (line) {
var self=this;
with(this.env) {
try {
if (line.match(/;[ \n]*$/))
eval(line);
else
self.output(eval(line));
} catch (e) {
var more='';
if (e.name==="SyntaxError"||e.name==="TypeError") {
try {
var ast = Esprima.parse(line, { tolerant: true, loc:true });
if (ast.errors && ast.errors.length>0) more = ", "+ast.errors[0];
} catch (_e) {
e=_e;
}
self.output(e.toString()+more)
} else if (e.stack) {
var line = e.stack.toString().match(/<anonymous>:([0-9]+):([0-9]+)\)/)
self.output(e.toString()+(line?', at line '+line[1]:''));
} else {
self.output(e.toString())
}
if (self.options.verbose>1) self.output(Io.sprintstack(e)); }
}
}
module.exports = Shell;
};
BundleModuleCode['top/jamlib']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 25-12-16 by sbosse.
** $RCS: $Id: jamlib.js,v 1.5 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.35.1
**
** $INFO:
**
** JAM library API that can be embedded in any host application.
**
**
** New: Embedded auto setup (e.g., for clusters) using command line arguments
**
** jamlib autosetup:"{options}"
**
**
** $ENDOFINFO
*/
var onexit=false;
var start=false;
var options = {
geo:undefined,
verbose:0,
version:'1.35.1' // public version
};
global.config={simulation:false,nonetwork:false};
var Io = Require('com/io');
var Comp = Require('com/compat');
var Aios = Require('jam/aios');
var Esprima = Require('parser/esprima');
var Json = Require('jam/jsonfn');
var fs = Require('fs');
var Sat = Require('dos/ext/satelize');
var GPS5 = Require('geoip/gps5');
var GeoLoc5 = Require('geoip/geoloc5');
var CBL = Require('com/cbl');
var platform = Require('os/platform');
var DIR = Aios.DIR;
// Parse command line arguments; extract a:v attributes
var environment = process.env; process.argv.slice(2).forEach(function (arg) {
var tokens=arg.match(/([a-zA-Z]+):(['"0-9a-zA-Z_:\->\.\{\},;]+)/);
if (tokens && tokens.length==3) environment[tokens[1]]=tokens[2];
});
function locationEvalError(e) {
return (e.lineNumber?(' at line '+e.lineNumber+
(e.columnNumber?(' column '+e.columnNumber):'')):'')
}
if (typeof setImmediate == 'undefined') {
function setImmediate(callback) {return setTimeout(callback,0)};
}
// Extend DIR with IP capabilities of NORTH, ..
DIR.North= function (ip) { return {tag:DIR.NORTH,ip:ip}}
DIR.South= function (ip) { return {tag:DIR.SOUTH,ip:ip}}
DIR.West = function (ip) { return {tag:DIR.WEST ,ip:ip}}
DIR.East = function (ip) { return {tag:DIR.EAST ,ip:ip}}
DIR.Up = function (ip) { return {tag:DIR.UP ,ip:ip}}
DIR.Down = function (ip) { return {tag:DIR.DOWN ,ip:ip}}
/**
* typeof options = {
* connections?,
* print? is agent and control message output function,
* printAgent? is agent message only output function,
* printAsync? async (callback) output function,
* fork?,
* provider?, consumer?,
* classes?,
* id?:string is JAM and JAM root node id,
* world?:string is JAM world id,
* position?:{x,y},
* cluster?:boolean|[] is an attached cluster node,
* nowatch:boolean is a disable flag for agent watchdog checking,
* checkpoint:boolean is a flag forcing code checkpointing (even if watchdog is available),
* nolimits:boolean is a disable flag for agent resource monitoring,
* log?:{class?:boolean,node?,agent?,parent?,host?,time?,Time?,pid?},
* logJam?:{host?,time?,pid?,node?,world?},
* scheduler?:scheduler is an external scheduler, singlestep?,
* network?:{cluster?,rows,columns,connect?:function},
* verbose?, TMO? }
* with typeof connections = {
* @kind : {from:string,to?:string,proto:string='udp'|'tcp'|'http'|'stream',num?:number,on?,range?:number[]},
* @kind : {send:function, status:function, register?:function(@link)} ,
* @kind : .. }
* with @kind = {north,south,west,east,ip, ..}
*
* Connecting JAM nodes (IP)
* -------------------------
*
* .. Jam({
* connections: {
* // Generic, P2PN
* ip?: {from:string,to?:string,proto:string='udp'|'tcp'|'http',num?:number} // AMP link (UDP) or set of AMP links (num>1)
* // Assigned to a logical direction, P2P
* north?: {
* from:string,to?:string,proto?='udp'|'tcp'|'http'|'device',device?:string // device is a hardware P2P stream device
* }, ..
*
* Integration of host program streams
* ------------------------------------
*
* var chan = Some Stream Channel Object;
*
* .. Jam({
* connections: {
* north?: {
* register: function (link) {
* // register channel data handler with link handler
* chan.on('data',function (data) {
* // process raw data, extract msg={agent:string,to?,from?,..} or {signal:string,to?,from?,..}
* if (msg.agent) link.emit('agent',msg.agent);
* if (msg.signal) link.emit('signal',msg.signal);
* });
* }
* send: function (msg) {
* chan.send(msg);
* },
* status: function (to) {
* return true;
* }
* }
* }, ..
* }
*
* Cluster
* --------
*
* A forked cluster consists of a master node (0) and up to 8 child ndoes connected around the root node
* by streams in directions {E,S,SE,W,SW,N,NW,NE}. Each node is executed physically in a different JAM process.
* Ex. network: {cluster:true, rows:2, columns:2},
*
*/
var jam = function (options) {
var self=this,
p,conn,node;
this.options = options||{};
this.environment=environment;
if (this.setup) this.setup(); // overwrite options
if (this.options.world && !this.options.id) this.options.id=this.options.world;
if (!this.options.id) this.options.id=Aios.aidgen();
if (!this.options.log) this.options.log={};
if (!this.options.logJam) this.options.logJam={pid:false,host:false,time:false};
this.verbose = this.options.verbose || 0;
this.Aios = Aios;
this.DIR = Aios.aios.DIR;
Aios.options.verbose=this.verbose;
if (options.scheduler) Aios.current.scheduler=scheduler;
if (options.nolimits||options.nowatch||options.checkpoint)
Aios.config({nolimits:options.nolimits,nowatch:options.nowatch,checkpoint:options.checkpoint});
// out=function (msg) { Io.print('[JAM '+self.options.id+'] '+msg)};
if (this.options.print) Aios.print=Aios.printAgent=this.options.print;
if (this.options.print2) Aios.printAgent=this.options.print2;
if (this.options.printAgent) Aios.printAgent=this.options.printAgent;
if (this.options.printAsync) Aios.printAsync=this.options.printAsync;
// JAM messages
this.log=function (msg) {
var s='[JAM',sep=' ';
if (self.options.logJam.pid && process) s += (' '+process.pid),sep=':';
if (self.options.logJam.world && Aios.current.world) s += (sep+Aios.current.world.id),sep=':';
if (self.options.logJam.node && Aios.current.node) s += (sep+Aios.current.node.id),sep=':';
if (self.options.logJam.time) s += (sep+Aios.time());
Aios.print(s+'] '+msg);
};
this.err=function (msg,err) {
self.log('Error: '+msg);
throw (err||'JAMLIB');
}
this.warn=function (msg) {
self.log('Warning: '+msg);
}
this.error=undefined;
// Create a world
this.world = Aios.World.World([],{
id:this.options.world||this.options.id.toUpperCase(),
classes:options.classes||[],
scheduler:options.scheduler,
verbose:options.verbose
});
if (this.verbose) this.log('Created world '+this.world.id+'.');
this.node=none;
this.run=false;
// Service loop executing the AIOS scheduler
// NOT USED if there is an external scheduler supplied (world will create JAM scheduling loop)
this.ticks=0; // schedule loop execution counter!
this.steps=0; // Number of schedule loop execution steps
this.loop=none; // Schedule loop function
this.looping=none; // Current schedule loop run (or none); can be waiting for a timeout
Aios.config({fastcopy:this.options.fastcopy,
verbose:this.options.verbose});
if (this.options.log)
for(p in this.options.log) Aios.config(this.options.log[p]?{"log+":p}:{"log-":p});
this.process = Aios.Proc.Proc();
this.process.agent={id:'jamlib'};
this.events={};
}
// Import analyzer class...
var JamAnal = Require('jam/analyzer');
JamAnal.current(Aios);
jam.prototype.analyzeSyntax=JamAnal.jamc.prototype.analyze;
jam.prototype.syntax=JamAnal.jamc.prototype.syntax;
/** Add agent class to the JAM world and create sandboxed constructors.
* type constructor = function|string
*/
jam.prototype.addClass = function (name,constructor,env) {
this.world.addClass(name,constructor,env);
if (this.verbose) this.log('Agent class '+name+' added to world library.');
};
/** Add a new node to the world.
* Assumption: 2d meshgrid network with (x,y) coordinates.
* The root node has position {x=0,y=0}.
* type of nodeDesc = {x:number,y:number,id?}
*
*/
jam.prototype.addNode = function (nodeDesc) {
var node,x,y;
x=nodeDesc.x;
y=nodeDesc.y;
if (Comp.array.find(this.world.nodes,function (node) {
return node.position.x==x && node.position.y==y;
})) {
this.err('addNodes: Node at positition ('+x+','+y+') exists already.');
return;
}
node=Aios.Node.Node({id:nodeDesc.id||Aios.aidgen(),position:{x:x,y:y}},true);
if (this.verbose) this.log('Created node '+node.id+' ('+x+','+y+').');
// Add node to world
this.world.addNode(node);
return node.id;
}
/** Add logical nodes.
* The root node has position {x=0,y=0}.
* type of nodes = [{x:number,y:number,id?},..]
*/
jam.prototype.addNodes = function (nodes) {
var n,node,x,y,nodeids=[];
for(n in nodes) {
nodeids.push(this.addNode(nodes[n]));
}
return nodeids;
}
/** Analyze agent class template in text or object form
** typeof @options = {..,classname?:string}
* Returns {report:string,interface}
*/
jam.prototype.analyze = function (ac,options) {
var source,name,syntax,content,report,interface;
if (Comp.obj.isString(ac)) {
// TODO
} else if (Comp.obj.isObject(ac)) {
// TODO
} else if (Comp.obj.isFunction(ac)) {
source = ac.toString();
if (!options.classname) {
name=source.match(/^ *function *([^\s\(]*)\(/);
if (name && name[1]!='') options.classname=name[1];
}
content = 'var ac ='+source;
syntax = Esprima.parse(content, { tolerant: true, loc:true });
try {
interface=this.analyzeSyntax(syntax,{
classname:options.classname||'anonymous',
level:options.level==undefined?2:options.level,
verbose:options.verbose,
err:function (msg){throw msg},
out:function (msg){if (!report) report=msg; else report=report+'\n'+msg;},
warn:function (msg){if (!report) report=msg; else report=report+'\n'+msg;}
});
return {report:report||'OK',interface:interface};
} catch (e) {
return {report:e,interface:interface};
}
}
}
jam.prototype.clock = Aios.clock;
/** Compile (analyze) an agent class constructor function and add it to the world class library.
** Can be used after an open statement.
** Usage: compileClass(name,constructor,options?)
** compileClass(constructor,options?)
**
** typeof @name=string|undefined
** typeof @constructor=function|string
** typeof @options={verbose:number|boolean)|verbose:number|undefined
*/
jam.prototype.compileClass = function (name,constructor,options) {
var ac,p,verbose,content,syntax,report,text,env={ac:undefined},self=this,ac;
if (typeof name == 'function') constructor=name,name=undefined,options=constructor;
if (typeof options == 'object') verbose=options.verbose||0;
else if (options!=undefined) verbose=options; else verbose=this.verbose;
// if (typeof constructor != 'function') throw 'compileClass: second constructor argument not a function';
if (typeof constructor == 'function') text = constructor.toString();
else text = constructor;
if (!name) {
// try to find name in function definition
name=text.match(/[\s]*function[\s]*([A-Za-z0-9_]+)[\s]*\(/);
if (!name) throw ('compileClass: No class name provided and no name found in constructor '+
text.substring(0,80));
name=name[1];
}
content = 'var ac = '+text;
try { syntax = Esprima.parse(content, { tolerant: true, loc:true }) }
catch (e) { throw 'compileClass('+name+'): Parsing failed with '+e }
report = this.analyzeSyntax(syntax,
{
classname:name,
level:2,
verbose:verbose||0,
err: function (msg){self.log(msg)},
out: function (msg){self.log(msg)},
warn: function (msg){self.log(msg)}
});
if (report.errors.length) { throw 'compileClass('+name+'): failed with '+report.errors.join('; ')};
for (p in report.activities) env[p]=p;
try { with (env) { eval(content) } }
catch (e) { throw ('compileClass('+name+'): failed with '+e+locationEvalError(e)) };
ac=env.ac; env.ac=undefined;
this.addClass(name,ac,env);
return name;
}
/** Connect logical nodes (virtual link).
* The root node has position {x=0,y=0}.
* type of links = [{x1:number,y1:number,x2:number,x2:number},..]|[{x,y},{x,y}]
*/
jam.prototype.connectNodes = function (connections) {
var c,node1,node2,x1,y1,x2,y2,dir;
if (connections[0].x != undefined && connections[0].y != undefined) {
if (connections.length!=2) throw 'INVALID'; // invalid
// simple style
connections=[{x1:connections[0].x,x2:connections[1].x,
y1:connections[0].y,y2:connections[1].y}];
}
for(c in connections) {
x1=connections[c].x1;
y1=connections[c].y1;
x2=connections[c].x2;
y2=connections[c].y2;
if (this.verbose) this.log('Connecting ('+x1+','+y1+') -> ('+x2+','+y2+')');
node1=Comp.array.find(this.world.nodes,function (node) {
return node.position.x==x1 && node.position.y==y1;
});
node2=Comp.array.find(this.world.nodes,function (node) {
return node.position.x==x2 && node.position.y==y2;
});
if (!node1) this.err('connectNodes: Node at positition ('+x1+','+y1+') does not exist.');
if (!node2) this.err('connectNodes: Node at positition ('+x2+','+y2+') does not exist.');
if ((x2-x1)==0) {
if ((y2-y1) > 0) dir=Aios.DIR.SOUTH;
else dir=Aios.DIR.NORTH;
} else if ((x2-x1)>0) dir=Aios.DIR.EAST;
else dir=Aios.DIR.WEST;
this.world.connect(dir,node1,node2);
this.world.connect(Aios.DIR.opposite(dir),node2,node1);
}
}
/** Dynamically connect remote endpoint at run-time
* typeof @to = string <dir->url>|<url>
*/
jam.prototype.connectTo = function (to,nodeid) {
var node=this.getNode(nodeid),
tokens=(typeof to=='string')?to.split('->'):null,
dir;
// console.log(tokens)
if (!node) return;
if (to.tag) dir=to;
else if (tokens.length==2) {
dir=Aios.DIR.from(tokens[0]);
if (dir) dir.ip=tokens[1];
} else dir={tag:'DIR.IP',ip:to};
if (dir) this.world.connectTo(dir,node);
}
/** Check connection status of a link
*
*/
jam.prototype.connected = function (dir,nodeid) {
var node=this.getNode(nodeid);
if (!node) return;
return this.world.connected(dir,node);
}
/** Create and start an agent from class ac with arguments.
* Ac is either already loaded (i.e., ac specifies the class name) or
* AC is supplied as a constructor function (ac), a class name, or a sandboxed constructor
* {fun:function,mask:{}} object for a specific level.
*
* type of ac = string|object|function
* type of args = * []
* level = {0,1,2,3}
*
*/
jam.prototype.createAgent = function (ac,args,level,className,parent) {
var node=this.world.nodes[this.node],
process=none,sac;
if (level==undefined) level=Aios.options.LEVEL;
if (!className && typeof ac == 'string') className=ac;
if (!className && typeof ac == 'function') className=Aios.Code.className(ac);
if (Comp.obj.isFunction(ac) || Comp.obj.isObject(ac)) {
// Create an agent process from a constructor function or sandboxed constructor object
process = Aios.Code.createOn(node,ac,args,level,className);
if (process && !process.agent.parent) process.agent.parent=parent;
if (process) return process.agent.id;
} else {
// It is a class name. Find an already sandboxed constructor from world classes pool
if (this.world.classes[ac])
process = Aios.Code.createOn(node,this.world.classes[ac][level],args,level,className);
else {
this.error='createAgent: Cannot find agent class '+ac;
this.log(this.error);
return;
}
if (process) {
if (!process.agent.parent) process.agent.parent=parent;
process.agent.ac=ac;
return process.agent.id;
} else return none;
}
}
/** Create agent on specified (logical or physical) node.
* typeof node = number|string|{x,y}
*/
jam.prototype.createAgentOn = function (node,ac,args,level,className,parent) {
var res,_currentNode=this.node,found=this.getNode(node);
if (found) {
this.setCurrentNode();
res=this.createAgent(ac,args,level,className,parent);
this.setCurrentNode(_currentNode);
}
return res;
}
/** Create a physical communication port
*
*/
jam.prototype.createPort = function (dir,options,nodeid) {
if (!options) options={};
var multicast=options.multicast;
switch (dir.tag) {
case Aios.DIR.NORTH:
case Aios.DIR.SOUTH:
case Aios.DIR.WEST:
case Aios.DIR.EAST:
case Aios.DIR.UP:
case Aios.DIR.DOWN:
multicast=false;
break;
}
if (dir.ip && typeof dir.ip == 'string' && dir.ip.indexOf('//')>0) {
// extract proto from url
var addr = Aios.Amp.url2addr(dir.ip);
if (!options.proto && addr.proto) options.proto=addr.proto;
dir.ip=Aios.Amp.addr2url(addr);
}
if (options.from==undefined && dir.ip) options.from=dir.ip.toString();
var chan=this.world.connectPhy(
dir,
this.getNode(nodeid),
{
broker : options.broker,
keepAlive : options.keepAlive,
multicast : multicast,
name : options.name,
on : options.on,
oneway : options.oneway,
pem : options.pem,
proto : options.proto||'udp',
rcv : options.from,
secure : options.secure,
sharedSocket:options.sharedSocket,
snd : options.to,
verbose:(options.verbose!=undefined?options.verbose:this.verbose)
});
chan.init();
chan.start();
return chan;
}
/** Dynamically disconnect remote endpoint at run-time
*
*/
jam.prototype.disconnect = function (to,nodeid) {
var node=this.getNode(nodeid);
if (node) {
this.world.disconnect(to,node);
}
}
/** Emit an event
** function emit(@event,@arg1,..)
*/
jam.prototype.emit = function () {
Aios.emit.apply(this,arguments);
}
/** Execute an agent snapshot on current node delivered in JSON+ text format or read from a file.
*/
jam.prototype.execute = function (data,file) {
if (!data && file && fs)
try {
data=fs.readFileSync(file,'utf8');
} catch (e) {
this.log('Error: Reading file '+file+' failed: '+e);
return undefined;
}
if (data) return this.world.nodes[this.node].receive(data,true);
}
/** Execute an agent snapshot on node @node delivered in JSON+ text format or read from a file.
*/
jam.prototype.executeOn = function (data,node,file) {
node=this.getNode(node);
if (!node) return;
if (!data && file && fs)
try {
data=fs.readFileSync(file,'utf8');
} catch (e) {
this.log('Error: Reading file '+file+' failed: '+e);
return undefined;
}
if (data) return node.receive(data,true);
}
/** Extend AIOS of specific privilege level. The added functions can be accessed by agents.
*
* function extend(level:number [],name:string,func:function,argn?:number|number []);
*/
jam.prototype.extend = function (level,name,funcOrObj,argn) {
var self=this;
if (Comp.obj.isArray(level)) {
Comp.array.iter(level,function (l) {self.extend(l,name,funcOrObj,argn)});
return;
}
function range(n) {
var l=[];
for(var i=0;i<n+1;i++) l.push(i);
return l;
}
switch (level) {
case 0:
if (Aios.aios0[name]) throw Error('JAM: Cannot extend AIOS(0) with '+name+', existst already!');
Aios.aios0[name]=funcOrObj; break;
case 1:
if (Aios.aios1[name]) throw Error('JAM: Cannot extend AIOS(1) with '+name+', existst already!');
Aios.aios1[name]=funcOrObj; break;
case 2:
if (Aios.aios2[name]) throw Error('JAM: Cannot extend AIOS(2) with '+name+', existst already!');
Aios.aios2[name]=funcOrObj; break;
case 3:
if (Aios.aios3[name]) throw Error('JAM: Cannot extend AIOS(3) with '+name+', existst already!');
Aios.aios3[name]=funcOrObj; break;
default:
throw Error('JAM: Extend: Invalid privilige level argument ([0,1,2,3])');
}
if (!JamAnal.corefuncs[name]) {
if (typeof funcOrObj == 'function')
JamAnal.corefuncs[name]={argn:Comp.obj.isArray(argn)?argn:argn!=undefined?range(argn):range(funcOrObj.length)};
else {
// extend an object (may not be nested to get the type signature)
var obj = {};
Object.keys(funcOrObj).forEach(function (attr) {
obj[attr]={argn:range(funcOrObj[attr].length)};
});
JamAnal.corefuncs[name]={obj:obj};
}
}
}
jam.prototype.getCurrentNode=function (asname) {
if (!asname) return this.node;
else return this.world.nodes[this.node].id;
}
/** Return node object referenced by logical node number, position, or name
* If @id is undefined return current node object.
*/
jam.prototype.getNode = function (id) {
var node;
if (id==undefined) return this.world.nodes[this.node];
if (typeof id == 'number')
node=this.world.nodes[id];
else if (typeof id == 'string') {
// Search node identifier or position;
loop: for(var i in this.world.nodes) {
if (this.world.nodes[i] && this.world.nodes[i].id==id) {
node = this.world.nodes[i];
break loop;
}
}
} else if (id.x != undefined &&
id.y != undefined) {
// Search node position;
loop: for(var i in this.world.nodes) {
if (this.world.nodes[i] && Comp.obj.equal(this.world.nodes[i].position,id)) {
node = this.world.nodes[i];
break loop;
}
}
}
return node;
}
/** Return node name from logical node number or position
*
*/
jam.prototype.getNodeName = function (nodeNumberorPosition) {
var node=this.getNode(nodeNumberorPosition);
if (node) return node.id;
}
/** Get current agent process or search for agent process
*
*/
jam.prototype.getProcess = function (agent) {
if (!agent)
return Aios.current.process;
else {
var node = this.getNode(); // current node
if (node) return node.getAgentProcess(agent);
}
}
/** Return node name from logical node number or position
*
*/
jam.prototype.getWorldName = function () {
return this.world.id;
}
/** Get info about node, agents, plattform
*
*/
jam.prototype.info = function (kind,id) {
switch (kind) {
case 'node':
var node=this.getNode(id);
if (!node) return;
return {
id:node.id,
position: node.position,
location:node.location,
type:node.type
}
break;
case 'agent':
var agent = this.getProcess(id);
if (!agent) return;
var code = Aios.Code.print(agent.agent,true);
return {
id:id,
pid:agent.pid,
level:agent.level,
blocked:agent.blocked,
suspended:agent.suspended,
resources:agent.resources,
code:code
}
break;
case 'agent-data':
var agent = this.getProcess(id);
if (!agent) return;
else return agent.agent;
break;
case 'version': return Aios.options.version;
case 'host': return {
type:global.TARGET,
watchdog:Aios.watchdog?true:false,
protect: Aios.watchdog&&Aios.watchdog.protect?true:false,
jsonify:Aios.options.json,
minify:!Aios.Code.options.compactit,
};
case 'platform': return platform;
}
}
/** INITIALIZE
* 1. Create and initialize node(s)/world
* 2. Add optional TS provider/consumer
* 3. Create physical network conenctions
*/
jam.prototype.init = function (callback) {
var i=0,j=0, n, p, id, node, connect=[], chan, dir, dirs, pos,
self=this;
// Current node == root node
this.node=0;
///////////// CREATE NODES /////////
if (!this.options.network) {
if (this.options.position) i=this.options.position.x,j=this.options.position.y;
// Create one (root) node if not already existing
if (!this.getNode({x:i,y:j})) {
node = Aios.Node.Node({
id:this.options.id,
position:{x:i,y:j},
TMO:this.options.TMO,
type:this.options.type
},true);
// Add node to world
if (this.verbose) this.log('Created '+(i==0&&j==0?'root ':'')+'node '+node.id+' ('+i+','+j+').');
this.world.addNode(node);
}
// Register jamlib event handler for the root node
this.register(node);
} else if (!this.options.network.cluster) {
// Create a virtual network of logical nodes. Default: grid
if (this.options.network.rows && this.options.network.columns) {
for(j=0;j<this.options.network.rows;j++)
for(i=0;i<this.options.network.columns;i++) {
node = Aios.Node.Node({id:Aios.aidgen(),position:{x:i,y:j},TMO:this.options.TMO},true);
if (this.verbose) this.log('Created node '+node.id+' at ('+i+','+j+').');
if (i==0&&j==0) {
// Register jamlib event handler for the root node
this.register(node);
}
this.world.addNode(node);
}
// Connect nodes with virtual links
for(j=0;j<this.options.network.rows;j++)
for(i=0;i<this.options.network.columns;i++) {
if (i+1<this.options.network.columns) connect.push({x1:i,y1:j,x2:i+1,y2:j});
if (j+1<this.options.network.rows) connect.push({x1:i,y1:j,x2:i,y2:j+1});
}
if (this.options.network.connect) connect=connect.filter(this.options.network.connect);
this.connectNodes(connect);
}
} else if (this.options.network.cluster && this.options.fork) {
// Physical network cluster; each node is executed in a process on this host
dirs=[DIR.ORIGIN,DIR.EAST,DIR.SOUTH,DIR.SE,DIR.WEST,DIR.SW,DIR.NORTH,DIR.NW,DIR.NE];
pos={x:[0,1,0,1,-1,-1,0,-1,1],
y:[0,0,1,1,0,1,-1,-1,-1]};
// Create a physical network of nodes. Here create only the root node (0,0)
this.cluster=[]; this.master=true;
for(j=0;j<this.options.network.rows;j++)
for(i=0;i<this.options.network.columns;i++) {
id=Aios.aidgen();
if (i==0 && j==0) {
dir=undefined;
node = Aios.Node.Node({id:id,position:{x:i,y:j},TMO:this.options.TMO},true);
if (this.verbose) this.log('Created root node '+node.id+' at ('+i+','+j+').');
// Register jamlib event handler for the root node
this.register(node);
this.world.addNode(node);
this.setCurrentNode(id);
} else {
n=i+j*this.options.network.columns;
dir=dirs[n];
if (this.verbose) this.log('Started cluster node '+id+' at ('+i+','+j+'). with link '+DIR.print(dir));
this.cluster[id]=this.options.fork(process.argv[1],['autosetup:'+JSON.stringify({
id:id,
world:this.world.id,
cluster:true,
network:null,
position:{x:pos.x[n],y:pos.y[n]},
dir:dir,
connections:{
stream:{
dir:DIR.opposite(dir)
}
}
})]);
this.cluster[id].dir=dir;
// Clustered forked nodes communicate via process.send, receive message via process.on('message') handler
}
}
// Create physical stream links to all child nodes
for(p in this.cluster) {
chan=this.world.connectPhy(
this.cluster[p].dir,
this.getNode(),
{
proto:'stream',
sock:this.cluster[p],
mode:'object',
verbose:this.verbose
});
chan.init();
}
}
//////////// Install host platform tuple provider and consumer //////////
/*
** Each time a tuple of a specific dimension is requested by an agent (rd)
** the provider function can return (provide) a mathcing tuple (returning the tuple).
** IO gate between agents/JAM and host application.
*/
if (this.options.provider) this.world.nodes[this.node].ts.register(function (pat) {
// Caching?
return self.options.provider(pat);
});
/*
** Each time a tuple of a specific dimension is stored by an agent (out)
** the consumer function can return consume the tuple (returning true).
** IO gate between agents/JAM and host application.
*/
if (this.options.consumer) this.world.nodes[this.node].ts.register(function (tuple) {
// Caching?
return self.options.consumer(tuple);
},true);
///////////// CREATE NETWORK CONNECTIVITY /////////
// Register host application connections {send,status,count,register?} using host app. streams or
// create physical conenction ports (using the AMP P2P protocol over IP/RS232) {from:*,proto:'udp'|..}
if (this.options.connections) {
for (p in this.options.connections) {
conn=this.options.connections[p];
if (!conn) continue;
if (p=='ip' || conn.proto) {
// 1. IP
// Attach AMP port to root node, actually not linked with endpoint
n=1;
switch (p) {
case 'ip':
dir=this.DIR.IP(this.options.connections.ip.from||'*');
// actually not linked with endpoint
n = (conn.range && conn.range.length==2 && (conn.range[1]-conn.range[0]+1))||
conn.num||
1; // multiple interface are allowed
break;
case 'north': dir=this.DIR.NORTH; break;
case 'south': dir=this.DIR.SOUTH; break;
case 'west': dir=this.DIR.WEST; break;
case 'east': dir=this.DIR.EAST; break;
case 'up': dir=this.DIR.UP; break;
case 'down': dir=this.DIR.DOWN; break;
}
function makeAddr(ip,i) {
if (!conn.range) return ip;
else return ip+':'+(conn.range[0]+i);
}
for(i=0;i<n;i++) {
chan=this.world.connectPhy(
dir,
this.getNode(),
{
broker:conn.broker,
multicast:conn.multicast,
name:conn.name,
on:conn.on,
oneway:conn.oneway,
proto:conn.proto||'udp',
rcv:makeAddr(conn.from,i),
snd:conn.to,
verbose:this.verbose
});
chan.init();
}
} else if (conn.send) {
// 2. Host stream interface
node=this.world.nodes[this.node]; // TODO: connections.node -> world node#
function makeconn (p,conn) {
var link = {
_handler:[],
emit: function (event,msg) {
if (link._handler[event]) link._handler[event](msg);
},
on: function (event,callback) {
link._handler[event]=callback;
},
send: function (data,dest,context) {
var res;
self.world.nodes[self.node].connections[p]._count += data.length;
res=conn.send(data,dest);
if (!res) {
context.error='Migration to destination '+dest+' failed';
// We're still in the agent process context! Throw an error for this agent ..
throw 'MOVE';
};
// kill ghost agent
context.process.finalize();
},
status : conn.status?conn.status:(function () {return true}),
count: conn.count?conn.count:function () {return link._count},
_count:0
};
if (conn.register) conn.register(link);
return link;
}
node.connections[p] = makeconn(p,conn);
// register agent receiver and signal handler
node.connections[p].on('agent',node.receive.bind(node));
node.connections[p].on('signal',node.handle.bind(node));
} else if (p=='stream') {
// 3. Physical process stream interface (cluster); child->parent proecss connection
chan=this.world.connectPhy(
conn.dir,
this.getNode(),
{
proto:'stream',
sock:process,
mode:'object',
verbose:this.verbose
});
chan.init();
}
}
}
if (callback) callback();
}
/** Tuple space input operation - non blocking, i.e., equiv. to inp(pat,_,0)
*/
jam.prototype.inp = function (pat,all) {
return this.world.nodes[this.node].ts.extern.inp(pat,all);
}
/** Kill agent with specified id ('*': kill all agents on node or current node)
*/
jam.prototype.kill = function (id,node) {
if (id=='*') {
this.world.nodes[this.node].processes.table.forEach(function (p) {
if (p) Aios.kill(p.agent.id);
});
} else
return Aios.kill(id);
}
/** Try to locate this node (based on network connectivity)
* Any geospatial information is attached to current (node=undefined) or specific node
*/
jam.prototype.locate = function (nodeid,cb,options) {
if (typeof nodeid == 'function') { options=cb;cb=nodeid;nodeid=0};
if (typeof nodeid == 'object') { options=nodeid;cb=null;nodeid=0};
var node=this.getNode(nodeid);
if (!node) return;
return GeoLoc5.locate(function (location,errors) {
node.location=node.location||{};
Object.assign(node.location,location);
if (cb) cb(location,errors);
},options);
}
/** Lookup nodes and get connection info (more general as connected and broker support)
*
*/
jam.prototype.lookup = function (dir,callback,nodeid) {
var node=this.getNode(nodeid);
if (!node) return;
return this.world.lookup(dir,callback,node);
}
/** Tuple space output operation with timeout
*/
jam.prototype.mark = function (tuple,tmo) {
return this.world.nodes[this.node].ts.extern.mark(tuple,tmo);
}
/** Execute an agent snapshot in JSON+ text form after migration provided from host application
*/
jam.prototype.migrate = function (data) {
return this.world.nodes[this.node].receive(data,false);
}
/** Install event handler
*
* typeof @event = {'agent','agent+','agent-','signal+','signal','link+','link-',..}
* agent+/agent-: Agent creation and destruction event
* agent: Agent receive event
* signal+: Signal raise event
* signal: Signal receive (handle) event
* route+: A new link was established
* route-: A link is broken
*/
jam.prototype.on = function (event,handler) {
Aios.on(event,handler);
}
/** Remove event handler
*/
jam.prototype.off = function (ev) {
Aios.off(event);
}
/** Read and parse one agent class from file. Can contain nested open statements.
* Browser (no fs module): @file parameter contains source text.
* File/source text format: function [ac] (p1,p2,..) { this.x; .. ; this.act = {..}; ..}
* open(file:string,options?:{verbose?:number|boolean,classname?:string}) -> function | object
*
* Output can be processed by method compileClass
*/
jam.prototype.open = function (file,options) {
var self=this,
res,
text,
name,
ast=null;
if (!options) options={};
name=options.classname||'<unknown>';
if (options.verbose>0) this.log('Reading agent class template '+name+' from '+file);
function parseModel (text) {
var modu={},more,module={exports:{}},name=text.match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/);
if (name) name=name[1];
function open(filename) {
var text;
try {
text=fs?fs.readFileSync(filename,'utf8'):null;
return parseModel(text);
} catch (e) {
self.log('Error: Opening of '+(fs?file:'text')+' failed: '+e);
}
}
try {
with (module) {eval('res = '+text)};
if (name) { modu[name]=res; return modu}
else if (module.exports) return module.exports;
else return res;
} catch (e) {
try {
ast = Esprima.parse(text, { tolerant: true, loc:true });
if (ast.errors && ast.errors.length>0) more = ', '+ast.errors[0];
} catch (e) {
if (e.lineNumber) more = ', in line '+e.lineNumber;
}
self.log(e.name+(e.message?': '+e.message:'')+(more?more:''));
}
}
try {
text=fs?fs.readFileSync(file,'utf8'):file; // Browser: file parameter contains already source text
return parseModel(text);
} catch (e) {
this.log('Error: Opening of '+(fs?file:'text')+' failed: '+e);
}
};
/** Tuple space output operation
*/
jam.prototype.out = function (tuple) {
return this.world.nodes[this.node].ts.extern.out(tuple);
}
/** Tuple space read operation - non blocking, i.e., equiv. to rd(pat,_,0)
*/
jam.prototype.rd = function (pat,all) {
return this.world.nodes[this.node].ts.extern.rd(pat,all);
}
/** 1. Read agent template classes from file and compile (analyze) agent constructor functions.
* Expected file format: module.exports = { ac1: function (p1,p2,..) {}, ac2:.. }
* 2. Read single agent constructor function from file
*
* typeof @options={verbose,error:function}
*/
// TODO: clean up, split fs interface, no require caching ..
if (fs) jam.prototype.readClass = function (file,options) {
var self=this,
ac,
name,
env,
interface,
text,
modu,
path,
p,m,
regex1,
ast=null,
fileText=null,
off=null;
this.error=_;
function errLoc(ast) {
var err;
if (ast && ast.errors && ast.errors.length) {
err=ast.errors[0];
if (err.lineNumber != undefined) return 'line '+err.lineNumber;
}
return 'unknown'
}
try {
if (!options) options={};
if (options.verbose>0) this.log('Looking up agent class template(s) from '+file);
//modu=Require(file);
if (Comp.obj.isEmpty(modu)) {
if (options.verbose>0) this.log('Reading agent class template(s) from file '+file);
if (Comp.string.get(file,0)!='/')
path = (process.cwd?process.cwd()+'/':'./')+file;
else
path = file;
fileText=fs.readFileSync(path,'utf8');
ast=Esprima.parse(fileText, { tolerant: true, loc:true });
if (require.cache) delete require.cache[file]; // force reload of file by require
modu=require(path);
if(Comp.obj.isEmpty(modu)) {
modu={};
// Try evaluation of fileText containing one single function definition
if (!fileText) throw 'No such file!';
name=fileText.match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/);
if (!name) throw ('Export interface of module is empty and file contains no valid function definition!');
name=name[1];
eval('(function () {'+fileText+' modu["'+name+'"]='+name+'})()');
}
}
if (!modu || Comp.obj.isEmpty(modu)) throw 'Empty module.';
for (m in modu) {
ac=modu[m];
env={};
if (fileText) off=this.syntax.find(fileText,'VariableDeclarator',m);
if (off && off.loc) this.syntax.offset=off.loc.start.line-1;
content = 'var ac = '+ac;
syntax = Esprima.parse(content, { tolerant: true, loc:true });
interface = this.analyzeSyntax(syntax,
{
classname:m,
level:2,
verbose: options.verbose||0,
err: options.error||function (msg){throw(msg)},
out: function (msg){self.log(msg)},
warn: function (msg){self.log(msg)}
});
// text=Json.stringify(ac);
for (var p in interface.activities) env[p]=p;
with (env) { eval(content) };
if (options.verbose>0) this.log('Adding agent class constructor '+m+' ('+(typeof ac)+').');
this.addClass(m,ac,env);
this.syntax.offset=0;
}
this.error=undefined;
return true;
} catch (e) {
this.error='Compiling agent class file "'+file+'" failed: '+e+
(ast && ast.errors.length?', in '+errLoc(ast):'');
if (options.error)
options.error(e+(ast && ast.errors.length?', in '+errLoc(ast):''));
else {
this.log(this.error);
}
return false;
}
};
/** Register jamlib event handler for the (root) node
*/
jam.prototype.register = function (node) {
this.on('agent', function (msg) { node.receive(msg) });
this.on('signal', function (msg) { node.handle(msg) });
}
/** Disconnect and remove a virtual node from the world
*
*/
jam.prototype.removeNode = function (nodeid) {
this.world.removeNode(nodeid);
}
/** Tuple space remove operation
*/
jam.prototype.rm = function (pat,all) {
return this.world.nodes[this.node].ts.extern.rm(pat,all);
}
/** Take an agent process snapshot executed currently on given node @node:number|string|undefined.
* If @file:string is not specified, a string containing the snapshot is
* returned, otehrwise it is saved to the file (text format. JSON+).
* If @node is undefined, the current node is used.
* If @kill is set, the agent is killed after taken the snapshot.
*/
jam.prototype.saveSnapshotOn = function (aid,node,file,kill) {
var snapshot,pro;
node=this.getNode(node);
if (!node) return;
// Look-up agent process ..
pro=node.getAgentProcess(aid);
if (!pro) return;
// Take snapshot od the process ..
snapshot=Aios.Code.ofCode(pro,false);
if (kill) Aios.killOn(aid,node);
// Save it ..
if (!file) return snapshot;
else if (fs) return fs.writeFileSync(file, snapshot, 'utf8');
}
jam.prototype.saveSnapshot = function (aid,file,kill) {
return this.saveSnapshotOn(aid,_,file,kill);
}
/** Force a scheduler run immediately normally executed by the
* jam service loop. Required if there were externeal agent
* management, e.g., by sending signals.
*/
jam.prototype.schedule = function () {
if (this.loop) {
clearTimeout(this.loop);
setImmediate(this.looping);
} else if (!this.run) setImmediate(this.looping);
}
/** Access to JAM security module
*
*/
jam.prototype.security = Aios.Sec;
/** Set current node (by index number or node name)
*
*/
jam.prototype.setCurrentNode=function (n) {
if (typeof n == 'number') {
if (n>=0 && n < this.world.nodes.length) this.node=n;
} else if (typeof n == 'string') {
this.node=this.world.nodes.indexOf(this.world.getNode(n))
}
current.node=this.world.nodes[this.node];
}
/** Send a signal to a specific agent 'to'.
*
*/
jam.prototype.signal=function (to,sig,arg,broadcast) {
var node=this.getNode(),
_process=Aios.current.process;
Aios.current.process=this.process;
if (!broadcast)
Aios.aios.send(to,sig,arg);
else
Aios.aios.broadcast(to,sig,arg);
Aios.current.process=_process;
this.schedule();
}
/** Set-up connections, start the JAM, but not the scheduler (used in single-step mode)
*
*/
jam.prototype.start0=function (callback) {
if (this.run) return;
var self=this,cbl=CBL(callback);
// Start all connections if not already done
this.world.nodes.forEach(function (node) {
node.connections.forEach(function (chan,kind) {
if (!chan) return;
if (chan.start) cbl.push(function (next) {chan.start(next)});
});
});
cbl.start();
Aios.on('schedule',function () {
self.schedule();
});
this.world.start();
if (this.verbose) this.log('Starting JAM .. ');
return;
}
/** Set-up connections, start the JAM scheduler
*
*/
jam.prototype.start=function (callback) {
if (this.run) return;
var self=this,
current=Aios.current,
cbl=CBL(callback);
// Start all connections if not already done
this.world.nodes.forEach(function (node) {
node.connections.forEach(function (chan,kind) {
if (!chan) return;
if (chan.start) cbl.push(function (next) {chan.start(next)});
});
});
cbl.start();
Aios.on('schedule',function () {
self.schedule();
});
function loop() {
var loop = function () {
var nexttime,curtime;
if (self.verbose>3) self.log('loop: Entering scheduler #'+self.ticks);
self.ticks++;
nexttime=Aios.scheduler();
curtime=Aios.time();
if (self.verbose>3) self.log('loop: Scheduler returned nexttime='+nexttime+
' ('+(nexttime>0?nexttime-curtime:0)+')');
if (!self.run) return;
if (nexttime>0)
self.loop=setTimeout(loop,nexttime-curtime);
else if (nexttime==0)
self.loop=setTimeout(loop,1000);
else setImmediate(loop);
// else setTimeout(loop,10);
};
self.loop = setTimeout(loop,1);
};
this.looping=loop;
Aios.config({iterations:100});
this.run=true;
this.world.start();
if (this.verbose) this.log('Starting JAM loop .. ');
if (!this.options.scheduler) loop(); // Start internal scheduling loop
}
/** Get agent process table info and other statistics
*
* type kind = 'process'|'agent'|'node'|'vm'|'conn'
*/
jam.prototype.stats = function (kind,id) {
var p,n,sys,conn,pro,agent,state,stats,allstats={},signals,node;
switch (kind) {
case 'process':
case 'agent':
for(n in this.world.nodes) {
stats={};
node=this.world.nodes[n];
for (p in node.processes.table) {
if (node.processes.table[p]) {
pro=node.processes.table[p];
if (pro.signals.length == 0) signals=[];
else signals = pro.signals.map(function (sig) {return sig[0] });
agent=pro.agent;
if (pro.suspended) state='SUSPENDED';
else if (pro.blocked) state='BLOCKED';
else if (pro.dead) state='DEAD';
else if (pro.kill) state='KILL';
else if (pro.move) state='MOVE';
else state='READY';
stats[agent.id]={
pid:pro.pid,
gid:pro.gid,
state:state,
parent:pro.agent.parent,
class:pro.agent.ac,
next:agent.next,
resources:Comp.obj.copy(pro.resources)
};
if (signals.length) stats[agent.id].signals=signals;
}
}
allstats[node.id]=stats;
}
break;
case 'node':
return Comp.obj.copy(this.getNode(id).stats);
break;
case 'conn':
for(n in this.world.nodes) {
stats={};
node=this.world.nodes[n];
for (p in node.connections) {
conn=node.connections[p];
if (conn) {
stats[p]={count:conn.count(),conn:conn.status('%')};
}
}
allstats[node.id]=stats;
}
break;
case 'vm':
// Return VM memory usage in kB units and VM system information
if (process && process.memoryUsage) {
sys=process.memoryUsage();
for ( p in sys) sys[p] = (sys[p]/1024)|0;
sys.v8 = process.versions && process.versions.v8;
sys.node = process.versions && process.versions.node;
sys.arch = process.arch;
sys.platform = process.platform;
sys.watchdog = Aios.watchdog?(Aios.watchdog.checkPoint?'semi':'full'):'none';
return sys;
}
break;
}
if (this.world.nodes.length==1) return stats;
else return allstats;
}
/** Stepping the scheduler loop
*/
jam.prototype.step = function (steps,callback) {
// TODO: accurate timing
var self=this,
sync=callback===true,
milliTime=function () {return Math.ceil(Date.now())},
current=Aios.current,
curtime=Aios.time(),// Aios.time();
lasttime=curtime;
function loop () {
var loop = function () {
var nexttime,curtime;
if (self.verbose>1) self.log('loop: Entering scheduler #'+self.ticks);
self.ticks++,self.steps--;
self.time=curtime=Aios.time();
// Execute scheduler loop
nexttime=Aios.scheduler();
curtime=Aios.time();
if (self.verbose>3) self.log('loop: Scheduler returned nexttime='+nexttime+
' ('+(nexttime>0?nexttime-curtime:0)+')');
if (sync) {
self.time=curtime;
return;
}
if (self.steps==0 || !self.run) {
self.loop=none;
self.run=false;
self.time=curtime;
if (callback) callback();
return;
}
if (nexttime>0)
self.loop=setTimeout(loop,nexttime-curtime);
else if (nexttime < 0) self.loop=setImmediate(loop);
else {
self.loop=none;
self.run=false;
self.time=curtime;
if (callback) callback();
}
};
if (sync) loop();
else self.loop = setTimeout(loop,1);
};
this.looping=loop;
Aios.config({iterations:1});
this.steps=steps;
this.run=true;
if (this.time>0) current.world.lag=current.world.lag+(curtime-this.time);
this.time=curtime;
if (!this.options.scheduler) {
if (sync) {
this.run=true;
for(var step=0;step<steps;step++) loop();
this.run=false;
} else
loop(); // Start internal scheduling loop
}
}
/** Stop the JAM scheduler and all network connections
*
*/
jam.prototype.stop=function (callback) {
if (!this.run) return;
this.run=false,cbl=CBL(callback);
this.log('Stopping JAM ..');
Aios.off('schedule');
if (this.loop)
clearTimeout(this.loop);
this.world.nodes.forEach(function (node) {
node.connections.forEach(function (chan,kind) {
if (!chan) return;
if (chan.stop) cbl.push(function (next) {chan.stop(next)});
});
});
cbl.start();
}
/** Tuple space test operation - non blocking
*/
jam.prototype.test = function (pat) {
return this.world.nodes[this.node].ts.extern.exists(pat);
}
/** Tuple space testandset operation
*/
jam.prototype.ts = function (pat,callback) {
return this.world.nodes[this.node].ts.extern.ts(pat,callback);
}
/** Get JAM time
*/
jam.prototype.time=function () {
return Aios.time();
}
/** Get JAMLIB version
*/
jam.prototype.version=function () {
return options.version;
}
var Jam = function(options) {
var obj = new jam(options);
return obj;
};
/** Embedded cluster setup and start;
* Provided by process arguments
*/
if (environment.autosetup) {
try {
var _options=JSON.parse(environment.autosetup);
// console.log('['+process.pid+'] JAM cluster setup with options:',process.argv[_index+1]);
jam.prototype.setup=function () {
for(var p in _options) this.options[p]=_options[p];
}
} catch (e) {
console.log('['+process.pid+'] JAM auto setup failed: '+e);
}
}
module.exports = {
Aios:Aios,
Comp:Comp,
Esprima:Esprima,
Io:Io,
Jam:Jam,
Json:Json,
environment:environment,
options:options
}
};
BundleModuleCode['jam/aios']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2024 bLAB
** $CREATED: 15-1-16 by sbosse.
** $VERSION: 1.67.2
** $RCS: $Id: aios.js,v 1.6 2020/02/03 09:45:01 sbosse Exp sbosse $
** $INFO:
**
** JavaScript AIOS: Agent Execution & IO System with Sandbox environment.
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var Name = Require('com/pwgen');
var Conf = Require('jam/conf');
var Code = Require('jam/code');
var Sig = Require('jam/sig');
var Node = Require('jam/node');
var Proc = Require('jam/proc');
var Sec = Require('jam/security');
var Ts = Require('jam/ts');
var World = Require('jam/world');
var Chan = Require('jam/chan');
var Mobi = Require('jam/mobi');
var Simu = global.config.simulation?Require(global.config.simulation):none;
var Json = Require('jam/jsonfn');
var watchdog = Require('jam/watchdog');
var util = Require('util');
var Amp = Require('jam/amp')
var aiosExceptions = [
'CREATE',
'MOVE',
'SIGNAL',
'SCHEDULE',
'WATCHDOG',
'KILL'];
var aiosErrors = [
// error name - violation of
'SCHEDULE', // TIMESCHED
'CPU', // TIMEPOOL
'EOL', // LIFETIME
'EOM', // MEMPOOL
'EOT', // TSPOOL
'EOA', // AGENTPOOL
'EOR', // AGENTSIZE ..
];
var aiosEvents = ['agent','agent+','agent-','signal','signal+','node+','node-'];
// AIOS OPTIONS //
var options = {
version: "1.67.2",
debug:{},
// Fast dirty process forking and migration between logical nodes (virtual)
// w/o using of/toCode?
fastcopy:false,
// Using JSON+ (json compliant) or JSOB (raw object) in to/ofCode?
json:false,
// logging parameters
log : {
node:false,
agent:true,
parent:false,
pid:false, // agent process id!
host:false, // host id (os pid)
time:false, // time in milliseconds
Time:true, // time in hour:minute:sec format
date:false, // full date of day
class:false
},
// agent ID generator name options
nameopts : {length:8, memorable:true, lowercase:true},
// Disable agent checkpointing and resource control
nolimits:false,
// No statistics
nostats:false,
// Use process memory for resource control? (slows down JAM execution)
useproc: false,
// Verbosity level
verbose:0,
// Default maximal agent life-time on this host (even idle) in ms
LIFETIME: Infinity,
// Default maximal agent run-time of an agent process activity in ms
TIMESCHED:200,
// Default maximal agent run-time of an agent process in ms
TIMEPOOL:5000,
// Default maximal memory of an agent (code+data)
MEMPOOL:50000,
// Maximal number of tuple generations on current node per agent
TSPOOL:1000,
// Default lifetime of tuples (0: unlimited) => Aios.Ts.options.timeout
TSTMO : 0,
// Maximal number of agent generations on current node (by one agent)
AGENTPOOL:20,
// Default minimal run-time costs below 1ms resolution (very short activity executions)
MINCOST:0.1,
// Default maximal scheduler run-time (ms)
RUNTIME:1000,
// Maximal size in bytes of serialized agent (outgoing migration) < MAX
AGENTSIZE: 60000,
// Maximal size in bytes of serialized agent (incoming migration)
AGENTSIZEMAX: 256000,
// Default scheduler idle-time (maximal nexttime interval, ms)
IDLETIME:0,
// Default AIOS level for received or platform created agents
LEVEL: 1,
// Random service ports (capability protection)
// (public service port: private security port) pairs
security : {
}
};
var timer,
ticks=0, // scheduler execution counter!
iterations=0,
events={};
// Current execution environment (scheduler: global scheduler)
var current = {process:none,world:none,node:none,network:none,error:none,scheduler:none};
// System clock in ms (what=true) or hh:mm:ss format (what=undefined) or
// full date+time (what='date')
function clock (what) {
if (what==undefined) return Io.Time();
else if (what=='date') return Io.Date();
else return Io.time(); // ms clock
}
function format(msg,cls) {
switch (cls) {
case 'aios':
return ('['+(options.log.host?('#'+process.pid+'.'):'')+
(options.log.world&&current.world?(current.world.id+'.'):'')+
(options.log.node&&current.node?(current.node.id+'.'):'')+
(options.log.pid&&current.process?('('+current.process.pid+')'):'')+
(options.log.date?('@'+Io.date()):
(options.log.time?('@'+Io.time()):
(options.log.Time?('@'+Io.Time()):'')))+
'] '+msg);
case 'agent':
return ('['+(options.log.host?('#'+process.pid+'.'):'')+
(options.log.world&&current.world?(current.world.id+'.'):'')+
(options.log.node&&current.node?(current.node.id+'.'):'')+
(options.log.class&&current.process?(current.process.agent.ac+'.'):'')+
(options.log.agent&&current.process?(current.process.agent.id):'')+
(options.log.parent&&current.process?('<'+current.process.agent.parent):'')+
(options.log.pid&&current.process?('('+current.process.pid+')'):'')+
(options.log.date?('@'+Io.date()):
(options.log.time?('@'+Io.time()):
(options.log.Time?('@'+Io.Time()):'')))+
'] '+msg);
default:
return ('['+
(options.log.date?('@'+Io.date()):
(options.log.time?('@'+Io.time()):
(options.log.Time?('@'+Io.Time()):'')))+
'] '+msg)
}
}
// AIOS smart logging function for Agents
var logAgent = function(){
var msg='';
arguments.forEach(function (arg,i) {
if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg);
else msg += (i>0?' '+Io.inspect(arg):Io.inspect(arg));
});
(Aios.printAgent||Aios.print)(format(msg,'agent'))
}
// AIOS smart logging function for AIOS internals (w/o agent messages)
var logAIOS = function(){
var msg='';
arguments.forEach(function (arg,i) {
if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg);
else msg += (i>0?' '+Io.inspect(arg):Io.inspect(arg));
});
if (current.process) (Aios.printAgent||Aios.print)(format(msg,'aios'));
else (Aios.printAgent||Aios.print)(format(msg));
}
// AIOS smart logging function for AIOS internals (w/o agent messages) used by async callbacks
var logAIOSasync = function(){
var msg='';
arguments.forEach(function (arg,i) {
if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg);
else msg += (i>0?' '+Io.inspect(arg):Io.inspect(arg));
});
if (current.process) (Aios.printAsync||Aios.print)(Aios.printAgent||Aios.print)(format(msg,'aios'));
else (Aios.printAsync||Aios.print)(format(msg));
}
// Generic messages (used by other modules and drivers)
var log = function () {
var msg='',pref='';
arguments.forEach(function (arg,i) {
if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg);
else msg += (i>0?', '+Io.inspect(arg):Io.inspect(arg));
});
if (options.log.host && typeof process != 'undefined') pref='#'+process.pid+': ';
if (options.log.date) pref=pref+Io.date()+' ';
else if (options.log.time) pref=pref+Io.time()+' ';
else if (options.log.Time) pref=pref+Io.Time()+' ';
if (msg[0]=='[') Aios.print(pref+msg);
else Aios.print('[AIOS'+pref+'] '+msg);
}
// Generic async messages (async, from callbacks)
var logAsync = function () {
var msg='',pref='';
arguments.forEach(function (arg,i) {
if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg);
else msg += (i>0?', '+Io.inspect(arg):Io.inspect(arg));
});
if (options.log.host && typeof process != 'undefined') pref='#'+process.pid+': ';
if (options.log.date) pref=pref+Io.date()+' ';
else if (options.log.time) pref=pref+Io.time()+' ';
else if (options.log.Time) pref=pref+Io.Time()+' ';
if (msg[0]=='[') (Aios.printAsync||Aios.print)(pref+msg);
else (Aios.printAsync||Aios.print)('[AIOS'+pref+'] '+msg);
}
var eval0=eval;
/** Sandbox module environment for agents (level 0): Untrusted,
* minimal set of operations (no move, fork, kill(others),..)
*/
var aios0 = {
abs:Math.abs,
add: function (a,b) {
var res,i;
if (Comp.obj.isNumber(a) && Comp.obj.isNumber(b)) return a+b;
if (Comp.obj.isArray(a) && Comp.obj.isArray(b)) {
if (a.length!=b.length) return none;
res=Comp.array.copy(a);
for (i in a) {
res[i]=aios0.add(a[i],b[i]);
}
return res;
}
if (Comp.obj.isArray(a) && Comp.obj.isFunction(b)) {
res=Comp.array.copy(a);
for (i in a) {
res[i]=aios0.add(a[i],b.call(current.process.agent,a[i]));
}
return res;
}
if (Comp.obj.isObj(a) && Comp.obj.isObj(b)) {
res={};
for (i in a) {
res[i]=aios0.add(a[i],b[i]);
}
return res;
}
return none;
},
angle: function (p1,p2) {
var angle,v1,v2;
if (Comp.obj.isArray(p1)) v1=p1;
else if (Comp.obj.isObj(p1)) v1=[p1.x,p1.y];
if (Comp.obj.isArray(p2)) v2=p2;
else if (Comp.obj.isObj(p2)) v2=[p2.x,p2.y];
if (p2==undefined) {v2=v1;v1=[0,0]};
angle=Math.atan2(v2[1]-v1[1],v2[0]-v1[0]);
return 180*angle/Math.PI;
},
array: function (cols,init) {
if (init==undefined) init=0;
var row=[];
for(var j=0;j<cols;j++) row.push(typeof init == 'function'?init(j):init);
return row;
},
assign : function (src,dst) {
for(var p in src) dst[p]=src[p]
return dst;
},
Capability: Sec.Capability,
clock: clock,
concat: function (a,b,unique) {
var res,i;
if (Comp.obj.isArray(a) && Comp.obj.isArray(b)) {
if (!unique) return a.concat(b);
res=a.slice();
for(var i in b) {
if (res.indexOf(b[i])==-1) res.push(b[i]);
}
return res;
} else if (Comp.obj.isObj(a) && Comp.obj.isObj(b)) {
res={};
for (i in a) {
res[i]=a[i];
}
for (i in b) {
res[i]=b[i];
}
return res;
} else if (Comp.obj.isString(a) && Comp.obj.isString(b)) {
return a+b;
} else
return undefined;
},
contains : function (o,e) {
// e can be a scalar or array of values
if (Comp.obj.isArray(o))
return Comp.array.contains(o,e);
else if (Comp.obj.isObj(o) && (Comp.obj.isString(e) || Comp.obj.isNumber(e)))
return o[e] != undefined;
else if (Comp.obj.isString(o) && Comp.obj.isString(e))
return o.indexOf(e)!=-1
},
copy : function (o) {
// recursively copy objects
var _o,p;
if (Comp.obj.isArray(o)) {
if (typeof o[0] != 'object') return o.slice();
else return o.map(function (e) {
if (typeof e == 'object') return aios0.copy(e);
else return e;
});
} else if (Comp.obj.isObject(o)) {
_o={};
for(p in o) _o[p]=(typeof o[p]=='object'?aios0.copy(o[p]):o[p]);
return _o;
}
else if (Comp.obj.isString(o))
return o.slice();
else return o;
},
delta : function (o1,o2) {
var res;
if (Comp.obj.isArray(o1) && Comp.obj.isArray(o2)) {
if (o1.length != o2.length) return;
res=[];
for (var i in o1) res[i]=o1[i]-o2[i];
} else if (Comp.obj.isObject(o1) && Comp.obj.isObject(o2)) {
res={};
for (var p in o1) res[p]=o1[p]-o2[p];
}
return res;
},
distance: function (p1,p2) {
var y=0;
if (p2) for(var p in p1) if (typeof p1[p] == 'number' &&
typeof p2[p] == 'number') y+=Math.pow(p1[p]-p2[p],2);
else for(var p in p1) if (typeof p1[p] == 'number') y+=Math.pow(p1[p],2);
return Math.sqrt(y)
},
div: div,
dump: function (x) {
if (x=='res') x=Comp.obj.copy(current.process.resources);
if (x=='?') x=this;
logAgent(util.inspect(x)); },
empty: function (o) {
if (Comp.obj.isArray(o) || Comp.obj.isString(o)) return o.length==0;
else if (Comp.obj.isObj(o)) return Comp.obj.isEmpty(o);
else return false;
},
equal: function (a,b) {
var i;
if (Comp.obj.isNumber(a) && Comp.obj.isNumber(b)) return a==b;
else if (Comp.obj.isArray(a) && Comp.obj.isArray(b)) {
if (a.length!=b.length) return false;
for (i in a) {
if (!aios0.equal(a[i],b[i])) return false;
}
return true;
}
else if (Comp.obj.isObj(a) && Comp.obj.isObj(b)) {
for (i in a) {
if (!aios0.equal(a[i],b[i])) return false;
}
return true;
}
else if (Comp.obj.isString(a) && Comp.obj.isString(b))
return (a.length==b.length && a==b)
return false;
},
filter:function (a,f) {
var element,res=[],len,len2,i,j,found;
if (Comp.obj.isArray(a) && Comp.obj.isFunction(f)) {
res=[];
len=a.length;
for(i=0;i<len;i++) {
element=a[i];
if (f.call(current.process.agent,element,i)) res.push(element);
}
return res;
} else if (Comp.obj.isArray(a) && Comp.obj.isArray(f)) {
res=[];
len=a.length;
len2=f.length;
for(i=0;i<len;i++) {
element=a[i];
found=false;
for (j=0;j<len2;j++) if(element==f[j]){found=true; break;}
if (!found) res.push(element);
}
return res;
} else return undefined;
},
flatten : function (a,level) {
if (Comp.obj.isMatrix(a)) { // [][] -> []
return a.reduce(function (flat, toFlatten) {
return flat.concat(Array.isArray(toFlatten) && level>1? aios0.flatten(toFlatten,level-1) : toFlatten);
}, []);
} else if (Comp.obj.isObj(a)) { // {{}} {[]} -> {}
function flo (o) {
var o2={},o3;
for(var p in o) {
if (typeof o[p]=='object') {
o3=flo(o[p]);
for(var p2 in o3) {
o2[p+p2]=o3[p2];
}
} else o2[p]=o[p];
}
return o2;
}
return flo(a);
}
return a;
},
head:function (a) {
if (Comp.obj.isArray(a))
return Comp.array.head(a);
else return undefined;
},
id:aidgen,
info:function (kind) {
switch (kind) {
case 'node':
return {
id:current.node.id,
position: current.node.position,
location:current.node.location,
type:current.node.type,
};
case 'version':
return options.version;
case 'host':
return {
type:global.TARGET
};
}
},
int: int,
isin: function (o,v) {
var p;
if (Comp.obj.isArray(o)) {
for(p in o) if (aios0.equal(o[p],v)) return true;
return false;
} else if (Comp.obj.isObj(o)) {
for(p in o) if (aios0.equal(o[p],v)) return true;
return false;
} else if (Comp.obj.isString(o)) {
return o.indexOf(v)!=-1
}
},
iter:function (obj,fun) {
var p;
if (Comp.obj.isArray(obj))
for(p in obj) fun.call(current.process.agent,obj[p],Number(p));
else
for(p in obj) fun.call(current.process.agent,obj[p],p)
},
keys: Object.keys,
kill:function () {kill(current.process.agent.id)},
last: function (o) {
if (o==undefined) return;
else if (Comp.obj.isArray(o) || Comp.obj.isString(o))
return o[o.length-1];
else if (Comp.obj.isObj(o)) {
var p,l;
for(p in o) if (o[p]!=undefined) l=o[p];
return l;
}
},
length: function (o) {
if (o==undefined) return 0;
else if (Comp.obj.isObj(o)) {
var p,l=0;
for(p in o) if (o[p]!=undefined) l++;
return l;
} else return o.length
},
log:function () { logAgent.apply(_,arguments) },
map:function (a,f) {
var res,i,p;
if (Comp.obj.isArray(a) && Comp.obj.isFunction(f)) {
res=[];
for (i in a) {
v=f.call(current.process.agent,a[i],i);
if (v!=undefined) res.push(v);
}
return res;
} else if (Comp.obj.isObject(a) && Comp.obj.isFunction(f)) {
// Objects can be filtered (on first level), too!
res={};
for(p in a) {
v=f.call(current.process.agent,a[p],p);
if (v != undefined) res[p]=v;
}
return res;
} else return undefined;
},
matrix: function (x,y,init) {
var row=[];
var mat=[];
for (var j=0;j<y;j++) {
row=[];
for(var i=0;i<x;i++)
row.push(init||0)
mat.push(row)
}
return mat;
},
max: function (a,b) {
if (Comp.obj.isArray(a)) {
var f=function (x) {return x},v,vi;
if (Comp.obj.isFunction(b)) f=b;
Comp.array.iter(a,function (a0,i) {
a0=f(a0);
if (v==undefined || a0>v) {v=a0; vi=i};
});
if (vi!=undefined) return a[vi];
} else return Math.max(a,b);
},
me: function () {
return current.process.agent.id;
},
min: function (a,b) {
if (Comp.obj.isArray(a)) {
var f=function (x) {return x},v,vi;
if (Comp.obj.isFunction(b)) f=b;
Comp.array.iter(a,function (a0,i) {
a0=f(a0);
if (v==undefined || a0<v) {v=a0; vi=i};
});
if (vi!=undefined) return a[vi];
} else return Math.min(a,b);
},
myClass: function () {
return current.process.agent.ac;
},
myLevel: function () {
return current.process.level;
},
myNode: function () {
return current.node.id;
},
myParent: function () {
return current.process.agent.parent;
},
myPosition: function () {
return current.node.location||current.node.position;
},
neg: function (v) {
var p;
if (Comp.obj.isNumber(v)) return -v;
if (Comp.obj.isArray(v)) return v.map(function (e) {return aios0.neg(e)});
if (Comp.obj.isObj(v)) {
var o=v,_o={};
for(p in o) _o[p]=typeof o[p]=='number'?-o[p]:o[p];
return _o;
}
},
negotiate:function (res,val,cap) { return negotiate(0,res,val,cap) },
next:function () {},
object : function (str) {
var myobj={data:null};
with ({myobj:myobj, str:str}) { myobj.data=eval0('var _o='+str+';_o') };
return myobj.data;
},
pluck : function (table,column) {
var res=[];
for(var i in table) {
res.push(table[i][column]);
}
return res;
},
privilege: function () {return 0},
Port: Sec.Port,
Private: Sec.Private,
random: function (a,b,frac) {
var r,n,p,i,keys,k;
if (Comp.obj.isArray(a)) {
n = a.length;
if (n>0)
return a[Comp.random.int(n)];
else
return none;
} else if (Comp.obj.isObj(a)) {
keys=Object.keys(a);
n = keys.length;
if (n>0)
return a[keys[Comp.random.int(n)]];
else
return none;
} else if (b==undefined) {b=a;a=0};
if (!frac ||frac==1)
return Comp.random.interval(a,b);
else {
r=Comp.random.range(a,b);
return ((r/frac)|0)*frac;
}
},
reduce : function (a,f) {
if (Comp.obj.isArray(a)) {
return a.reduce(function (a,b) {
return current.process?f.call(current.process.agent,a,b):f(a,b);
});
}
},
reverse: function (a) {
if (Comp.obj.isArray(a))
return a.slice().reverse();
else if (Comp.obj.isString(a))
return a.split("").reverse().join("")
},
sleep:Sig.agent.sleep,
sort: function (a,f) {
if (Comp.obj.isArray(a) && Comp.obj.isFunction(f)) {
return Comp.array.sort(a,function (x,y) {
return f.call(current.process.agent,x,y);
});
} else return undefined;
},
sum: function (o,f) {
if (Comp.obj.isArray(o)) return Comp.array.sum(o,f);
else if (Comp.obj.isObject(o)) {
var s=0,p;
if (!f) f=function(x){return x};
for(p in o) s+=f(o[p]);
return s;
}
},
string:function (o) {if (Comp.obj.isString(o)) return o; else return o.toString()},
tail:function (a) {
if (Comp.obj.isArray(a))
return Comp.array.tail(a);
else return undefined;
},
time:function () { return time()-current.world.lag},
// returns a without b
without : function (a,b) {
if (Comp.obj.isArray(a) && (Comp.obj.isArray(b)))
return a.filter(function (v) {
return !aios0.contains(b,v);
});
else if (Comp.obj.isArray(a))
return a.filter(function (v) {
return !aios0.equal(b,v);
});
},
zero: function (a) {
var i;
if (Comp.obj.isNumber(a)) return a==0;
if (Comp.obj.isArray(a)) {
for (i in a) {
if (!aios0.zero(a[i])) return false;
}
return true;
}
if (Comp.obj.isObj(a)) {
for (i in a) {
if (!aios0.zero(a[i])) return false;
}
return true;
}
return false;
},
Vector: function (x,y,z) {var o={}; if (x!=_) o['x']=x; if (y!=_) o['y']=y; if (z!=_) o['z']=z; return o},
// Scheduling and checkpointing
B:B,
CP:CP,
I:I,
L:L,
RT:RT,
Math:Math
}
// Sandbox module environment for agents (level 1): Trusted, standard operational set
var aios1 = {
abs:aios0.abs,
act:Conf.agent.act,
add:aios0.add,
angle:aios0.angle,
alt:Ts.agent.alt,
array:aios0.array,
assign:aios0.assign,
broadcast:Sig.agent.broadcast,
Capability: Sec.Capability,
clock: clock,
collect:Ts.agent.collect,
concat:aios0.concat,
contains:aios0.contains,
copy:aios0.copy,
copyto:Ts.agent.copyto,
// type create = function(ac:string|object,args:object|[]) -> agentid:string
create: function(ac,args,level) {
if (level==undefined || level>1) level=1;
if (args==undefined) args={};
var process=none,code;
if (!Comp.obj.isArray(args) && !Comp.obj.isObject(args)) {
current.error='Invalid argument: Agent argument is neither array nor object';
throw 'CREATE';
};
current.process.resources.agents++;
if (typeof ac == 'object') {
// indeed a forking with modified act/trans/body
// { x:this.x, .., act : {}|[], trans:{}, on:[}}
process = Code.createFromOn(current.node,current.process,ac,level);
} else if (current.world.classes[ac] && current.world.classes[ac][level])
process = Code.createOn(current.node,current.world.classes[ac][level],args,level,ac);
else if (current.process.agent.subclass && current.process.agent.subclass[ac]) {
process = Code.createOn(current.node,current.process.agent.subclass[ac],args,level,ac);
} else {
current.error='Invalid argument: Unknown agent class '+ac;
throw 'CREATE';
}
if (process) {
if (current.process!=none && process.gid==none) {
process.gid=current.process.pid;
if (!process.agent.parent)
process.agent.parent=current.process.agent.id;
}
return process.agent.id;
} else return none;
},
delta:aios0.delta,
distance: aios0.distance,
div: aios0.div,
dump: aios0.dump,
empty:aios0.empty,
evaluate:Ts.agent.evaluate,
equal:aios0.equal,
exists:Ts.agent.exists,
Export:function (name,code) { current.node.export(name,code) },
filter:aios0.filter,
flatten:aios0.flatten,
fork:function (parameter) {var process = current.process.fork(parameter,undefined,options.fastcopy); return process.agent.id},
head:aios0.head,
id:aidgen,
Import:function (name) { return current.node.import(name) },
info:aios0.info,
inp:Ts.agent.inp,
int: aios0.int,
isin: aios0.isin,
iter:aios0.iter,
keys: Object.keys,
kill:function (aid) {if (aid==undefined) kill(current.process.agent.id); else kill(aid)},
last: aios0.last,
length: aios0.length,
link:function (dir) {return current.world.connected(dir,current.node)},
listen:Ts.agent.listen,
log:aios0.log,
me:aios0.me,
mark:Ts.agent.mark,
map:aios0.map,
max:aios0.max,
matrix:aios0.matrix,
moveto:Mobi.agent.move,
min:aios0.min,
myClass:aios0.myClass,
myNode:aios0.myNode,
myLevel:aios0.myLevel,
myParent:aios0.myParent,
myPosition:aios0.myPosition,
neg:aios0.neg,
negotiate:function (res,val,cap) { return negotiate(1,res,val,cap) },
object:aios0.object,
opposite:Mobi.agent.opposite,
out:Ts.agent.out,
pluck : aios0.pluck,
Port: Sec.Port,
position: function () {return current.node.position},
Private: Sec.Private,
privilege: function () {return 1},
random: aios0.random,
rd:Ts.agent.rd,
reduce:aios0.reduce,
reverse:aios0.reverse,
rm:Ts.agent.rm,
security: Sec,
send:Sig.agent.send,
sendto:Sig.agent.sendto,
sleep:Sig.agent.sleep,
sort:aios0.sort,
store:Ts.agent.store,
string:aios0.string,
sum:aios0.sum,
tail:aios0.tail,
test:Ts.agent.exists,
time:aios0.time,
timer:Sig.agent.timer,
trans:Conf.agent.trans,
try_alt:Ts.agent.try.alt,
try_inp:Ts.agent.try.inp,
try_rd:Ts.agent.try.rd,
ts:Ts.agent.ts,
wakeup:Sig.agent.wakeup,
without:aios0.without,
zero:aios0.zero,
B:B,
CP:CP,
I:I,
L:L,
RT:RT,
Vector:aios0.Vector,
DIR:Mobi.agent.DIR,
Math:Math
};
// Sandbox module environment for agents (level 2): Trusted with extended privileges
var aios2 = {
abs:aios0.abs,
add:aios0.add,
act:Conf.agent.act,
angle:aios0.angle,
alt:Ts.agent.alt,
array:aios0.array,
assign:aios0.assign,
broadcast:Sig.agent.broadcast,
Capability: Sec.Capability,
clock: clock,
collect:Ts.agent.collect,
concat:aios0.concat,
contains:aios0.contains,
copy:aios0.copy,
copyto:Ts.agent.copyto,
create: function(ac,args,level) {
var process=none;
if (level==undefined || level>2) level=2;
if (args==undefined) args={};
if (!Comp.obj.isArray(args) && !Comp.obj.isObject(args)) {
current.error='Invalid argument: Agent arguments is neither array nor object';
throw 'CREATE';
};
current.process.resources.agents++;
if (typeof ac == 'object') {
// indeed a forking with modified act/trans/body
// { x:this.x, .., act : {}|[], trans:{}, on:[}}
process = Code.createFromOn(current.node,current.process,ac,level);
} else if (current.world.classes[ac] && current.world.classes[ac][level])
process = Code.createOn(current.node,current.world.classes[ac][level],args,level,ac);
else if (current.process.agent.subclass && current.process.agent.subclass[ac]) {
process = Code.createOn(current.node,current.process.agent.subclass[ac],args,level,ac);
} else {
current.error='Invalid argument: Unknown agent class '+ac;
throw 'CREATE';
}
if (process) {
process.agent.ac=ac;
if (current.process!=none && process.gid==none) {
process.gid=current.process.pid;
if (process.agent.parent==_ || process.agent.parent==none)
process.agent.parent=current.process.agent.id;
}
return process.agent.id;
} else return none;
},
delta:aios0.delta,
distance: aios0.distance,
div: aios0.div,
dump: aios0.dump,
empty:aios0.empty,
evaluate:Ts.agent.evaluate,
equal:aios0.equal,
exists:Ts.agent.exists,
Export:function (name,code) { current.node.export(name,code) },
filter:aios0.filter,
flatten:aios0.flatten,
fork:function (parameter) {var process = current.process.fork(parameter); return process.agent.id},
head:aios0.head,
id:aidgen,
Import:function (name) { return current.node.import(name) },
info:aios0.info,
inp:Ts.agent.inp,
int: aios0.int,
isin: aios0.isin,
iter:aios0.iter,
keys: Object.keys,
kill:function (aid) {if (aid==undefined) kill(current.process.agent.id); else kill(aid)},
last: aios0.last,
length: aios0.length,
link:function (dir) {return current.world.connected(dir,current.node)},
listen:Ts.agent.listen,
log:aios0.log,
max:aios0.max,
me:aios0.me,
min:aios0.min,
myClass:aios0.myClass,
myNode:aios0.myNode,
myLevel:aios0.myLevel,
myParent:aios0.myParent,
myPosition:aios0.myPosition,
mark:Ts.agent.mark,
map:aios0.map,
matrix:aios0.matrix,
moveto:Mobi.agent.move,
neg:aios0.neg,
negotiate:function (res,val,cap) { return negotiate(2,res,val,cap) },
object:aios0.object,
opposite:Mobi.agent.opposite,
out:Ts.agent.out,
random: aios0.random,
reduce:aios0.reduce,
rd:Ts.agent.rd,
reverse:aios0.reverse,
rm:Ts.agent.rm,
pluck : aios0.pluck,
Port: Sec.Port,
position: function () {return current.node.position},
Private: Sec.Private,
privilege: function () {return 2},
security: Sec,
send:Sig.agent.send,
sendto:Sig.agent.sendto,
sleep:Sig.agent.sleep,
sort:aios0.sort,
store:Ts.agent.store,
string:aios0.string,
sum:aios0.sum,
tail:aios0.tail,
test:Ts.agent.exists,
time:aios0.time,
timer:Sig.agent.timer,
trans:Conf.agent.trans,
try_alt:Ts.agent.try.alt,
try_inp:Ts.agent.try.inp,
try_rd:Ts.agent.try.rd,
ts:Ts.agent.ts,
wakeup:Sig.agent.wakeup,
without:aios0.without,
zero:aios0.zero,
B:B,
CP:CP,
I:I,
L:L,
RT:RT,
Vector:aios0.Vector,
DIR:Mobi.agent.DIR,
Math:Math,
};
// Sandbox module environment for agents (level 3): Trusted with extended privileges, system level
// May not migrate!!
var aios3 = {
abs:aios0.abs,
act:Conf.agent.act,
add:aios0.add,
angle:aios0.angle,
alt:Ts.agent.alt,
array:aios0.array,
assign:aios0.assign,
broadcast:Sig.agent.broadcast,
Capability: Sec.Capability,
clock: clock,
collect:Ts.agent.collect,
connectTo:function (dir,options) {
// Connect this node with another node using a virtual or physical channel link
var node=current.node, world=current.world;
if (!dir || !dir.tag) throw('CONNECT');
world.connectTo(dir,node,options);
},
concat:aios0.concat,
contains:aios0.contains,
copy:aios0.copy,
copyto:Ts.agent.copyto,
create: function(ac,args,level) {
var process=none;
if (level==undefined) level=3;
if (args==undefined) args={};
if (!Comp.obj.isArray(args) && !Comp.obj.isObject(args)) {
current.error='Invalid argument: Agent arguments is neither array nor object';
throw 'CREATE';
};
current.process.resources.agents++;
if (typeof ac == 'object') {
// indeed a forking with modified act/trans/body
// { x:this.x, .., act : {}|[], trans:{}, on:[}}
process = Code.createFromOn(current.node,current.process,ac,level);
} else if (current.world.classes[ac] && current.world.classes[ac][level])
process = Code.createOn(current.node,current.world.classes[ac][level],args,level,ac);
else if (current.process.agent.subclass && current.process.agent.subclass[ac]) {
process = Code.createOn(current.node,current.process.agent.subclass[ac],args,level,ac);
} else {
current.error='Invalid argument: Unknown agent class '+ac;
throw 'CREATE';
}
if (process) {
process.agent.ac=ac;
if (current.process!=none && process.gid==none) {
process.gid=current.process.pid;
if (process.agent.parent==_ || process.agent.parent==none)
process.agent.parent=current.process.agent.id;
}
return process.agent.id;
} else return none;
},
delta:aios0.delta,
distance: aios0.distance,
div: aios0.div,
dump: aios0.dump,
empty:aios0.empty,
equal:aios0.equal,
evaluate:Ts.agent.evaluate,
exists:Ts.agent.exists,
Export:aios2.Export,
filter:aios0.filter,
flatten:aios0.flatten,
fork:aios2.fork,
head:aios0.head,
id:aidgen,
Import:aios2.Import,
info:aios0.info,
inp:Ts.agent.inp,
int: aios0.int,
isin: aios0.isin,
iter:aios0.iter,
keys: Object.keys,
kill:aios2.kill,
last: aios0.last,
length:aios0.length,
link:aios2.link,
listen:Ts.agent.listen,
log:aios0.log,
max:aios0.max,
me:aios0.me,
min:aios0.min,
myClass:aios0.myClass,
myLevel:aios0.myLevel,
myNode:aios0.myNode,
myParent:aios0.myParent,
myPosition:aios0.myPosition,
mark:Ts.agent.mark,
map:aios0.map,
matrix:aios0.matrix,
moveto:function () {/* System level agents may not migrate ! */ current.error='ENOTSUPPORTED';throw 'MOVE';},
neg:aios0.neg,
negotiate:function (res,val,cap) { return negotiate(3,res,val,cap) },
object:aios0.object,
opposite:Mobi.agent.opposite,
out:Ts.agent.out,
pluck : aios0.pluck,
Port: Sec.Port,
position: function () {return current.node.position},
Private: Sec.Private,
privilege: function () {return 3},
reduce:aios0.reduce,
random: aios0.random,
rd:Ts.agent.rd,
reverse:aios0.reverse,
rm:Ts.agent.rm,
send:Sig.agent.send,
sendto:Sig.agent.sendto,
sleep:aios0.sleep,
sort:aios0.sort,
store:Ts.agent.store,
string:aios0.string,
sum:aios0.sum,
tail:aios0.tail,
test:Ts.agent.exists,
time:aios0.time,
timer:Sig.agent.timer,
trans:Conf.agent.trans,
try_alt:Ts.agent.try.alt,
try_inp:Ts.agent.try.inp,
try_rd:Ts.agent.try.rd,
ts:Ts.agent.ts,
wakeup:Sig.agent.wakeup,
without:aios0.without,
zero:aios0.zero,
B:B,
CP:CP,
I:I,
L:L,
RT:RT,
Vector:aios0.Vector,
DIR:Mobi.agent.DIR,
Math:Math,
// Exucute an IO block sequence in an agent process context
IOB: function (block) {
var proc=current.process;
setImmediate(function () {
var index=0;
function next (to) {
var _proc=current.process,_node=current.node;
if (to==none) {
// done or failiure
proc.mask.next=undefined;
proc.wakeup();
return;
}
index=index+to;
try {
current.process=proc; current.node=proc.node;
block[index].call(proc.agent);
} catch (e) {
logAgent('Caught IOB error: '+e);
}
current.process=_proc; current.node=_node;
}
proc.mask.next=next;
next(0);
});
proc.suspend();
}
};
var aios = aios1;
/*
** Agent code scheduling blocks can migrate
** - must be handled different from internal scheduling blocks!
*/
// Schedule linear sequence of functions that may block (suspending execution of current agent process).
function B(block) {
if (current.process.schedule.length==0)
current.process.schedule = block;
else
current.process.schedule = Comp.array.concat(block,current.process.schedule);
}
/** Add pending callback call to process scheduling block
*
*/
function CB(process,cb,args) {
if (args)
Comp.array.push(process.schedule,function () { cb.apply(this,args) });
else
Comp.array.push(process.schedule,cb);
}
/** Agent process activity check pointing (injected in loops/functions)
*
*/
function CP() {
if (current.process.runtime && (current.process.runtime+current.world.lag-Date.now())<0) throw "SCHEDULE";
return true;
}
/** Agent exception checker; agents may not consume scheduler/watchdog exceptions!!
*/
function RT(e) {
if (['WATCHDOG','SCHEDULE'].indexOf(e.toString())!=-1) throw(e);
}
/** Schedule an object iteration sequence that may block (suspending execution of current agent process).
*
*/
function I(obj,next,block,finalize) {
/*
** Iterate and schedule a block
* obj: []
* next: function(next) {}
*/
var index=0;
var length=obj.length;
var iterator = [
function() {
next(obj[index]);
if (index<length) {
B(block.slice());
index++;
}
},
function () {
if (index<length) B(iterator.slice());
else if (finalize) finalize.call(this);
}
];
B(iterator.slice());
}
// Schedule a loop iteration sequence that may block (suspending execution of current agent process).
function L(init,cond,next,block,finalize) {
/*
** Iterate and schedule a block
* init: function() {}
* cond: function() { return cond; }
* next: function() {}
*/
var loop = [
function() {
if (cond.call(this)) B(block.slice());
},
next,
function () {
if (cond.call(this)) B(loop.slice());
}
];
B(loop.slice());
B([init]);
}
/** Agent Identifier Generator
*
*/
function aidgen(_options) {
return Name.generate(_options||options.nameopts);
}
/** AIOS configuration
*
*/
function config(settings) {
for (var p in settings) {
switch (p) {
case 'iterations': iterations=settings[p]; break;
case 'fastcopy': options.fastcopy=settings[p]; break;
case 'verbose': options.verbose=settings[p]; break;
case 'log+': options.log[settings[p]]=true; break;
case 'log-': options.log[settings[p]]=false; break;
case 'log':
// log options object override
for(var l in settings[p]) options.log[l]=settings[p][l];
break;
case 'nolimits':
if (settings[p])
Aios.watchdog=undefined,
options.nolimits=true,
Aios.Code.inject.cp=undefined,
Aios.Code.inject.rt=undefined;
break;
case 'nowatch':
if (settings[p])
Aios.watchdog=undefined,
Aios.Code.inject.cp=undefined,
Aios.Code.inject.rt=undefined;
break;
case 'checkpoint':
if (settings[p])
Aios.watchdog=undefined,
Aios.Code.inject.cp='CP',
Aios.Code.inject.rt='RT';
break;
case 'security':
// Capability port-random pairs
for (var q in settings[p]) {
var port=Sec.Port.ofString(q),
random=Sec.Port.ofString(settings[p][q]);
options.security[port]=random;
}
break;
case 'print': Aios.print=settings[p]; break;
case 'printAgent': Aios.printAgent=settings[p]; break;
case 'printAsync': Aios.printAsync=settings[p]; break;
case 'LEVEL': options.LEVEL=settings[p]; break;
case 'LIFETIME': options.LIFETIME=settings[p]; break;
case 'TIMESCHED': options.TIMESCHED=settings[p]; break;
case 'TIMEPOOL': options.TIMEPOOL=settings[p]; break;
case 'MEMPOOL': options.MEMPOOL=settings[p]; break;
case 'TSPOOL': options.TSPOOL=settings[p]; break;
case 'AGENTPOOL': options.AGENTPOOL=settings[p]; break;
case 'MINCOST': options.MINCOST=settings[p]; break;
case 'RUNTIME' : options.RUNTIME=settings[p]; break;
case 'IDLETIME' : options.IDLETIME=settings[p]; break;
case 'TSTMO': options.TSTMO=Aios.Ts.options.timeout=settings[p]; break;
case 'time':
time=settings[p];
// Update alle time and CP references
Aios.time=aios0.time=aios1.time=aios2.time=aios3.time=time;
Aios.CP=aios0.CP=aios1.CP=aios2.CP=aios3.CP=function () {
if (current.process.runtime && (current.process.runtime+current.world.lag-time())<0) throw "SCHEDULE";
return true;
};
break;
}
}
}
function configGet() {
return {
LEVEL : options.LEVEL,
LIFETIME : options.LIFETIME,
TIMESCHED : options.TIMESCHED,
TIMEPOOL : options.TIMEPOOL,
TSPOOL : options.TSPOOL,
AGENTPOOL : options.AGENTPOOL,
MEMPOOL : options.MEMPOOL,
MINCOST : options.MINCOST,
RUNTIME : options.RUNTIME,
IDLETIME : options.IDLETIME,
TSTMO : Aios.Ts.options.timeout,
security : Object.keys(options.security).map(function (port) {
return { port:Sec.Port.toString(port), random:Sec.Port.toString(options.security[port])}
}),
checkpoint: { cp:Aios.Code.inject.cp, rt:Aios.Code.inject.rt, watchdog: Aios.watchdog?true:fals },
nolimits : options.nolimits,
iterations: iterations,
fastcopy : options.fastcopy,
verbose : options.verbose,
log : options.log,
}
}
function dump(e) {
var e = e ||(new Error('dummy'));
var stack = e.stack.replace(/^[^\(]+?[\n$]/gm, '')
.replace(/^\s+at\s+/gm, '')
.replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@')
.split('\n');
log(e);
log('Stack Trace');
log('--------------------------------');
for(var i in stack) {
if (i>0) {
var line = stack[i];
if(line.indexOf('Module.',0)>=0) break;
log(line);
}
}
log('--------------------------------');
};
/** Emit event
* function emit(@event,@arg1,..)
*/
function emit() {
if (events[arguments[0]])
events[arguments[0]](arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]);
}
/** Try to get the source position of an error raised in an agent activity
*
*/
function errorLocation(process,err) {
try {
var stack = err.stack.split('\n');
for (var i in stack) {
var line=stack[i];
if (line.indexOf('at act.')>=0||line.indexOf('at F.act.')>=0) {
return line.replace(/\([^\)]+\)/,'').replace(/\)/,'');
}
else if (line.indexOf('at trans.')>=0 || line.indexOf('at F.trans.')>=0) {
return line.replace(/\([^\)]+\)/,'').replace(/\)/,'');
}
}
return '';
} catch (e) {
return '';
}
}
// Execute a block scheduling function
function exec_block_fun(next) {
var fun = next[0]||next,
argn = next.length-1;
switch (argn) {
case 0:
case -1:
fun(); break;
case 1: fun(next[1]); break;
case 2: fun(next[1],next[2]); break;
case 3: fun(next[1],next[2],next[3]); break;
case 4: fun(next[1],next[2],next[3],next[4]); break;
case 5: fun(next[1],next[2],next[3],next[4],next[5]); break;
case 6: fun(next[1],next[2],next[3],next[4],next[5],next[6]); break;
case 7: fun(next[1],next[2],next[3],next[4],next[5],next[6],next[7]); break;
case 8: fun(next[1],next[2],next[3],next[4],next[5],next[6],next[7],next[8]); break;
case 9: fun(next[1],next[2],next[3],next[4],next[5],next[6],next[7],next[8],next[9]); break;
default:
// TODO: fun.apply(undefined,next.slice(1))
Io.err('Aios.exec_block_fun: more than 9 function arguments');
}
}
/** Fork the current agent with an optional new set of parameters.
*
*/
function fork(parameters) {
return current.process.fork(parameters);
}
/** Kill an agent (if agent identifier is undefined the current agent will be killed).
*
*/
function kill(agent) {
var process;
if (!agent) {
process=current.process;
} else {
process=current.node.processes.process(agent);
}
if (options.debug.kill) console.log('Aios.kill',agent,process!=null);
if (process) {
process.kill=true;
current.node.unregister(process);
return true;
} else if (current.node.processes.gone[agent]) {
// migrated agent: try to send kill signal!
Sig.agent.send(agent,'PROC.KILL',9,current.process.agent.id);
} else return false;
}
function killOn(agent,node) {
var process;
process=node.processes.process(agent);
if (process) {
process.kill=true;
node.unregister(process);
};
}
/** Lock the global namespace. Disable inter-agent communication
* by using the global namespace => Sandbox (level 2)
*
*/
function lock() {
Object.preventExtensions(global);
}
/** Execute agent processes until there are no more schedulable agents.
* Loop returns if there are no more runnable agents. If there are waiting
* agent processes, the loop will be rescheduled on the earliest time event.
*
*/
function loop(services) {
var nexttime = scheduler(services);
if (nexttime>0) {
// Nothing to do.
// Sleep until next event and re-enter the scheduling loop.
if (options.verbose>3) log('[LOOP '+current.node.id+'] next schedule on '+ nexttime);
timer=setTimeout(function () {loop (services)},nexttime-time());
}
}
function min0(a,b) { return a==0?b:(b==0?a:Comp.pervasives.min(a,b)) };
/** Call agent exception handler. If exception was handled by agent return true, otherwise false.
*
*/
function handleException(process,exc,arg1,arg2,arg3,arg4) {
var agent=process.agent;
if (Aios.watchdog && Aios.watchdog.protect) {
try { Aios.watchdog.protect(function () {agent.on[exc].call(agent,arg1,arg2,arg3,arg4)})} catch(e) {
// If there is no handler managing the error (e.g. SCHEDULE), the agent must be terminated!
if (options.verbose) logAIOS ('Agent '+agent.id+' ['+agent.ac+'] failed handling '+exc+'('+arg1+')');
process.kill=true
return false;
};
} else
try {agent.on[exc].call(agent,arg1,arg2,arg3,arg4)} catch(e) {
// If there is no handler managing the error (e.g. SCHEDULE), the agent must be terminated!
if (options.verbose) logAIOS ('Agent '+agent.id+' ['+agent.ac+'] failed handling '+exc+'('+arg1+')');
process.kill=true
return false;
}
return true;
}
/** Agent resource constraint negotiation
*
*/
function negotiate (level,resource,value,cap) {
var obj,security=options.security;
// Check capability rights
function checkRights(r) {
return (level > 1 ||
(cap && security[cap.cap_port] && Sec.Private.rights_check(cap.cap_priv,security[cap.cap_port],r)))
}
switch (resource) {
case 'LIFE':
case 'LIFETIME':
if (!checkRights(Sec.Rights.NEG_LIFE)) return false;
current.process.resources.LIFE=value; break;
case 'CPU':
case 'TIMEPOOL':
if (!checkRights(Sec.Rights.NEG_CPU)) return false;
current.process.resources.CPU=value; break;
case 'SCHED':
case 'SCHEDULE':
case 'TIMESCHED':
if (!checkRights(Sec.Rights.NEG_SCHED)) return false;
current.process.resources.SCHED=value; break;
case 'MEM':
case 'MEMORY':
case 'MEMPOOL':
if (!checkRights(Sec.Rights.NEG_RES)) return false;
current.process.resources.MEM=value; break;
case 'TS':
case 'TSPOOL':
if (!checkRights(Sec.Rights.NEG_RES)) return false;
current.process.resources.TS=value; break;
case 'AGENT':
case 'AGENTPPOL':
if (!checkRights(Sec.Rights.NEG_RES)) return false;
current.process.resources.AGENT=value; break;
case 'LEVEL':
if (!checkRights(Sec.Rights.NEG_LEVEL)) return false;
// Extend process mask TODO!
switch (value) {
case 1:
case 2:
current.process.upgrade(value);
break;
}
break;
case '?':
obj=Comp.obj.copy(current.process.resources);
Comp.obj.extend(obj,{
SCHED: current.process.resources.SCHED||options.TIMESCHED,
CPU: current.process.resources.CPU||options.TIMEPOOL,
MEM: current.process.resources.MEM||options.MEMPOOL,
TS: current.process.resources.TS||options.TSPOOL,
AGENT: current.process.resources.AGENT||options.AGENTPOOL,
});
return obj;
break;
default: return false;
}
return true;
}
/** Event callback management
*
*/
function off(event) {
// TODO: care of function chains??
events[event]=undefined;
}
function on(event,fun) {
if (events[event]) {
// Implement callback function chain
var funorig=events[event];
events[event]=function () {
funorig.apply(this,arguments);
fun.apply(this,arguments);
};
} else
events[event]=fun;
}
function out(str) {log(str)};
/** Get current resource allocation of process memory
*
*/
function resource(r0) {
var r;
if (!options.useproc) return 0;
// Time expensive operation: requires system call and a lot of internal computation
r=process.memoryUsage();
// console.log(r)
if (r0==undefined)
return {r:r.rss-r.heapTotal,h:r.heapUsed};
else return int((Math.max(0,r.rss-r.heapTotal-r0.r)+Math.max(0,r.heapUsed-r0.h))/1024);
}
/** Scheduling function for one agent process.
*
* Scheduling order:
* 1. Process Blocks (process.block, passed to global DOS scheduler)
* 2. Signals (process.signals, handled by AIOS scheduler)
* 3. Transition (process.transition==true, handled by AIOS scheduler)
* 4. Agent Blocks (process.schedule, handled by AIOS scheduler)
* 5. Activity (handled by AIOS scheduler)
*
*/
var SA = {
NOOP:0,
BLOCK:1,
NORES:2,
SIG:3,
TRANS:4,
SCHED:5,
ACT:6,
print: function (op) {
switch (op) {
case SA.NOOP: return 'NOOP';
case SA.BLOCK: return 'BLOCK';
case SA.NORES: return 'NORES';
case SA.SIG: return 'SIG';
case SA.TRANS: return 'TRANS';
case SA.SCHED: return 'SCHED';
case SA.ACT: return 'ACT';
}
}
}
// One scheduler run
function schedule(process) {
var exec,sig,start,delta,next,
_current,
node=current.node,
agent=process.agent,
action='',
op=SA.NOOP,
handled,
exception,
curtime,
r0;
ticks++; // move to scheduler ???
// console.log(process);
assert((process.agent!=undefined && process.id=='agent')||('Aios.schedule: not an agent process: '+process.id));
/* Order of operation selection:
**
** -1: Lifetime check
** 0. Process (internal) block scheduling [block]
** 1. Resource exception handling
** 2. Signal handling [signals]
** - Signals only handled if process priority < HIGH
** - Signal handling increase proecss priority to enable act scheduling!
** 3. Transition execution
** 4. Agent schedule block execution [schedule]
** 5. Next activity execution
*/
curtime = time();
if (!options.nolimits && !process.kill &&
(process.resources.start+(process.resources.LIFE||options.LIFETIME))<
(curtime-current.world.lag)) op=SA.NORES;
else if (process.blocked ||
(process.suspended==true && process.block.length==0 && process.signals.length==0) ||
process.dead==true ||
(agent.next==none && process.signals.length==0 && process.schedule.length == 0)) op=SA.NOOP;
// if (process.suspended==true && process.schedule.length==0 && process.signals.length==0) op=SA.NOOP;
else if (!process.blocked && process.block.length > 0) op=SA.BLOCK;
else if (!options.nolimits &&
(process.resources.consumed>(process.resources.CPU||options.TIMEPOOL) ||
process.resources.memory>(process.resources.MEM||options.MEMPOOL)
))
op=SA.NORES;
else if (process.priority<Proc.PRIO.HIGH && process.signals.length>0) op=SA.SIG;
else if (!process.suspended && process.transition) op=SA.TRANS;
else if (!process.suspended && process.schedule.length > 0) op=SA.SCHED;
else if (!process.suspended) op=SA.ACT;
else if (process.signals.length>0) op=SA.SIG;
if (options.verbose>3) print('[SCH] '+time()+' '+process.agent.id+' : '+
SA.print(op)+' [susp='+process.suspended+
',trans='+process.transition+',tmo='+process.timeout+']');
if (op==SA.NOOP) return 0;
start=curtime;
if (Aios.watchdog) Aios.watchdog.start(process.resources.SCHED||options.TIMESCHED);
else if (!options.nolimits)
process.runtime=start-current.world.lag+(process.resources.SCHED||options.TIMESCHED);
if (!options.nolimits)
r0=resource(); // Start resource monitor
current.process=process;
current.error=none;
if (current.scheduler) _current=current.scheduler.SetCurrent(process);
try {
switch (op) {
case SA.BLOCK:
// An internal schedule block [Linear/Loop]
// Pass to global scheduler
// console.log(process.block)
schedule_block(process);
break;
case SA.NORES:
throw 'EOL';
break;
case SA.SIG:
/* Execute a signal handler
** 1. A signal handler can wakeup a suspended agent process by calling wakeup()
** 2. A signal handler can wakeup a suspended agent process by modifying variables and satisfying the current
** transition condition resulting in an activity transition!
*/
if (!process.suspended && !process.transition) process.priority++;
// Pending activity execution -> block signal handling temporarily
action='signal';
sig=Comp.array.pop(process.signals);
try {
// sig=[signal,argument?,from?]
agent.on[sig[0]].call(agent,sig[1],sig[2],sig[3]);
if (process.suspended && process.transition) process.suspended=false; // ==> 2.)
} catch(e) {
if (!agent.on[sig[0]])
logAIOS ('Signal handler '+sig[0]+' in agent '+agent.id+' ['+agent.ac+'] not defined, ignoring signal.');
else
logAIOS ('Signal handler '+sig[0]+' in agent '+agent.id+' ['+agent.ac+'] failed: '+e+
(current.error?' / '+current.error:'')+', in: \n'+Code.print(agent.on[sig[0]])+
+errorLocation(process,e))
current.error=none;
process.kill=true; // Always?
};
Aios.emit('signal+',process,node,sig[0],sig[1],sig[2]);
break;
case SA.TRANS:
// Pending next computation: Compute next transition after wakeup or after a signal was handled.
// If still not successfull, suspend agent process.
try {
action='transition';
if (!agent.trans[agent.next]) throw "NOTDEFINED";
next=(typeof agent.trans[agent.next] == 'function')?
agent.trans[agent.next].call(agent):
agent.trans[agent.next];
// TODO: check blocking state - transitions may not block!
if (next) {
agent.next=next;
process.suspended=false;
process.transition=false;
} else {
process.suspended=true;
}
} catch (e) {
if (agent.trans[agent.next]==undefined)
logAIOS ('Transition table entry '+agent.next+' not defined in agent '+agent.id+' ['+agent.ac+'].');
else
logAIOS ('Agent '+agent.id+' ['+agent.ac+'] in transition '+agent.next+
' failed:\n'+e+(current.error?' / '+current.error:'')+
+errorLocation(process,e));
process.kill=true;
current.error=none;
}
break;
case SA.SCHED:
// An agent schedule block function [Linear/Loop] executed in agent context
action='block';
exec = Comp.array.pop(process.schedule);
Aios.watchdog&&Aios.watchdog.protect?Aios.watchdog.protect(exec.bind(agent)):exec.call(agent);
if (!process.kill && !process.suspended && process.schedule.length == 0) {
if (process.notransition) {
// prevent transition after this process.schedule was executed.
process.notransition=false;
} else {
// next=agent.trans[agent.next].call(agent);
next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent):agent.trans[agent.next];
if (!next) process.suspend(0,true); // no current transition enabled; suspend process
else agent.next=next;
}
}
break;
case SA.ACT:
// Normal activity execution
// console.log('[SCH] next:'+agent.next)
if (process.priority==Proc.PRIO.HIGH) process.priority--;
action='activity';
if (agent.next==none) throw 'KILL';
Aios.watchdog&&Aios.watchdog.protect?
Aios.watchdog.protect(agent.act[agent.next].bind(agent)):
agent.act[agent.next].call(agent);
if (!process.kill && !process.suspended && process.schedule.length == 0) {
action='transition';
// next=agent.trans[agent.next].call(agent);
if (!agent.trans[agent.next]) throw "NOTDEFINED";
next=(typeof agent.trans[agent.next] == 'function')?
agent.trans[agent.next].call(agent):
agent.trans[agent.next];
// TODO: check blocking state - transitions may not block!
if (!next) process.suspend(0,true); // no current transition enabled; suspend process
else agent.next=next;
}
break;
}
} catch (e) {
if (Aios.watchdog) Aios.watchdog.stop();
curtime=time()-current.world.lag;
exception=true;
switch (e) {
case 'SCHEDULE':
case 'WATCHDOG':
e='SCHEDULE';
if (Aios.watchdog) Aios.watchdog.start(options.TIMESCHED/10);
else process.runtime=curtime+options.TIMESCHED/10;
handleException(process,'error',e,options.TIMESCHED,agent.next);
break;
case 'EOL':
if (Aios.watchdog) Aios.watchdog.start(options.TIMESCHED/10); else
process.runtime=curtime+options.TIMESCHED/10;
// New time or memory contingent must be negotiated based on policy!
if (process.resources.consumed>=(process.resources.CPU||options.TIMEPOOL)) {
handleException(process,'error','CPU',e,process.resources.consumed,agent.next);
if (process.resources.consumed>=(process.resources.CPU||options.TIMEPOOL))
process.kill=true;
} else if (process.resources.memory>=(process.resources.MEM||options.MEMPOOL)) {
handleException(process,'error','EOM',process.resources.memory,agent.next);
if (process.resources.memory>=(process.resources.MEM||options.MEMPOOL))
process.kill=true;
} else if (process.resources.tuples>=(process.resources.TS||options.TSPOOL)) {
handleException(process,'error','EOT',process.resources.memory,agent.next);
if (process.resources.tuples>=(process.resources.TS||options.TSPOOL))
process.kill=true;
} else if ((process.resources.start+(process.resources.LIFE||options.LIFETIME))
<curtime) {
handleException(process,'error','EOL',process.resources.memory,agent.next);
if ((process.resources.start+(process.resources.LIFE||options.LIFETIME))
<curtime)
process.kill=true;
} else {
// TODO generic resource overflow?
handleException(process,'error','EOR',0,agent.next);
process.kill=true;
}
break;
case 'KILL':
if (Aios.watchdog) Aios.watchdog.start(options.TIMESCHED/10);
else process.runtime=curtime+options.TIMESCHED/10;
handleException(process,'exit');
process.kill=true;
break;
case 'NOTDEFINED':
if (agent.act[agent.next]==undefined && options.verbose)
logAIOS('Activity '+agent.next+' not defined in agent '+
agent.id+' ['+agent.ac+'].');
else if (agent.trans[agent.next]==undefined && options.verbose)
logAIOS('Transition table entry '+agent.next+' not defined in agent '+agent.id+' ['+agent.ac+'].');
process.kill=true;
current.error=none;
break;
default:
handled=handleException(process,aiosExceptions.indexOf(e.toString())!=-1?e:'error',e,current.error,agent.next);
if (!handled && options.verbose)
logAIOS ('Agent '+agent.id+' ['+agent.ac+'] in '+(action=='block'?'block in':action)+' '+
(action=='signal'?sig[0]:agent.next)+
' failed: Error '+e+(current.error?('; '+current.error):'')+
(options.verbose>1?(
', in code: \n'+(
action=='activity'?Code.print(agent.act[agent.next]):
(action=='transition'?Code.print(agent.trans[agent.next]):
(agent.on && sig && agent.on[sig[0]])?Code.print(agent.on[sig[0]]):'none')
)+
errorLocation(process,e)
):'')
);
if (options.verbose>2 && ['CREATE','MOVE','SIGNAL'].indexOf(e) == -1) Io.printstack(e);
if (!handled) process.kill=true;
else {
action='transition';
// next=agent.trans[agent.next].call(agent);
if (!agent.trans[agent.next]) throw "NOTDEFINED";
next=(typeof agent.trans[agent.next] == 'function')?
agent.trans[agent.next].call(agent):
agent.trans[agent.next];
// TODO: check blocking state - transitions may not block!
if (!next) process.suspend(0,true); // no current transition enabled; suspend process
else agent.next=next;
}
current.error=none;
}
}
if (Aios.watchdog) Aios.watchdog.stop();
else process.runtime=0;
if (!options.nostats) {
delta=(time()-start)||options.MINCOST;
process.resources.consumed += delta;
process.resources.memory += resource(r0);
current.node.stats.cpu += delta;
}
if (options.verbose && exception && process.kill) logAIOS('Killed agent '+agent.id);
if (current.scheduler) current.scheduler.SetCurrent(_current);
current.process=none;
if (options.verbose>3) print(time()+' <- '+process.print());
return 1;
}
/**
* Internal block scheduling
*/
function schedule_block(process) {
var next;
/*
** Process current function block sequence first!
** Format: [[fun,arg1,arg2,...],[block2], [block3], ..]
** Simplified: [fun,fun,...]
*/
if (!process.blocked) {
next = process.block[0];
process.block.splice(0,1);
/*
** Do no execute handler blocks maybe at the end of a subsection
** of the block list.
*/
while (!Comp.array.empty(process.block) && next.handler!=undefined) {
next = process.block[0];
process.block.splice(0,1);
}
if (next.handler==undefined) {
try {exec_block_fun(next)} catch(e) {
/*
** Iterate through the block list and try to find a handler entry.
*/
while (next.handler==undefined && !Comp.array.empty(process.block)) {
next = process.block[0];
process.block.splice(0,1);
}
if (next.handler!=undefined) {
/*
** Call handler ...
*/
// console.log(next.handler.toString())
try {exec_block_fun([next.handler,e])}
catch (e) {
Io.out('Aios.schedule_block [Internal B], in agent context '+
process.agent.id+', got exception in exception handler: '+e);
// Io.printstack(e);
Io.out(Json.stringify(next).replace(/\\n/g,'\n'));
};
} else {
logAIOS ('Agent '+process.agent.id+' ['+process.agent.ac+'] in activity '+
process.agent.next+
' failed:\n'+e+(current.error?' / '+current.error:', in: \n'+
Code.print(process.agent.act[process.agent.next]))+
'');// '\nat:\n'+Io.sprintstack(e)));
process.kill=true;
current.error=none;
}
}
}
}
}
var scheduled=0;
/** Main scheduler entry.
* Returns the next event time (absolute time!), negative number of scheduled agent processes, or zero
* if there is nothing to schedule.
* If result is negative, the scheduler should be executed immediately again because there
* can be pending agent signals created in the current run.
*/
function scheduler(services) {
var run=1,nexttime=0,n=0,curtime,process,env,node,pro,
timeout=time()+options.RUNTIME;
scheduled=0;
while (run && (iterations==0 || n<iterations) && time()<timeout) {
run=0; n++;
if (services) services();
nexttime=options.IDLETIME?Sig.agent.timeout(options.IDLETIME):0;
for (env in current.world.nodes) {
node=current.world.nodes[env];
if (!node) continue;
current.node=node;
curtime=time()-current.world.lag;
// 1. Timer management
if (node.timers.length>0) {
remove=false;
// 1.1. Check timers and execute runnable signaled agents
Comp.array.iter(node.timers, function(timer,i) {
if (timer && timer[1]<=curtime) {
var process=timer[0],
agent=process.agent,
// Save original process state
suspended=process.suspended,
timeout=process.timeout;
// process.suspeneded=false; ?? Signal handler can be executed even with blocked process
process.signals.push([timer[2],timer[3],agent.id]);
// TODO: A wakeup call in the signal handler re-enters schedule() !!!
run += schedule(process);
curtime=time()-current.world.lag;
if (timer[4]>0) {
// repeat
timer[1] = curtime + timer[4];
} else {
remove=true;
node.timers[i]=undefined;
}
// Restore original process state
//process.suspended=suspended; ??
process.timeout=timeout;
} else if (timer) nexttime=min0(nexttime,timer[1]);
});
// 1.2. Timer destruction
if (remove)
node.timers=
Comp.array.filter(node.timers,function (timer) {
return timer!=undefined;
});
}
curtime=time()-current.world.lag;
// Node service management (caches, TS)
node.service(curtime);
// 3. Agent process management
for (pro in node.processes.table) {
if (node.processes.table[pro]) {
// 2.1 Agent execution
curtime=time()-current.world.lag;
process=node.processes.table[pro];
// Io.out('scheduler: checking '+process.agent.id+': '+process.suspended+' '+process.timeout);
if (process.suspended && process.timeout && process.timeout<=curtime) {
// Io.out('scheduler: waking up '+process.agent.id);
process.wakeup();
}
run += schedule(process);
// 2.2 Agent destruction
if (node.processes.table[pro] && node.processes.table[pro].kill)
node.unregister(node.processes.table[pro]);
if (node.processes.table[pro] && process.suspended && process.timeout>0)
nexttime=min0(nexttime,process.timeout);
}
}
}
scheduled += run;
}
if (scheduled>0) return -scheduled;
else if (nexttime>0) return nexttime;
else return 0;
}
/*
** The time function can be changed, e.g., by simulators handling simulation
** steps instead of real time. Can be changed with Aios.config({time:fun}),
** updating all Aios/aiosX references and CP as well.
*/
var time = function () {return Math.ceil(Date.now())};
var Aios = {
aidgen:aidgen,
aios:aios1,
aios0:aios0,
aios1:aios1,
aios2:aios2,
aios3:aios3,
aiosEvents:aiosEvents,
Amp:Amp,
callback:undefined,
clock:clock,
collect:Ts.agent.collect,
// External API: Change AIOS settings only using config!
config:config,
configGet:configGet,
current:current,
emit:emit, // Emit event
err: function (msg) {if (options.verbose) log('Error: '+msg)},
fork:fork,
kill:kill,
killOn:killOn,
lock:lock,
loop:loop,
log:log, // Generic AIOS logging function
logAgent:logAgent, // Agent message logging (with details about current)
logAIOS:logAIOS, // AIOS logging function related with agent proecssing (with details about current)
logAIOSasync:logAIOSasync, // AIOS logging function related with agent proecssing (with details about current)
logAsync:logAsync, // AIOS logging function related with agent proecssing (with details about current) async
off:off, // Remove event handler
on:on, // Add event handler
options:options,
print:Io.out, // Low-level print IO function for agent messages via Aios.aiosX.log and internal Aios.log;
// OR if printAgent is set only AIOS internal messages; can be modified by host app
printAgent:undefined, // Low-level print IO function for agent messages only via Aios.aiosX.log; can be modified by host app
printAsync:undefined, // Low-level print IO function for async callback messages by host app
schedule:schedule,
scheduled:scheduled,
scheduler:scheduler,
ticks:function (v) { if (v!=undefined) ticks=v; else return ticks},
time:time,
timeout:function (tmo) { return tmo>0?Aios.time()-current.world.lag+tmo:0 }, // Compute absolute time from relative timeout
Chan:Chan,
Code:Code,
Json:Json,
Mobi:Mobi,
Name:Name,
Node:Node,
Proc:Proc,
Sec:Sec,
Sig:Sig,
Simu:Simu,
Ts:Ts,
World:World,
CB:CB,
CP:CP,
RT:RT,
B:B,
DIR:Mobi.DIR,
I:I,
L:L,
warn: function (msg) {if (options.verbose>1) log('Warning: '+msg)},
watchdog: undefined,
version : options.version
}
// Builtin watchdog support by JS VM platform?
if (watchdog && watchdog.start) Aios.watchdog=watchdog;
if (watchdog && watchdog.init) watchdog.init('WATCHDOG');
if (watchdog && watchdog.checkPoint) {
// only partial watchdog support by platform
aios0.CP=watchdog.checkPoint;
aios1.CP=watchdog.checkPoint;
aios2.CP=watchdog.checkPoint;
aios3.CP=watchdog.checkPoint;
Aios.CP=watchdog.checkPoint;
}
Conf.current(Aios);
Code.current(Aios);
Sig.current(Aios);
Sec.current(Aios);
Ts.current(Aios);
Proc.current(Aios);
Node.current(Aios);
World.current(Aios);
Mobi.current(Aios);
if (Simu) Simu.current(Aios);
Chan.current(Aios);
Json.current(Aios);
module.exports = Aios;
};
BundleModuleCode['jam/conf']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 15-1-16 by sbosse.
** $RCS: $Id: conf.js,v 1.2 2017/05/23 07:00:43 sbosse Exp $
** $VERSION: 1.3.1
**
** $INFO:
**
** JavaScript AIOS Agent Reconfiguration Sub-System
**
** $ENDOFINFO
*/
var Json = Require('jam/jsonfn');
var Comp = Require('com/compat');
var current=none;
var Aios = none;
var act = {
add: function (act,code) {
if (typeof code == 'function') current.process.agent.act[act]=code;
else if (typeof code == 'string') {
with(current.process.mask) {
current.process.agent.act[act]=eval(code);
}
}
// Add the new activity to the mask environment of the agent for further referencing.
current.process.mask[act]=act;
},
// return deleted activity(ies)
delete: function (act) {
if(Comp.obj.isArray(act)) return Comp.array.map(act,function (a) {
var f=current.process.agent.act[a];
current.process.agent.act[a]=undefined;
return f;
});
else {
var f = current.process.agent.act[act];
current.process.agent.act[act]=undefined;
return f;
}
},
update: function (act,code) {
if (typeof code == 'function') current.process.agent.act[act]=code;
else if (typeof code == 'string') current.process.agent.act[act]=Json.parse(code,current.process.mask);
}
};
var __eval=eval;
function sandBoxCode(mask,code) {
var __code;
with (mask) {
__code=__eval('__code='+code);
}
// console.log(typeof __code);
return __code;
}
var trans = {
add: function (from,cond,data) {
if (current.process.agent.trans[from]) {
function wrap(s) { return s[0]=='"'||s[0]=="'"?s:'"'+s+'"' };
var old = current.process.agent.trans[from];
if (typeof old != 'function') old='function () { return'+wrap(old)+'}';
else old = old.toString();
if (typeof cond != 'function') cond='function () { return'+wrap(cond)+'}';
else cond = cond.toString();
if (data) data=JSON.stringify(data);
else data='null';
var merged = 'function () { var next = ('+cond+').call(this,'+data+'); if (next) return next; else return ('+old+').call(this) }';
// console.log(merged);
var code = sandBoxCode(current.process.mask,merged);
current.process.agent.trans[from]=code;
// console.log(typeof current.process.agent.trans[from])
} else current.process.agent.trans[from]=cond;
},
// return deleted transaction(s)
delete: function (trans) {
if(Comp.obj.isArray(trans)) Comp.array.iter(trans,function (t) {
var f = current.process.agent.trans[t];
current.process.agent.trans[t]=undefined;
return f
});
else {
var f = current.process.agent.trans[trans];
current.process.agent.trans[trans]=undefined;
return f;
}
},
update: function (from,cond) {
current.process.agent.trans[from]=cond;
}
}
module.exports = {
agent:{
act:act,
trans:trans
},
current:function (module) { current=module.current; Aios=module; }
}
};
BundleModuleCode['jam/code']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 15-1-16 by sbosse.
** $RCS: $Id: code.js,v 1.4 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.12.1
**
** $INFO:
**
** JavaScript AIOS Agent Code Management Sub-System
**
** New: Correct soft code minifier with char state machine (CSM) if there is no builtin minifier
** New: Check pointing (CP) can be replaced with JS VM watchdog timer.
** New: Fast sandboxed constructor
** New: Dual mode JSON+/JSOB (with auto detection in toCode)
** New: Dirty fastcopy process copy (JS object copy)
** New: Function toString with minification (if supported by platform)
**
** JSOB: Simplified and compact textual representation of JS object including function code
** (Aios.options.json==false)
**
** $ENDOFINFO
*/
var options = {
debug:{},
compactit : false, // no default compaction, only useful with Aios.options.json==true
version : '1.12.1'
}
// Fallback minifier with char state machine
var minify = Require('com/minify');
var Esprima = Require('parser/esprima');
try {
// Use built-in JS code minimizer if available
var M = process.binding('minify');
Function.prototype._toString=Function.prototype.toString;
Function.prototype.toString = function (compact) {
return compact?M.minify(this._toString()):this._toString();
}
options.minifier=true;
options.minifyC=true;
minify=M.minify;
} catch (e) {
Function.prototype._toString=Function.prototype.toString;
Function.prototype.toString = function (compact) {
return compact?minify(this._toString()):this._toString();
}
options.minifier=true;
};
var Io = Require('com/io');
var Json = Require('jam/jsonfn');
var Comp = Require('com/compat');
var sandbox = Require('jam/sandbox')();
var current=none;
var Aios = none;
var util = Require('util');
/* Test if Json.stringify returns compacted code, otherwise text must be compacted here */
function _testac (p1,p2) {
/* comment */
this.x = p1;
this.y = p2;
this.z = 0;
this.act = {
init: function () {
/* comment */
this.z=this.x;
this.x++;
}
}
}
var _testobj = new _testac(1,2);
options.compactit=Json.stringify(_testobj).length>93;
var inject = {cp:undefined,rt:undefined};
// options.compactit=false;
/** Construct an agent object with given arguments (array)
*
*/
function construct(constructor,argArray) {
var inst = Object.create(constructor.prototype);
constructor.apply(inst, argArray);
return inst;
}
/** Fast dirty copy (fork): Return a fresh copy of the agent object (i.e., process.agent, instead using ofCode/toCode transf.)
* attached to a new process object.
* All function and AIOS references will be copied as is. The AIOS level cannot be changed. The mask of the
* parent process is now valid for the copied process, too. Any changes in the parent environment effects the child
* process, too, and vice versa.
*
*/
function copyProcess(process) {
var _process,_agent,agent=process.agent,mask=process.mask;
process.node.stats.fastcopy++;
agent.process={};
for (var p in process) {
switch (p) {
case 'schedule':
if (process.schedule.length > 0)
agent.process[p]=process.schedule;
// keep it only if <> []
break;
case 'blocked':
if (agent.process.suspended==true)
agent.process[p]=true;
// keep it only if it is true
break;
case 'gid':
case 'pid':
break; // ?????
// case 'delta':
case 'back':
case 'dir':
// keep it
agent.process[p]=process[p];
break;
}
}
if (Comp.obj.isEmpty(agent.process)) agent['process']=undefined;
agent['self']=undefined;
_agent=Comp.obj.copy(agent);
if (!_agent.process)
_process=Aios.Proc.Proc({mask:mask,agent:_agent});
else {
_process=Aios.Proc.Proc(agent.process);
_process.init({timeout:0,schedule:[],blocked:false,mask:mask,agent:_agent});
_agent['process']=undefined;
}
agent['self']=agent;
return _process;
}
/** Return name of a class constructor function
*
*/
function className (f) {
var name=f.toString().match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/);
return name?name[1]:"unknown"
}
/** Create a sandboxed agent object process on the current node
* using either a sandboxed agent constructor object {fun,mask}
* or a generic agent class constructor function that is sandboxed here.
*
* Returns process object.
*
* type create =
* function (node:node,
* constructor:function|{fun:function,mask:{}},
* args:{}|[],level?:number,className?:string) -> process
*/
function create(constructor,args,level,className) {
return createOn(current.node,constructor,args,level,className);
}
/** Create a sandboxed agent process on a specified node
* using either a sandboxed agent constructor object {fun,mask}
* or a generic agent class constructor function that is sandboxed here.
*
*
* Returns process object.
*
* type createOn =
* function (node:node,
* constructor:function|{fun:function,mask:{}},
* args:{}|*[],level?:number,className?:string) -> process
*/
function createOn(node,constructor,args,level,className) {
if (!constructor.fun && !Comp.obj.isFunction(constructor)) {
Aios.err('Code.create: No valid constructor function specified.');
return;
}
var code,
agent0,
agent,
process,
_process;
_process=current.process;
current.process={timeout:0};
if (level==undefined) level=Aios.options.LEVEL;
try {
if (!constructor.fun)
constructor=makeSandbox(constructor,level);
if (!(args instanceof Array)) args=[args];
agent0= construct(constructor.fun,args);
if (!agent0) {
Aios.err('Code.createOn ('+className+'): Agent constructor failed.');
current.process=_process;
return null;
}
process=makeProcess(agent0,constructor.mask);
process.resources.memory=constructor.size||0;
current.process=_process;
} catch (e) {
current.process=_process;
Aios.err('Code.createOn ('+className+'): '+e);
return;
}
agent=process.agent;
if (!Comp.obj.isArray(args) && Comp.obj.isObject(args))
for (var p in args) {
if (Comp.obj.hasProperty(agent,p)) agent[p]=args[p];
}
// Test minimal structure requirements
if (!agent['next'] || !agent['trans'] || !agent['act']) {
Aios.err('Code.createOn: Missing next/trans/act attribute in agent constructor '+className);
return none; // must be defined and initialized
}
process.level=level;
agent['self']=agent;
if (className) agent['ac']=className;
node.register(process);
node.stats.create++;
return process;
}
// fork an egent from { x:this.x, .., act : {}|[], trans:{}, on:[}}
function createFromOn(node,_process,subclass,level) {
var code,process;
code = forkCode(_process,subclass);
process = toCode(code,level);
process.agent.id=Aios.aidgen();
process.init({gid:_process.pid});
node.register(process);
var agent_=process.agent;
if (!subclass.next) try {
agent_.next=(typeof agent_.trans[agent_.next] == 'function')?agent_.trans[agent_.next].call(agent_):
agent_.trans[agent_.next];
} catch (e) { /*kill agent?*/ process.kill=true; };
return process;
}
/** Create a compiled agent process in a sandbox environment from an
* agent class constructor function on the current node.
* Returns JSON+/JSOB representation of agent process snapshot and
* the newly created process.
*
*/
function createAndReturn(constructor,ac,args,level) {
if (!(args instanceof Array)) args=[args];
/*
var code = ofCode({agent:new constructor(args[0],args[1],args[2],args[3],
args[4],args[5],args[6],args[7],
args[8],args[9])},true);
*/
var process,agent,
code = ofCode({agent:construct(constructor,args)},true);
if (level==undefined) level=Aios.options.LEVEL;
process = toCode(code,level);
agent=process.agent;
agent.id=Aios.aidgen();
agent.ac=ac;
return {code:ofCode(process,false),process:process};
}
/** Fork an agent object and return JSON+/JSOB text code.
* Note: Forking discards current scheduling blocks (in contrast to migration)!!!
*
* New: subclass: { x:this.x, .., act : {}|[], trans:{}, on:[}}
* Used to subclass parent agent
*
* Returns cleaned code (w/o CP and internal AIOS properties).
*
*/
function forkCode(process,subclass) {
var code='',p;
var agent,self;
var agent;
if (!subclass) {
agent = process.agent;
self = agent.self;
// Clean up current agent process
agent['process']=undefined;
agent['self']=undefined;
} else if (typeof subclass == 'object') {
var agent = { act:{}, trans:{}, next:process.agent.next}
console.log(subclass)
for (p in subclass) {
var o = subclass[p];
switch (p) {
case 'act':
if (Comp.obj.isArray(o)) {
for(var q in o) {
var v = o[q];
if (process.agent.act[v])
agent.act[v]=process.agent.act[v];
}
} else if (Comp.obj.isObject(o)) {
for(var q in o) {
agent.act[q]=o[q];
}
}
break;
case 'trans':
// only updates here
if (Comp.obj.isObject(o)) {
for(var q in o) {
agent.trans[q]=o[q];
}
}
break;
case 'on':
agent.on={};
if (Comp.obj.isArray(o)) {
for(var q in o) {
var v = o[q];
if (process.agent.on[v])
agent.on[v]=process.agent.on[v];
}
} else if (Comp.obj.isObject(o)) {
for(var q in o) {
agent.on[q]=o[q];
}
}
break;
case 'var':
if (Comp.obj.isArray(o)) {
for(var q in o) {
var v = o[q];
agent[v]=process.agent[v];
}
}
break;
default:
agent[p]=o;
break;
}
}
// include all remaining necessary transitions
for(var p in agent.act) {
if (!agent.trans[p] && process.agent.trans[p])
agent.trans[p]=process.agent.trans[p];
}
} else throw "forkCode: invalid subclass"
code=Aios.options.json?Json.stringify(agent):toString(agent);
if (!subclass) {
// Restore current agent process
agent.process=process;
agent.self=self;
}
// Cleanup required?
// CP/RT removal
if (inject.cp || inject.rt)
code=removeInjection(code);
return code;
}
/** Convert agent object code from a process to text JSON+/JSOB.
* Returns cleaned code (w/o CP and internal AIOS properties).
* @clean: Code is already clean, no further filtering
*
*/
function ofCode(process,clean) {
var code='',p;
var agent=process.agent;
agent.process={};
for (var p in process) {
switch (p) {
case 'schedule':
if (process.schedule.length > 0)
agent.process[p]=process.schedule;
// keep it only if <> []
break;
case 'blocked':
if (agent.process.suspended==true)
agent.process[p]=true;
// keep it only if it is true
break;
case 'gid':
case 'pid':
break; // ?????
// case 'delta':
case 'back':
case 'dir':
// keep it
agent.process[p]=process[p];
break;
}
}
if (Comp.obj.isEmpty(agent.process)) agent['process']=undefined;
agent['self']=undefined;
try {
code=Aios.options.json?Json.stringify(agent):toString(agent);
} catch (e) {
throw 'Code.ofCode: Code-Text serialization failed ('+e.toString()+')';
}
if (clean && !options.compactit) return code;
/* Compaction should only be done one time on agent creation with compact=true option.
**
*/
if (!clean && (inject.cp||inject.rt))
// CP/RT removal; no or only partial watchdog support by platform
code=removeInjection(code);
if (options.compactit) code=minimize(code);
return code;
}
/** Fast copy agent process creation (virtual, migrate).
* All function and AIOS references will remain unchanged. The AIOS level cannot be changed. The mask of the
* original (died) process is now valid for the new process, too.
*/
function ofObject(agent) {
var process;
if (!agent.process)
process=Aios.Proc.Proc({mask:agent.mask,agent:agent});
else {
process=Aios.Proc.Proc(agent.process);
process.init({timeout:0,schedule:[],blocked:false,mask:agent.mask,agent:agent});
agent['process']=undefined;
}
agent['mask']=undefined;
process.node.stats.fastcopy++;
return process;
}
/** Convert agent text sources to agent code in JSOB format
*
*/
function _ofString(source,mask) {
var code;
try {
// execute script in private context
with (mask) {
eval('"use strict"; code = '+source);
}
} catch (e) { console.log(e) };
return code;
}
function ofString(source,mask) {
// arrow functions do not have a this variable
// they are bound to this in the current context!
// need a wrapper function and a temporary this object
var self={},temp;
function evalCode() {
var code;
try {
// execute script in private context
with (mask) {
eval('"use strict"; code = '+source);
}
} catch (e) { console.log(e) };
return code;
}
temp = evalCode.call(self);
// arrow functions are now bound to self,
// update self with temp. agent object first-level attributes
self=Object.assign(self,temp);
return self;
}
function parseString (source,mask) {
// deal with arrow functions correctly
// arrow functions do not have a this variable
// they are bound to this in the current context!
// need a wrapper function and a temporary this object
var self={},temp;
function evalCode() {
var code;
try {
code = Json.parse(source,mask);
} catch (e) { console.log(e) };
return code;
}
temp = evalCode.call(self);
// arrow functions are now bound to self,
// update self with temp. agent object first-level attributes
self=Object.assign(self,temp);
return self;
}
/** Create an agent process from agent object code
*
*/
function makeProcess (agent,mask) {
var process;
// Add all activities to the masked environment:
if (mask) for(var p in agent.act) {
mask[p]=p;
}
if (!agent.process)
process=Aios.Proc.Proc({mask:mask,agent:agent});
else {
process=Aios.Proc.Proc(agent.process);
process.init({timeout:0,schedule:[],blocked:false,mask:mask,agent:agent});
agent['process']=undefined;
}
agent['self']=agent;
return process;
}
/** Create a sandboxed agent class constructor object {fun,mask} from
* an agent class template constructor function providing
* a sandboxed agent constructor function and the sandbox
* mask agent environment.
* The optional environment object 'env' can contain additional references, e.g.,
* activitiy references.
*
* Note: All agents created using the constructor function share the same mask
* object!
*
* typeof constructor = function|string
* typeof sac = {fun:function, mask: {}, size:number }
*/
function makeSandbox (constructor,level,env) {
var _process,sac,aios;
switch (level) {
case 0: aios=Aios.aios0; break;
case 1: aios=Aios.aios1; break;
case 2: aios=Aios.aios2; break;
case 3: aios=Aios.aios3; break;
default: aios=Aios.aios0; break;
}
_process=current.process;
current.process={timeout:0};
sac=sandbox(constructor,aios,inject,env);
current.process=_process;
return sac;
}
/** Minimize code text
*
*/
function minimize (code) { return minify(code) }
/** Print agent code
*/
function print(agent,compact) {
var process = agent.process;
var self = agent.self;
agent['process']=undefined;
agent['self']=undefined;
var text=Aios.options.json?Json.stringify(agent):toString(agent);
agent.process=process;
agent.self=self;
if (!text) return 'undefined';
var regex4= /\\n/g;
if (inject.cp || inject.rt)
// CP/RT removal; i.e., none or only partial watchdog support by platform
text= removeInjection(text);
if (compact && options.compactit)
text=minimize(text);
return text.replace(regex4,'\n');
}
/** Remove CP/RT injections from code text
*
*/
function removeInjection(text) {
// CP removal
if (inject.cp) {
var regex1= /CP\(\);/g;
var regex2= /\(\(([^\)]+)\)\s&&\sCP\(\)\)/g;
var regex3= /,CP\(\)/g;
text=text.replace(regex1,"").replace(regex2,"($1)").replace(regex3,"");
}
// RT removal
if (inject.rt) {
var regex4= /RT\(\);/g;
text=text.replace(regex4,"");
}
return text;
}
/** Returns size of cleaned code (w/o CP and internal AIOS properties).
*
*/
function size(agent) {
var text='',p;
var process = agent.process;
var self = agent.self;
agent['process']=undefined;
agent['self']=undefined;
text=Aios.options.json?Json.stringify(agent):toString(agent);
agent.process=process;
agent.self=self;
if (inject.cp || inject.rt) {
text=removeInjection(text);
}
return text.length;
}
function test (what) {
switch (what) {
case 'minify': if (minify.test) minify.test(); break;
}
}
/** Convert JSON+/or JSOB text to an agent object process encapsulated in a sandbox (aios access only).
* Returns process container with CP injected agent code (process.agent).
*
* CP Injection (required on generic JS VM platform w/o watchdog, e.g., node.js, browser):
* 1. In all loop expressions (for/while)
* 2. In all function bodies (start)
*
* No watchdog: Aios.watchdog == undefined (nodes.js, browser)
* Full watchdog implementation: Aios.watchdog && Aios.watchdog.checkPoint==undefined (jvm)
* Partial watchdog implementation with checkPoint function: Aios.watchdog.checkPoint (jxcore)
*
*
*/
function toCode(text,level) {
var agent,
process,
p,
aios,
next;
switch (level) {
case undefined:
case 0: aios=Aios.aios0; break;
case 1: aios=Aios.aios1; break;
case 2: aios=Aios.aios2; break;
case 3: aios=Aios.aios3; break;
default: aios=Aios.aios0; break;
}
if (inject.cp) {
// CP injection; no or only partial watchdog support
var regex1= /while[\s]*\(([^\'")]+)\)/g;
var regex2= /for[\s]*\(([^\)'"]+)\)/g;
var regex3= /function([^\{'"]+)\{/g;
text=text.replace(regex1,"while (($1) && CP())")
.replace(regex2,"for ($1,CP())")
.replace(regex3,"function $1{CP();");
}
if (inject.rt) {
// RT injection
var regex4 = /catch[\s]*\([\s]*([a-zA-Z0-9_]+)[\s]*\)[\s]*\{/g;
text=text.replace(regex4,'catch ($1) {'+inject.rt+'($1);');
}
/* Set up an object to serve as the local context for the code
** being evaluated. The entire global scope must be masked out!
** Additionally, Json defines a variable current, which must be
** masked, too.
*/
var mask = {current:undefined,require:undefined};
// mask local properties
for (p in this)
mask[p] = undefined;
// mask global properties
for (p in global)
mask[p] = undefined;
// add sandbox content
for (p in aios) {
mask[p]=aios[p];
}
// Auto detect JSON+ / JSOB format
var isjson=Comp.string.startsWith(text,'{"');
try {agent=isjson?parseString(text,mask):ofString(text,mask);}
catch (e) {
if (Aios.options.verbose) Aios.log('Aios.code.toCode: '+e+(current.error?(',\nin: '+current.error):''));
return null;
}
if (!agent) {
var more;
try {
var ast = Esprima.parse('code='+text, { tolerant: true, loc:true });
if (ast.errors && ast.errors.length>0) console.log(ast.errors[0]);
} catch (e) {
console.log(e);
}
__LASTCODE=text;
return Aios.log('Aios.code.toCode: Invalid agent code received (invalid source text?) See __LASTCODE');
}
// Add all activities to the masked environment:
for(var p in agent.act) {
mask[p]=p;
}
if (!agent.process)
process=Aios.Proc.Proc({mask:mask,agent:agent});
else {
process=Aios.Proc.Proc(agent.process);
process.init({timeout:0,schedule:[],blocked:false,mask:mask,agent:agent});
agent['process']=undefined;
}
process.level=level;
process.resources.memory=text.length;
agent['self']=agent;
return process;
}
/** Convert agent object (i.e., process.agent) to a snapshot object.
*
*/
function toObject(process) {
var _process,_agent,agent=process.agent,mask=process.mask;
agent.process={};
for (var p in process) {
switch (p) {
case 'schedule':
if (process.schedule.length > 0)
agent.process[p]=process.schedule;
// keep it only if <> []
break;
case 'blocked':
if (agent.process.suspended==true)
agent.process[p]=true;
// keep it only if it is true
break;
case 'gid':
case 'pid':
break; // ?????
// case 'delta':
case 'back':
case 'dir':
// keep it
agent.process[p]=process[p];
break;
}
}
if (Comp.obj.isEmpty(agent.process)) agent['process']=undefined;
agent['self']=undefined;
_agent=Comp.obj.copy(agent);
_agent.mask = mask;
agent['self']=agent;
return _agent;
}
/** Convert agent object to text source in JSOB format
*
*/
function toString(o) {
var p,i,s='',sep;
if (Comp.obj.isArray(o)) {
s='[';sep='';
for(p in o) {
s=s+sep+toString(o[p]);
sep=',';
}
s+=']';
} else if (o instanceof Buffer) {
s='[';sep='';
for(i=0;i<o.length;i++) {
s=s+sep+toString(o[i]);
sep=',';
}
s+=']';
} else if (typeof o == 'object') {
s='{';sep='';
for(p in o) {
if (o[p]==undefined) continue;
s=s+sep+"'"+p+"'"+':'+toString(o[p]);
sep=',';
}
s+='}';
} else if (typeof o == 'string')
s="'"+o.toString()+"'";
else if (typeof o == 'function')
s=o.toString(true); // try minification (true) if supported by platform
else if (o != undefined)
s=o.toString();
else s='undefined';
return s;
}
module.exports = {
className:className,
copyProcess:copyProcess,
create:create,
createAndReturn:createAndReturn,
createFromOn:createFromOn,
createOn:createOn,
forkCode:forkCode,
ofCode:ofCode,
ofObject:ofObject,
ofString:ofString,
inject:inject,
Jsonf:Json,
makeProcess:makeProcess,
makeSandbox:makeSandbox,
minimize:minimize,
print:print,
size:size,
test:test,
toCode:toCode,
toObject:toObject,
toString:toString,
options:options,
current:function (module) {
current=module.current;
Aios=module;
// Set-up inject configuration
inject.cp=(Aios.watchdog && !Aios.watchdog.checkPoint)?undefined:'CP';
inject.rt=(Aios.watchdog && Aios.watchdog.protect)?undefined:'RT';
if (inject.cp||inject.rt) options.inject=inject;
if (Aios.watchdog && Aios.watchdog.protect) options.watchdog='native';
else options.watchdog=false;
}
}
};
BundleModuleCode['com/minify']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse, Douglas Crockford (jsmin, C, 2002)
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: 09-06-20 by sbosse.
** $RCS: $Id: minify.js,v 1.4 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.1.1
**
** $INFO:
**
** Fast JavaScript Code Minifier (C-to-JS port)
**
** $ENDOFINFO
*/
var EOF = null;
var theA;
var theB;
var theLookahead = EOF;
var theX = EOF;
var theY = EOF;
var theInbuf = null;
var theOutbuf = null;
var theInbufIndex = 0;
var theOutbufIndex = 0;
/* isAlphanum -- return true if the character is a letter, digit, underscore,
dollar sign, or non-ASCII character.
*/
function isAlphanum(c)
{
return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' ||
c > String.fromCharCode(126));
}
function get() {
var c = theLookahead;
theLookahead = EOF;
if (c == EOF) {
c = theInbuf[theInbufIndex++];
}
if (c >= ' ' || c == '\n' || c == EOF) {
return c;
}
if (c == '\r') {
return '\n';
}
return ' ';
}
function put(c) {
theOutbuf += c;
}
function peek()
{
theLookahead = get();
return theLookahead;
}
function error(msg) { throw msg }
function next()
{
var c = get();
if (c == '/') {
switch (peek()) {
case '/':
for (;;) {
c = get();
if (c <= '\n') {
break;
}
}
break;
case '*':
get();
while (c != ' ') {
switch (get()) {
case '*':
if (peek() == '/') {
get();
c = ' ';
}
break;
case EOF:
error("Unterminated comment.");
}
}
break;
}
}
theY = theX;
theX = c;
return c;
}
/* action -- do something! What you do is determined by the argument:
1 Output A. Copy B to A. Get the next B.
2 Copy B to A. Get the next B. (Delete A).
3 Get the next B. (Delete B).
action treats a string as a single character. Wow!
action recognizes a regular expression if it is preceded by ( or , or =.
*/
function action(d)
{
switch (d) {
case 1:
put(theA);
if (
(theY == '\n' || theY == ' ') &&
(theA == '+' || theA == '-' || theA == '*' || theA == '/') &&
(theB == '+' || theB == '-' || theB == '*' || theB == '/')
) {
put(theY);
}
case 2:
theA = theB;
if (theA == '\'' || theA == '"' || theA == '`') {
for (;;) {
put(theA);
theA = get();
if (theA == theB) {
break;
}
if (theA == '\\') {
put(theA);
theA = get();
}
if (theA == EOF) {
error("Unterminated string literal.");
}
}
}
case 3:
theB = next();
if (theB == '/' && (
theA == '(' || theA == ',' || theA == '=' || theA == ':' ||
theA == '[' || theA == '!' || theA == '&' || theA == '|' ||
theA == '?' || theA == '+' || theA == '-' || theA == '~' ||
theA == '*' || theA == '/' || theA == '{' || theA == '\n'
)) {
put(theA);
if (theA == '/' || theA == '*') {
put(' ');
}
put(theB);
for (;;) {
theA = get();
if (theA == '[') {
for (;;) {
put(theA);
theA = get();
if (theA == ']') {
break;
}
if (theA == '\\') {
put(theA);
theA = get();
}
if (theA == EOF) {
error("Unterminated set in Regular Expression literal.");
}
}
} else if (theA == '/') {
switch (peek()) {
case '/':
case '*':
error("Unterminated set in Regular Expression literal.");
break;
case '\n':
put(theA);
theA='\n'; /* Newline required after end of regex */
}
break;
} else if (theA =='\\') {
put(theA);
theA = get();
}
if (theA == EOF) {
error("Unterminated Regular Expression literal.");
}
put(theA);
}
theB = next();
}
}
}
function minify(text) {
theA=0;
theB=0;
theLookahead = EOF;
theX = EOF;
theY = EOF;
theInbuf=text;
theInbufIndex=0;
theOutbuf='';
if (peek() == 0xEF) {
get();
get();
get();
}
theA = get();
action(3);
while (theA != EOF) {
switch (theA) {
case ' ':
action(isAlphanum(theB) ? 1 : 2);
break;
case '\n':
switch (theB) {
case '{':
case '[':
case '(':
case '+':
case '-':
case '!':
case '~':
action(1);
break;
case ' ':
action(3);
break;
default:
action(isAlphanum(theB) ? 1 : 2);
}
break;
default:
switch (theB) {
case ' ':
action(isAlphanum(theA) ? 1 : 3);
break;
case '\n':
switch (theA) {
case '}':
case ']':
case ')':
case '+':
case '-':
case '"':
case '\'':
case '`':
action(1);
break;
default:
action(isAlphanum(theA) ? 1 : 3);
}
break;
default:
action(1);
break;
}
}
}
return theOutbuf;
}
// old faulty regex minimizer from Code module!!
function minimize0 (code) {
// Inline and multi-line comments
var regex4= /\/\*([\S\s]*?)\*\//g;
var regex5= /([^\\}])\\n/g;
var regex6= /\/\/[^\n]+/g;
// Newline after {},;
var regex7= /[ ]*([{},; ]|else)[ ]*\n[\n]*/g;
// Filter for string quotes
var regex8= /([^\'"]+)|([\'"](?:[^\'"\\]|\\.)+[\'"])/g;
// Multi-spaces reduction
var regex9= / [ ]+/g;
// relax } <identifier> syntax errors after newline removal; exclude keywords!
var regex10= /}\s+(?!else|finally|catch)([a-zA-Z_]+)/g;
// relax ) <identifier> syntax errors after newline removal
var regex11= /\)\s+([a-zA-Z_]+)/g;
code=code.replace(regex4,"")
.replace(regex5,'$1\n')
.replace(regex5,'$1\n')
.replace(regex6,"")
.replace(regex7,"$1")
.replace(regex8, function($0, $1, $2) {
if ($1) {
return $1.replace(regex9,' ').replace(regex10,'};$1').replace(regex11,')\n$1');
} else {
return $2;
}
});
return code;
}
// compare regex minimizer versa character state machine
function test() {
var n=1E4;
var text=minify.toString();
var t0=Date.now()
for(var i=0;i<n;i++) {
minify(text);
}
var t1=Date.now();
console.log('minify: '+((t1-t0)/n/text.length*1000)+' us/char');
var t0=Date.now()
for(var i=0;i<n;i++) {
minimize0(text);
}
var t1=Date.now();
console.log('minimize0: '+((t1-t0)/n/text.length*1000)+' us/char');
try {
var M = process.binding('minify');
var t0=Date.now()
for(var i=0;i<n;i++) {
M.minify(text);
}
var t1=Date.now();
console.log('minifyC: '+((t1-t0)/n/text.length*1000)+' us/char');
} catch (e) {
}
}
minify.test=test;
module.exports = minify;
};
BundleModuleCode['parser/esprima']=function (module,exports){
/*
Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
$Id: esprima.js,v 1.3.2 2017/06/08 15:41:11 sbosse Exp sbosse $
*/
(function (root, factory) {
'use strict';
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
// Rhino, and plain browser loading.
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports);
} else {
factory((root.esprima = {}));
}
}(this, function (exports) {
'use strict';
var Token,
TokenName,
FnExprTokens,
Syntax,
PlaceHolders,
Messages,
Regex,
source,
strict,
index,
lineNumber,
lineStart,
hasLineTerminator,
lastIndex,
lastLineNumber,
lastLineStart,
startIndex,
startLineNumber,
startLineStart,
scanning,
length,
lookahead,
state,
extra,
isBindingElement,
isAssignmentTarget,
firstCoverInitializedNameError;
Token = {
BooleanLiteral: 1,
EOF: 2,
Identifier: 3,
Keyword: 4,
NullLiteral: 5,
NumericLiteral: 6,
Punctuator: 7,
StringLiteral: 8,
RegularExpression: 9,
Template: 10
};
TokenName = {};
TokenName[Token.BooleanLiteral] = 'Boolean';
TokenName[Token.EOF] = '<end>';
TokenName[Token.Identifier] = 'Identifier';
TokenName[Token.Keyword] = 'Keyword';
TokenName[Token.NullLiteral] = 'Null';
TokenName[Token.NumericLiteral] = 'Numeric';
TokenName[Token.Punctuator] = 'Punctuator';
TokenName[Token.StringLiteral] = 'String';
TokenName[Token.RegularExpression] = 'RegularExpression';
TokenName[Token.Template] = 'Template';
// A function following one of those tokens is an expression.
FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new',
'return', 'case', 'delete', 'throw', 'void',
// assignment operators
'=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=',
'&=', '|=', '^=', ',',
// binary/unary operators
'+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&',
'|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=',
'<=', '<', '>', '!=', '!=='];
Syntax = {
AssignmentExpression: 'AssignmentExpression',
AssignmentPattern: 'AssignmentPattern',
ArrayExpression: 'ArrayExpression',
ArrayPattern: 'ArrayPattern',
ArrowFunctionExpression: 'ArrowFunctionExpression',
BlockStatement: 'BlockStatement',
BinaryExpression: 'BinaryExpression',
BreakStatement: 'BreakStatement',
CallExpression: 'CallExpression',
CatchClause: 'CatchClause',
ClassBody: 'ClassBody',
ClassDeclaration: 'ClassDeclaration',
ClassExpression: 'ClassExpression',
ConditionalExpression: 'ConditionalExpression',
ContinueStatement: 'ContinueStatement',
DoWhileStatement: 'DoWhileStatement',
DebuggerStatement: 'DebuggerStatement',
EmptyStatement: 'EmptyStatement',
ExportAllDeclaration: 'ExportAllDeclaration',
ExportDefaultDeclaration: 'ExportDefaultDeclaration',
ExportNamedDeclaration: 'ExportNamedDeclaration',
ExportSpecifier: 'ExportSpecifier',
ExpressionStatement: 'ExpressionStatement',
ForStatement: 'ForStatement',
ForOfStatement: 'ForOfStatement',
ForInStatement: 'ForInStatement',
FunctionDeclaration: 'FunctionDeclaration',
FunctionExpression: 'FunctionExpression',
Identifier: 'Identifier',
IfStatement: 'IfStatement',
ImportDeclaration: 'ImportDeclaration',
ImportDefaultSpecifier: 'ImportDefaultSpecifier',
ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
ImportSpecifier: 'ImportSpecifier',
Literal: 'Literal',
LabeledStatement: 'LabeledStatement',
LogicalExpression: 'LogicalExpression',
MemberExpression: 'MemberExpression',
MetaProperty: 'MetaProperty',
MethodDefinition: 'MethodDefinition',
NewExpression: 'NewExpression',
ObjectExpression: 'ObjectExpression',
ObjectPattern: 'ObjectPattern',
Program: 'Program',
Property: 'Property',
RestElement: 'RestElement',
ReturnStatement: 'ReturnStatement',
SequenceExpression: 'SequenceExpression',
SpreadElement: 'SpreadElement',
Super: 'Super',
SwitchCase: 'SwitchCase',
SwitchStatement: 'SwitchStatement',
TaggedTemplateExpression: 'TaggedTemplateExpression',
TemplateElement: 'TemplateElement',
TemplateLiteral: 'TemplateLiteral',
ThisExpression: 'ThisExpression',
ThrowStatement: 'ThrowStatement',
TryStatement: 'TryStatement',
UnaryExpression: 'UnaryExpression',
UpdateExpression: 'UpdateExpression',
VariableDeclaration: 'VariableDeclaration',
VariableDeclarator: 'VariableDeclarator',
WhileStatement: 'WhileStatement',
WithStatement: 'WithStatement',
YieldExpression: 'YieldExpression'
};
PlaceHolders = {
ArrowParameterPlaceHolder: 'ArrowParameterPlaceHolder'
};
// Error messages should be identical to V8.
Messages = {
UnexpectedToken: 'Unexpected token %0',
UnexpectedNumber: 'Unexpected number',
UnexpectedString: 'Unexpected string',
UnexpectedIdentifier: 'Unexpected identifier',
UnexpectedReserved: 'Unexpected reserved word',
UnexpectedTemplate: 'Unexpected quasi %0',
UnexpectedEOS: 'Unexpected end of input',
NewlineAfterThrow: 'Illegal newline after throw',
InvalidRegExp: 'Invalid regular expression',
UnterminatedRegExp: 'Invalid regular expression: missing /',
InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
InvalidLHSInForIn: 'Invalid left-hand side in for-in',
InvalidLHSInForLoop: 'Invalid left-hand side in for-loop',
MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
NoCatchOrFinally: 'Missing catch or finally after try',
UnknownLabel: 'Undefined label \'%0\'',
Redeclaration: '%0 \'%1\' has already been declared',
IllegalContinue: 'Illegal continue statement',
IllegalBreak: 'Illegal break statement',
IllegalReturn: 'Illegal return statement',
StrictModeWith: 'Strict mode code may not include a with statement',
StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
StrictVarName: 'Variable name may not be eval or arguments in strict mode',
StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
StrictDelete: 'Delete of an unqualified identifier in strict mode.',
StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
StrictReservedWord: 'Use of future reserved word in strict mode',
TemplateOctalLiteral: 'Octal literals are not allowed in template strings.',
ParameterAfterRestParameter: 'Rest parameter must be last formal parameter',
DefaultRestParameter: 'Unexpected token =',
ObjectPatternAsRestParameter: 'Unexpected token {',
DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals',
ConstructorSpecialMethod: 'Class constructor may not be an accessor',
DuplicateConstructor: 'A class may only have one constructor',
StaticPrototype: 'Classes may not have static property named prototype',
MissingFromClause: 'Unexpected token',
NoAsAfterImportNamespace: 'Unexpected token',
InvalidModuleSpecifier: 'Unexpected token',
IllegalImportDeclaration: 'Unexpected token',
IllegalExportDeclaration: 'Unexpected token',
DuplicateBinding: 'Duplicate binding %0'
};
// See also tools/generate-unicode-regex.js.
Regex = {
// ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierStart:
NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B2\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDE00-\uDE11\uDE13-\uDE2B\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDE00-\uDE2F\uDE44\uDE80-\uDEAA]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]/,
// ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierPart:
NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B2\u08E4-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA69D\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2D\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDD0-\uDDDA\uDE00-\uDE11\uDE13-\uDE37\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF01-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF98]|\uD809[\uDC00-\uDC6E]|[\uD80C\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/
};
// Ensure the condition is true, otherwise throw an error.
// This is only to have a better contract semantic, i.e. another safety net
// to catch a logic error. The condition shall be fulfilled in normal case.
// Do NOT use this to enforce a certain condition on any user input.
function assert(condition, message) {
/* istanbul ignore if */
if (!condition) {
throw new Error('ASSERT: ' + message);
}
}
function isDecimalDigit(ch) {
return (ch >= 0x30 && ch <= 0x39); // 0..9
}
function isHexDigit(ch) {
return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
}
function isOctalDigit(ch) {
return '01234567'.indexOf(ch) >= 0;
}
function octalToDecimal(ch) {
// \0 is not octal escape sequence
var octal = (ch !== '0'), code = '01234567'.indexOf(ch);
if (index < length && isOctalDigit(source[index])) {
octal = true;
code = code * 8 + '01234567'.indexOf(source[index++]);
// 3 digits are only allowed when string starts
// with 0, 1, 2, 3
if ('0123'.indexOf(ch) >= 0 &&
index < length &&
isOctalDigit(source[index])) {
code = code * 8 + '01234567'.indexOf(source[index++]);
}
}
return {
code: code,
octal: octal
};
}
// ECMA-262 11.2 White Space
function isWhiteSpace(ch) {
return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
(ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0);
}
// ECMA-262 11.3 Line Terminators
function isLineTerminator(ch) {
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029);
}
// ECMA-262 11.6 Identifier Names and Identifiers
function fromCodePoint(cp) {
return (cp < 0x10000) ? String.fromCharCode(cp) :
String.fromCharCode(0xD800 + ((cp - 0x10000) >> 10)) +
String.fromCharCode(0xDC00 + ((cp - 0x10000) & 1023));
}
function isIdentifierStart(ch) {
return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore)
(ch >= 0x41 && ch <= 0x5A) || // A..Z
(ch >= 0x61 && ch <= 0x7A) || // a..z
(ch === 0x5C) || // \ (backslash)
((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch)));
}
function isIdentifierPart(ch) {
return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore)
(ch >= 0x41 && ch <= 0x5A) || // A..Z
(ch >= 0x61 && ch <= 0x7A) || // a..z
(ch >= 0x30 && ch <= 0x39) || // 0..9
(ch === 0x5C) || // \ (backslash)
((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch)));
}
// ECMA-262 11.6.2.2 Future Reserved Words
function isFutureReservedWord(id) {
switch (id) {
case 'enum':
case 'export':
case 'import':
case 'super':
return true;
default:
return false;
}
}
function isStrictModeReservedWord(id) {
switch (id) {
case 'implements':
case 'interface':
case 'package':
case 'private':
case 'protected':
case 'public':
case 'static':
case 'yield':
case 'let':
return true;
default:
return false;
}
}
function isRestrictedWord(id) {
return id === 'eval' || id === 'arguments';
}
// ECMA-262 11.6.2.1 Keywords
function isKeyword(id) {
switch (id.length) {
case 2:
return (id === 'if') || (id === 'in') || (id === 'do');
case 3:
return (id === 'var') || (id === 'for') || (id === 'new') ||
(id === 'try') || (id === 'let');
case 4:
return (id === 'this') || (id === 'else') || (id === 'case') ||
(id === 'void') || (id === 'with') || (id === 'enum');
case 5:
return (id === 'while') || (id === 'break') || (id === 'catch') ||
(id === 'throw') || (id === 'const') || (id === 'yield') ||
(id === 'class') || (id === 'super');
case 6:
return (id === 'return') || (id === 'typeof') || (id === 'delete') ||
(id === 'switch') || (id === 'export') || (id === 'import');
case 7:
return (id === 'default') || (id === 'finally') || (id === 'extends');
case 8:
return (id === 'function') || (id === 'continue') || (id === 'debugger');
case 10:
return (id === 'instanceof');
default:
return false;
}
}
// ECMA-262 11.4 Comments
function addComment(type, value, start, end, loc) {
var comment;
assert(typeof start === 'number', 'Comment must have valid position');
state.lastCommentStart = start;
comment = {
type: type,
value: value
};
if (extra.range) {
comment.range = [start, end];
}
if (extra.loc) {
comment.loc = loc;
}
extra.comments.push(comment);
if (extra.attachComment) {
extra.leadingComments.push(comment);
extra.trailingComments.push(comment);
}
if (extra.tokenize) {
comment.type = comment.type + 'Comment';
if (extra.delegate) {
comment = extra.delegate(comment);
}
extra.tokens.push(comment);
}
}
function skipSingleLineComment(offset) {
var start, loc, ch, comment;
start = index - offset;
loc = {
start: {
line: lineNumber,
column: index - lineStart - offset
}
};
while (index < length) {
ch = source.charCodeAt(index);
++index;
if (isLineTerminator(ch)) {
hasLineTerminator = true;
if (extra.comments) {
comment = source.slice(start + offset, index - 1);
loc.end = {
line: lineNumber,
column: index - lineStart - 1
};
addComment('Line', comment, start, index - 1, loc);
}
if (ch === 13 && source.charCodeAt(index) === 10) {
++index;
}
++lineNumber;
lineStart = index;
return;
}
}
if (extra.comments) {
comment = source.slice(start + offset, index);
loc.end = {
line: lineNumber,
column: index - lineStart
};
addComment('Line', comment, start, index, loc);
}
}
function skipMultiLineComment() {
var start, loc, ch, comment;
if (extra.comments) {
start = index - 2;
loc = {
start: {
line: lineNumber,
column: index - lineStart - 2
}
};
}
while (index < length) {
ch = source.charCodeAt(index);
if (isLineTerminator(ch)) {
if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) {
++index;
}
hasLineTerminator = true;
++lineNumber;
++index;
lineStart = index;
} else if (ch === 0x2A) {
// Block comment ends with '*/'.
if (source.charCodeAt(index + 1) === 0x2F) {
++index;
++index;
if (extra.comments) {
comment = source.slice(start + 2, index - 2);
loc.end = {
line: lineNumber,
column: index - lineStart
};
addComment('Block', comment, start, index, loc);
}
return;
}
++index;
} else {
++index;
}
}
// Ran off the end of the file - the whole thing is a comment
if (extra.comments) {
loc.end = {
line: lineNumber,
column: index - lineStart
};
comment = source.slice(start + 2, index);
addComment('Block', comment, start, index, loc);
}
tolerateUnexpectedToken();
}
function skipComment() {
var ch, start;
hasLineTerminator = false;
start = (index === 0);
while (index < length) {
ch = source.charCodeAt(index);
if (isWhiteSpace(ch)) {
++index;
} else if (isLineTerminator(ch)) {
hasLineTerminator = true;
++index;
if (ch === 0x0D && source.charCodeAt(index) === 0x0A) {
++index;
}
++lineNumber;
lineStart = index;
start = true;
} else if (ch === 0x2F) { // U+002F is '/'
ch = source.charCodeAt(index + 1);
if (ch === 0x2F) {
++index;
++index;
skipSingleLineComment(2);
start = true;
} else if (ch === 0x2A) { // U+002A is '*'
++index;
++index;
skipMultiLineComment();
} else {
break;
}
} else if (start && ch === 0x2D) { // U+002D is '-'
// U+003E is '>'
if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) {
// '-->' is a single-line comment
index += 3;
skipSingleLineComment(3);
} else {
break;
}
} else if (ch === 0x3C) { // U+003C is '<'
if (source.slice(index + 1, index + 4) === '!--') {
++index; // `<`
++index; // `!`
++index; // `-`
++index; // `-`
skipSingleLineComment(4);
} else {
break;
}
} else {
break;
}
}
}
function scanHexEscape(prefix) {
var i, len, ch, code = 0;
len = (prefix === 'u') ? 4 : 2;
for (i = 0; i < len; ++i) {
if (index < length && isHexDigit(source[index])) {
ch = source[index++];
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
} else {
return '';
}
}
return String.fromCharCode(code);
}
function scanUnicodeCodePointEscape() {
var ch, code;
ch = source[index];
code = 0;
// At least, one hex digit is required.
if (ch === '}') {
throwUnexpectedToken();
}
while (index < length) {
ch = source[index++];
if (!isHexDigit(ch)) {
break;
}
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
}
if (code > 0x10FFFF || ch !== '}') {
throwUnexpectedToken();
}
return fromCodePoint(code);
}
function codePointAt(i) {
var cp, first, second;
cp = source.charCodeAt(i);
if (cp >= 0xD800 && cp <= 0xDBFF) {
second = source.charCodeAt(i + 1);
if (second >= 0xDC00 && second <= 0xDFFF) {
first = cp;
cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
}
}
return cp;
}
function getComplexIdentifier() {
var cp, ch, id;
cp = codePointAt(index);
id = fromCodePoint(cp);
index += id.length;
// '\u' (U+005C, U+0075) denotes an escaped character.
if (cp === 0x5C) {
if (source.charCodeAt(index) !== 0x75) {
throwUnexpectedToken();
}
++index;
if (source[index] === '{') {
++index;
ch = scanUnicodeCodePointEscape();
} else {
ch = scanHexEscape('u');
cp = ch.charCodeAt(0);
if (!ch || ch === '\\' || !isIdentifierStart(cp)) {
throwUnexpectedToken();
}
}
id = ch;
}
while (index < length) {
cp = codePointAt(index);
if (!isIdentifierPart(cp)) {
break;
}
ch = fromCodePoint(cp);
id += ch;
index += ch.length;
// '\u' (U+005C, U+0075) denotes an escaped character.
if (cp === 0x5C) {
id = id.substr(0, id.length - 1);
if (source.charCodeAt(index) !== 0x75) {
throwUnexpectedToken();
}
++index;
if (source[index] === '{') {
++index;
ch = scanUnicodeCodePointEscape();
} else {
ch = scanHexEscape('u');
cp = ch.charCodeAt(0);
if (!ch || ch === '\\' || !isIdentifierPart(cp)) {
throwUnexpectedToken();
}
}
id += ch;
}
}
return id;
}
function getIdentifier() {
var start, ch;
start = index++;
while (index < length) {
ch = source.charCodeAt(index);
if (ch === 0x5C) {
// Blackslash (U+005C) marks Unicode escape sequence.
index = start;
return getComplexIdentifier();
} else if (ch >= 0xD800 && ch < 0xDFFF) {
// Need to handle surrogate pairs.
index = start;
return getComplexIdentifier();
}
if (isIdentifierPart(ch)) {
++index;
} else {
break;
}
}
return source.slice(start, index);
}
function scanIdentifier() {
var start, id, type;
start = index;
// Backslash (U+005C) starts an escaped character.
id = (source.charCodeAt(index) === 0x5C) ? getComplexIdentifier() : getIdentifier();
// There is no keyword or literal with only one character.
// Thus, it must be an identifier.
if (id.length === 1) {
type = Token.Identifier;
} else if (isKeyword(id)) {
type = Token.Keyword;
} else if (id === 'null') {
type = Token.NullLiteral;
} else if (id === 'true' || id === 'false') {
type = Token.BooleanLiteral;
} else {
type = Token.Identifier;
}
return {
type: type,
value: id,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
// ECMA-262 11.7 Punctuators
function scanPunctuator() {
var token, str;
token = {
type: Token.Punctuator,
value: '',
lineNumber: lineNumber,
lineStart: lineStart,
start: index,
end: index
};
// Check for most common single-character punctuators.
str = source[index];
switch (str) {
case '(':
if (extra.tokenize) {
extra.openParenToken = extra.tokenValues.length;
}
++index;
break;
case '{':
if (extra.tokenize) {
extra.openCurlyToken = extra.tokenValues.length;
}
state.curlyStack.push('{');
++index;
break;
case '.':
++index;
if (source[index] === '.' && source[index + 1] === '.') {
// Spread operator: ...
index += 2;
str = '...';
}
break;
case '}':
++index;
state.curlyStack.pop();
break;
case ')':
case ';':
case ',':
case '[':
case ']':
case ':':
case '?':
case '~':
++index;
break;
default:
// 4-character punctuator.
str = source.substr(index, 4);
if (str === '>>>=') {
index += 4;
} else {
// 3-character punctuators.
str = str.substr(0, 3);
if (str === '===' || str === '!==' || str === '>>>' ||
str === '<<=' || str === '>>=') {
index += 3;
} else {
// 2-character punctuators.
str = str.substr(0, 2);
if (str === '&&' || str === '||' || str === '==' || str === '!=' ||
str === '+=' || str === '-=' || str === '*=' || str === '/=' ||
str === '++' || str === '--' || str === '<<' || str === '>>' ||
str === '&=' || str === '|=' || str === '^=' || str === '%=' ||
str === '<=' || str === '>=' || str === '=>') {
index += 2;
} else {
// 1-character punctuators.
str = source[index];
if ('<>=!+-*%&|^/'.indexOf(str) >= 0) {
++index;
}
}
}
}
}
if (index === token.start) {
throwUnexpectedToken();
}
token.end = index;
token.value = str;
return token;
}
// ECMA-262 11.8.3 Numeric Literals
function scanHexLiteral(start) {
var number = '';
while (index < length) {
if (!isHexDigit(source[index])) {
break;
}
number += source[index++];
}
if (number.length === 0) {
throwUnexpectedToken();
}
if (isIdentifierStart(source.charCodeAt(index))) {
throwUnexpectedToken();
}
return {
type: Token.NumericLiteral,
value: parseInt('0x' + number, 16),
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
function scanBinaryLiteral(start) {
var ch, number;
number = '';
while (index < length) {
ch = source[index];
if (ch !== '0' && ch !== '1') {
break;
}
number += source[index++];
}
if (number.length === 0) {
// only 0b or 0B
throwUnexpectedToken();
}
if (index < length) {
ch = source.charCodeAt(index);
/* istanbul ignore else */
if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
throwUnexpectedToken();
}
}
return {
type: Token.NumericLiteral,
value: parseInt(number, 2),
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
function scanOctalLiteral(prefix, start) {
var number, octal;
if (isOctalDigit(prefix)) {
octal = true;
number = '0' + source[index++];
} else {
octal = false;
++index;
number = '';
}
while (index < length) {
if (!isOctalDigit(source[index])) {
break;
}
number += source[index++];
}
if (!octal && number.length === 0) {
// only 0o or 0O
throwUnexpectedToken();
}
if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) {
throwUnexpectedToken();
}
return {
type: Token.NumericLiteral,
value: parseInt(number, 8),
octal: octal,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
function isImplicitOctalLiteral() {
var i, ch;
// Implicit octal, unless there is a non-octal digit.
// (Annex B.1.1 on Numeric Literals)
for (i = index + 1; i < length; ++i) {
ch = source[i];
if (ch === '8' || ch === '9') {
return false;
}
if (!isOctalDigit(ch)) {
return true;
}
}
return true;
}
function scanNumericLiteral() {
var number, start, ch;
ch = source[index];
assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
'Numeric literal must start with a decimal digit or a decimal point');
start = index;
number = '';
if (ch !== '.') {
number = source[index++];
ch = source[index];
// Hex number starts with '0x'.
// Octal number starts with '0'.
// Octal number in ES6 starts with '0o'.
// Binary number in ES6 starts with '0b'.
if (number === '0') {
if (ch === 'x' || ch === 'X') {
++index;
return scanHexLiteral(start);
}
if (ch === 'b' || ch === 'B') {
++index;
return scanBinaryLiteral(start);
}
if (ch === 'o' || ch === 'O') {
return scanOctalLiteral(ch, start);
}
if (isOctalDigit(ch)) {
if (isImplicitOctalLiteral()) {
return scanOctalLiteral(ch, start);
}
}
}
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
ch = source[index];
}
if (ch === '.') {
number += source[index++];
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
ch = source[index];
}
if (ch === 'e' || ch === 'E') {
number += source[index++];
ch = source[index];
if (ch === '+' || ch === '-') {
number += source[index++];
}
if (isDecimalDigit(source.charCodeAt(index))) {
while (isDecimalDigit(source.charCodeAt(index))) {
number += source[index++];
}
} else {
throwUnexpectedToken();
}
}
if (isIdentifierStart(source.charCodeAt(index))) {
throwUnexpectedToken();
}
return {
type: Token.NumericLiteral,
value: parseFloat(number),
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
// ECMA-262 11.8.4 String Literals
function scanStringLiteral() {
var str = '', quote, start, ch, unescaped, octToDec, octal = false;
quote = source[index];
assert((quote === '\'' || quote === '"'),
'String literal must starts with a quote');
start = index;
++index;
while (index < length) {
ch = source[index++];
if (ch === quote) {
quote = '';
break;
} else if (ch === '\\') {
ch = source[index++];
if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
switch (ch) {
case 'u':
case 'x':
if (source[index] === '{') {
++index;
str += scanUnicodeCodePointEscape();
} else {
unescaped = scanHexEscape(ch);
if (!unescaped) {
throw throwUnexpectedToken();
}
str += unescaped;
}
break;
case 'n':
str += '\n';
break;
case 'r':
str += '\r';
break;
case 't':
str += '\t';
break;
case 'b':
str += '\b';
break;
case 'f':
str += '\f';
break;
case 'v':
str += '\x0B';
break;
case '8':
case '9':
str += ch;
tolerateUnexpectedToken();
break;
default:
if (isOctalDigit(ch)) {
octToDec = octalToDecimal(ch);
octal = octToDec.octal || octal;
str += String.fromCharCode(octToDec.code);
} else {
str += ch;
}
break;
}
} else {
++lineNumber;
if (ch === '\r' && source[index] === '\n') {
++index;
}
lineStart = index;
}
} else if (isLineTerminator(ch.charCodeAt(0))) {
break;
} else {
str += ch;
}
}
if (quote !== '') {
throwUnexpectedToken();
}
return {
type: Token.StringLiteral,
value: str,
octal: octal,
lineNumber: startLineNumber,
lineStart: startLineStart,
start: start,
end: index
};
}
// ECMA-262 11.8.6 Template Literal Lexical Components
function scanTemplate() {
var cooked = '', ch, start, rawOffset, terminated, head, tail, restore, unescaped;
terminated = false;
tail = false;
start = index;
head = (source[index] === '`');
rawOffset = 2;
++index;
while (index < length) {
ch = source[index++];
if (ch === '`') {
rawOffset = 1;
tail = true;
terminated = true;
break;
} else if (ch === '$') {
if (source[index] === '{') {
state.curlyStack.push('${');
++index;
terminated = true;
break;
}
cooked += ch;
} else if (ch === '\\') {
ch = source[index++];
if (!isLineTerminator(ch.charCodeAt(0))) {
switch (ch) {
case 'n':
cooked += '\n';
break;
case 'r':
cooked += '\r';
break;
case 't':
cooked += '\t';
break;
case 'u':
case 'x':
if (source[index] === '{') {
++index;
cooked += scanUnicodeCodePointEscape();
} else {
restore = index;
unescaped = scanHexEscape(ch);
if (unescaped) {
cooked += unescaped;
} else {
index = restore;
cooked += ch;
}
}
break;
case 'b':
cooked += '\b';
break;
case 'f':
cooked += '\f';
break;
case 'v':
cooked += '\v';
break;
default:
if (ch === '0') {
if (isDecimalDigit(source.charCodeAt(index))) {
// Illegal: \01 \02 and so on
throwError(Messages.TemplateOctalLiteral);
}
cooked += '\0';
} else if (isOctalDigit(ch)) {
// Illegal: \1 \2
throwError(Messages.TemplateOctalLiteral);
} else {
cooked += ch;
}
break;
}
} else {
++lineNumber;
if (ch === '\r' && source[index] === '\n') {
++index;
}
lineStart = index;
}
} else if (isLineTerminator(ch.charCodeAt(0))) {
++lineNumber;
if (ch === '\r' && source[index] === '\n') {
++index;
}
lineStart = index;
cooked += '\n';
} else {
cooked += ch;
}
}
if (!terminated) {
throwUnexpectedToken();
}
if (!head) {
state.curlyStack.pop();
}
return {
type: Token.Template,
value: {
cooked: cooked,
raw: source.slice(start + 1, index - rawOffset)
},
head: head,
tail: tail,
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
// ECMA-262 11.8.5 Regular Expression Literals
function testRegExp(pattern, flags) {
// The BMP character to use as a replacement for astral symbols when
// translating an ES6 "u"-flagged pattern to an ES5-compatible
// approximation.
// Note: replacing with '\uFFFF' enables false positives in unlikely
// scenarios. For example, `[\u{1044f}-\u{10440}]` is an invalid
// pattern that would not be detected by this substitution.
var astralSubstitute = '\uFFFF',
tmp = pattern;
if (flags.indexOf('u') >= 0) {
tmp = tmp
// Replace every Unicode escape sequence with the equivalent
// BMP character or a constant ASCII code point in the case of
// astral symbols. (See the above note on `astralSubstitute`
// for more information.)
.replace(/\\u\{([0-9a-fA-F]+)\}|\\u([a-fA-F0-9]{4})/g, function ($0, $1, $2) {
var codePoint = parseInt($1 || $2, 16);
if (codePoint > 0x10FFFF) {
throwUnexpectedToken(null, Messages.InvalidRegExp);
}
if (codePoint <= 0xFFFF) {
return String.fromCharCode(codePoint);
}
return astralSubstitute;
})
// Replace each paired surrogate with a single ASCII symbol to
// avoid throwing on regular expressions that are only valid in
// combination with the "u" flag.
.replace(
/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
astralSubstitute
);
}
// First, detect invalid regular expressions.
try {
RegExp(tmp);
} catch (e) {
throwUnexpectedToken(null, Messages.InvalidRegExp);
}
// Return a regular expression object for this pattern-flag pair, or
// `null` in case the current environment doesn't support the flags it
// uses.
try {
return new RegExp(pattern, flags);
} catch (exception) {
return null;
}
}
function scanRegExpBody() {
var ch, str, classMarker, terminated, body;
ch = source[index];
assert(ch === '/', 'Regular expression literal must start with a slash');
str = source[index++];
classMarker = false;
terminated = false;
while (index < length) {
ch = source[index++];
str += ch;
if (ch === '\\') {
ch = source[index++];
// ECMA-262 7.8.5
if (isLineTerminator(ch.charCodeAt(0))) {
throwUnexpectedToken(null, Messages.UnterminatedRegExp);
}
str += ch;
} else if (isLineTerminator(ch.charCodeAt(0))) {
throwUnexpectedToken(null, Messages.UnterminatedRegExp);
} else if (classMarker) {
if (ch === ']') {
classMarker = false;
}
} else {
if (ch === '/') {
terminated = true;
break;
} else if (ch === '[') {
classMarker = true;
}
}
}
if (!terminated) {
throwUnexpectedToken(null, Messages.UnterminatedRegExp);
}
// Exclude leading and trailing slash.
body = str.substr(1, str.length - 2);
return {
value: body,
literal: str
};
}
function scanRegExpFlags() {
var ch, str, flags, restore;
str = '';
flags = '';
while (index < length) {
ch = source[index];
if (!isIdentifierPart(ch.charCodeAt(0))) {
break;
}
++index;
if (ch === '\\' && index < length) {
ch = source[index];
if (ch === 'u') {
++index;
restore = index;
ch = scanHexEscape('u');
if (ch) {
flags += ch;
for (str += '\\u'; restore < index; ++restore) {
str += source[restore];
}
} else {
index = restore;
flags += 'u';
str += '\\u';
}
tolerateUnexpectedToken();
} else {
str += '\\';
tolerateUnexpectedToken();
}
} else {
flags += ch;
str += ch;
}
}
return {
value: flags,
literal: str
};
}
function scanRegExp() {
var start, body, flags, value;
scanning = true;
lookahead = null;
skipComment();
start = index;
body = scanRegExpBody();
flags = scanRegExpFlags();
value = testRegExp(body.value, flags.value);
scanning = false;
if (extra.tokenize) {
return {
type: Token.RegularExpression,
value: value,
regex: {
pattern: body.value,
flags: flags.value
},
lineNumber: lineNumber,
lineStart: lineStart,
start: start,
end: index
};
}
return {
literal: body.literal + flags.literal,
value: value,
regex: {
pattern: body.value,
flags: flags.value
},
start: start,
end: index
};
}
function collectRegex() {
var pos, loc, regex, token;
skipComment();
pos = index;
loc = {
start: {
line: lineNumber,
column: index - lineStart
}
};
regex = scanRegExp();
loc.end = {
line: lineNumber,
column: index - lineStart
};
/* istanbul ignore next */
if (!extra.tokenize) {
// Pop the previous token, which is likely '/' or '/='
if (extra.tokens.length > 0) {
token = extra.tokens[extra.tokens.length - 1];
if (token.range[0] === pos && token.type === 'Punctuator') {
if (token.value === '/' || token.value === '/=') {
extra.tokens.pop();
}
}
}
extra.tokens.push({
type: 'RegularExpression',
value: regex.literal,
regex: regex.regex,
range: [pos, index],
loc: loc
});
}
return regex;
}
function isIdentifierName(token) {
return token.type === Token.Identifier ||
token.type === Token.Keyword ||
token.type === Token.BooleanLiteral ||
token.type === Token.NullLiteral;
}
// Using the following algorithm:
// https://github.com/mozilla/sweet.js/wiki/design
function advanceSlash() {
var regex, previous, check;
function testKeyword(value) {
return value && (value.length > 1) && (value[0] >= 'a') && (value[0] <= 'z');
}
previous = extra.tokenValues[extra.tokens.length - 1];
regex = (previous !== null);
switch (previous) {
case 'this':
case ']':
regex = false;
break;
case ')':
check = extra.tokenValues[extra.openParenToken - 1];
regex = (check === 'if' || check === 'while' || check === 'for' || check === 'with');
break;
case '}':
// Dividing a function by anything makes little sense,
// but we have to check for that.
regex = false;
if (testKeyword(extra.tokenValues[extra.openCurlyToken - 3])) {
// Anonymous function, e.g. function(){} /42
check = extra.tokenValues[extra.openCurlyToken - 4];
regex = check ? (FnExprTokens.indexOf(check) < 0) : false;
} else if (testKeyword(extra.tokenValues[extra.openCurlyToken - 4])) {
// Named function, e.g. function f(){} /42/
check = extra.tokenValues[extra.openCurlyToken - 5];
regex = check ? (FnExprTokens.indexOf(check) < 0) : true;
}
}
return regex ? collectRegex() : scanPunctuator();
}
function advance() {
var cp, token;
if (index >= length) {
return {
type: Token.EOF,
lineNumber: lineNumber,
lineStart: lineStart,
start: index,
end: index
};
}
cp = source.charCodeAt(index);
if (isIdentifierStart(cp)) {
token = scanIdentifier();
if (strict && isStrictModeReservedWord(token.value)) {
token.type = Token.Keyword;
}
return token;
}
// Very common: ( and ) and ;
if (cp === 0x28 || cp === 0x29 || cp === 0x3B) {
return scanPunctuator();
}
// String literal starts with single quote (U+0027) or double quote (U+0022).
if (cp === 0x27 || cp === 0x22) {
return scanStringLiteral();
}
// Dot (.) U+002E can also start a floating-point number, hence the need
// to check the next character.
if (cp === 0x2E) {
if (isDecimalDigit(source.charCodeAt(index + 1))) {
return scanNumericLiteral();
}
return scanPunctuator();
}
if (isDecimalDigit(cp)) {
return scanNumericLiteral();
}
// Slash (/) U+002F can also start a regex.
if (extra.tokenize && cp === 0x2F) {
return advanceSlash();
}
// Template literals start with ` (U+0060) for template head
// or } (U+007D) for template middle or template tail.
if (cp === 0x60 || (cp === 0x7D && state.curlyStack[state.curlyStack.length - 1] === '${')) {
return scanTemplate();
}
// Possible identifier start in a surrogate pair.
if (cp >= 0xD800 && cp < 0xDFFF) {
cp = codePointAt(index);
if (isIdentifierStart(cp)) {
return scanIdentifier();
}
}
return scanPunctuator();
}
function collectToken() {
var loc, token, value, entry;
loc = {
start: {
line: lineNumber,
column: index - lineStart
}
};
token = advance();
loc.end = {
line: lineNumber,
column: index - lineStart
};
if (token.type !== Token.EOF) {
value = source.slice(token.start, token.end);
entry = {
type: TokenName[token.type],
value: value,
range: [token.start, token.end],
loc: loc
};
if (token.regex) {
entry.regex = {
pattern: token.regex.pattern,
flags: token.regex.flags
};
}
if (extra.tokenValues) {
extra.tokenValues.push((entry.type === 'Punctuator' || entry.type === 'Keyword') ? entry.value : null);
}
if (extra.tokenize) {
if (!extra.range) {
delete entry.range;
}
if (!extra.loc) {
delete entry.loc;
}
if (extra.delegate) {
entry = extra.delegate(entry);
}
}
extra.tokens.push(entry);
}
return token;
}
function lex() {
var token;
scanning = true;
lastIndex = index;
lastLineNumber = lineNumber;
lastLineStart = lineStart;
skipComment();
token = lookahead;
startIndex = index;
startLineNumber = lineNumber;
startLineStart = lineStart;
lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance();
scanning = false;
return token;
}
function peek() {
scanning = true;
skipComment();
lastIndex = index;
lastLineNumber = lineNumber;
lastLineStart = lineStart;
startIndex = index;
startLineNumber = lineNumber;
startLineStart = lineStart;
lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance();
scanning = false;
}
function Position() {
this.line = startLineNumber;
this.column = startIndex - startLineStart;
}
function SourceLocation() {
this.start = new Position();
this.end = null;
}
function WrappingSourceLocation(startToken) {
this.start = {
line: startToken.lineNumber,
column: startToken.start - startToken.lineStart
};
this.end = null;
}
function Node() {
if (extra.range) {
this.range = [startIndex, 0];
}
if (extra.loc) {
this.loc = new SourceLocation();
}
}
function WrappingNode(startToken) {
if (extra.range) {
this.range = [startToken.start, 0];
}
if (extra.loc) {
this.loc = new WrappingSourceLocation(startToken);
}
}
WrappingNode.prototype = Node.prototype = {
processComment: function () {
var lastChild,
innerComments,
leadingComments,
trailingComments,
bottomRight = extra.bottomRightStack,
i,
comment,
last = bottomRight[bottomRight.length - 1];
if (this.type === Syntax.Program) {
if (this.body.length > 0) {
return;
}
}
/**
* patch innnerComments for properties empty block
* `function a() {/** comments **\/}`
*/
if (this.type === Syntax.BlockStatement && this.body.length === 0) {
innerComments = [];
for (i = extra.leadingComments.length - 1; i >= 0; --i) {
comment = extra.leadingComments[i];
if (this.range[1] >= comment.range[1]) {
innerComments.unshift(comment);
extra.leadingComments.splice(i, 1);
extra.trailingComments.splice(i, 1);
}
}
if (innerComments.length) {
this.innerComments = innerComments;
//bottomRight.push(this);
return;
}
}
if (extra.trailingComments.length > 0) {
trailingComments = [];
for (i = extra.trailingComments.length - 1; i >= 0; --i) {
comment = extra.trailingComments[i];
if (comment.range[0] >= this.range[1]) {
trailingComments.unshift(comment);
extra.trailingComments.splice(i, 1);
}
}
extra.trailingComments = [];
} else {
if (last && last.trailingComments && last.trailingComments[0].range[0] >= this.range[1]) {
trailingComments = last.trailingComments;
delete last.trailingComments;
}
}
// Eating the stack.
while (last && last.range[0] >= this.range[0]) {
lastChild = bottomRight.pop();
last = bottomRight[bottomRight.length - 1];
}
if (lastChild) {
if (lastChild.leadingComments) {
leadingComments = [];
for (i = lastChild.leadingComments.length - 1; i >= 0; --i) {
comment = lastChild.leadingComments[i];
if (comment.range[1] <= this.range[0]) {
leadingComments.unshift(comment);
lastChild.leadingComments.splice(i, 1);
}
}
if (!lastChild.leadingComments.length) {
lastChild.leadingComments = undefined;
}
}
} else if (extra.leadingComments.length > 0) {
leadingComments = [];
for (i = extra.leadingComments.length - 1; i >= 0; --i) {
comment = extra.leadingComments[i];
if (comment.range[1] <= this.range[0]) {
leadingComments.unshift(comment);
extra.leadingComments.splice(i, 1);
}
}
}
if (leadingComments && leadingComments.length > 0) {
this.leadingComments = leadingComments;
}
if (trailingComments && trailingComments.length > 0) {
this.trailingComments = trailingComments;
}
bottomRight.push(this);
},
finish: function () {
if (extra.range) {
this.range[1] = lastIndex;
}
if (extra.loc) {
this.loc.end = {
line: lastLineNumber,
column: lastIndex - lastLineStart
};
if (extra.source) {
this.loc.source = extra.source;
}
}
if (extra.attachComment) {
this.processComment();
}
},
finishArrayExpression: function (elements) {
this.type = Syntax.ArrayExpression;
this.elements = elements;
this.finish();
return this;
},
finishArrayPattern: function (elements) {
this.type = Syntax.ArrayPattern;
this.elements = elements;
this.finish();
return this;
},
finishArrowFunctionExpression: function (params, defaults, body, expression) {
this.type = Syntax.ArrowFunctionExpression;
this.id = null;
this.params = params;
this.defaults = defaults;
this.body = body;
this.generator = false;
this.expression = expression;
this.finish();
return this;
},
finishAssignmentExpression: function (operator, left, right) {
this.type = Syntax.AssignmentExpression;
this.operator = operator;
this.left = left;
this.right = right;
this.finish();
return this;
},
finishAssignmentPattern: function (left, right) {
this.type = Syntax.AssignmentPattern;
this.left = left;
this.right = right;
this.finish();
return this;
},
finishBinaryExpression: function (operator, left, right) {
this.type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression : Syntax.BinaryExpression;
this.operator = operator;
this.left = left;
this.right = right;
this.finish();
return this;
},
finishBlockStatement: function (body) {
this.type = Syntax.BlockStatement;
this.body = body;
this.finish();
return this;
},
finishBreakStatement: function (label) {
this.type = Syntax.BreakStatement;
this.label = label;
this.finish();
return this;
},
finishCallExpression: function (callee, args) {
this.type = Syntax.CallExpression;
this.callee = callee;
this.arguments = args;
this.finish();
return this;
},
finishCatchClause: function (param, body) {
this.type = Syntax.CatchClause;
this.param = param;
this.body = body;
this.finish();
return this;
},
finishClassBody: function (body) {
this.type = Syntax.ClassBody;
this.body = body;
this.finish();
return this;
},
finishClassDeclaration: function (id, superClass, body) {
this.type = Syntax.ClassDeclaration;
this.id = id;
this.superClass = superClass;
this.body = body;
this.finish();
return this;
},
finishClassExpression: function (id, superClass, body) {
this.type = Syntax.ClassExpression;
this.id = id;
this.superClass = superClass;
this.body = body;
this.finish();
return this;
},
finishConditionalExpression: function (test, consequent, alternate) {
this.type = Syntax.ConditionalExpression;
this.test = test;
this.consequent = consequent;
this.alternate = alternate;
this.finish();
return this;
},
finishContinueStatement: function (label) {
this.type = Syntax.ContinueStatement;
this.label = label;
this.finish();
return this;
},
finishDebuggerStatement: function () {
this.type = Syntax.DebuggerStatement;
this.finish();
return this;
},
finishDoWhileStatement: function (body, test) {
this.type = Syntax.DoWhileStatement;
this.body = body;
this.test = test;
this.finish();
return this;
},
finishEmptyStatement: function () {
this.type = Syntax.EmptyStatement;
this.finish();
return this;
},
finishExpressionStatement: function (expression) {
this.type = Syntax.ExpressionStatement;
this.expression = expression;
this.finish();
return this;
},
finishForStatement: function (init, test, update, body) {
this.type = Syntax.ForStatement;
this.init = init;
this.test = test;
this.update = update;
this.body = body;
this.finish();
return this;
},
finishForOfStatement: function (left, right, body) {
this.type = Syntax.ForOfStatement;
this.left = left;
this.right = right;
this.body = body;
this.finish();
return this;
},
finishForInStatement: function (left, right, body) {
this.type = Syntax.ForInStatement;
this.left = left;
this.right = right;
this.body = body;
this.each = false;
this.finish();
return this;
},
finishFunctionDeclaration: function (id, params, defaults, body, generator) {
this.type = Syntax.FunctionDeclaration;
this.id = id;
this.params = params;
this.defaults = defaults;
this.body = body;
this.generator = generator;
this.expression = false;
this.finish();
return this;
},
finishFunctionExpression: function (id, params, defaults, body, generator) {
this.type = Syntax.FunctionExpression;
this.id = id;
this.params = params;
this.defaults = defaults;
this.body = body;
this.generator = generator;
this.expression = false;
this.finish();
return this;
},
finishIdentifier: function (name) {
this.type = Syntax.Identifier;
this.name = name;
this.finish();
return this;
},
finishIfStatement: function (test, consequent, alternate) {
this.type = Syntax.IfStatement;
this.test = test;
this.consequent = consequent;
this.alternate = alternate;
this.finish();
return this;
},
finishLabeledStatement: function (label, body) {
this.type = Syntax.LabeledStatement;
this.label = label;
this.body = body;
this.finish();
return this;
},
finishLiteral: function (token) {
this.type = Syntax.Literal;
this.value = token.value;
this.raw = source.slice(token.start, token.end);
if (token.regex) {
this.regex = token.regex;
}
this.finish();
return this;
},
finishMemberExpression: function (accessor, object, property) {
this.type = Syntax.MemberExpression;
this.computed = accessor === '[';
this.object = object;
this.property = property;
this.finish();
return this;
},
finishMetaProperty: function (meta, property) {
this.type = Syntax.MetaProperty;
this.meta = meta;
this.property = property;
this.finish();
return this;
},
finishNewExpression: function (callee, args) {
this.type = Syntax.NewExpression;
this.callee = callee;
this.arguments = args;
this.finish();
return this;
},
finishObjectExpression: function (properties) {
this.type = Syntax.ObjectExpression;
this.properties = properties;
this.finish();
return this;
},
finishObjectPattern: function (properties) {
this.type = Syntax.ObjectPattern;
this.properties = properties;
this.finish();
return this;
},
finishPostfixExpression: function (operator, argument) {
this.type = Syntax.UpdateExpression;
this.operator = operator;
this.argument = argument;
this.prefix = false;
this.finish();
return this;
},
finishProgram: function (body, sourceType) {
this.type = Syntax.Program;
this.body = body;
this.sourceType = sourceType;
this.finish();
return this;
},
finishProperty: function (kind, key, computed, value, method, shorthand) {
this.type = Syntax.Property;
this.key = key;
this.computed = computed;
this.value = value;
this.kind = kind;
this.method = method;
this.shorthand = shorthand;
this.finish();
return this;
},
finishRestElement: function (argument) {
this.type = Syntax.RestElement;
this.argument = argument;
this.finish();
return this;
},
finishReturnStatement: function (argument) {
this.type = Syntax.ReturnStatement;
this.argument = argument;
this.finish();
return this;
},
finishSequenceExpression: function (expressions) {
this.type = Syntax.SequenceExpression;
this.expressions = expressions;
this.finish();
return this;
},
finishSpreadElement: function (argument) {
this.type = Syntax.SpreadElement;
this.argument = argument;
this.finish();
return this;
},
finishSwitchCase: function (test, consequent) {
this.type = Syntax.SwitchCase;
this.test = test;
this.consequent = consequent;
this.finish();
return this;
},
finishSuper: function () {
this.type = Syntax.Super;
this.finish();
return this;
},
finishSwitchStatement: function (discriminant, cases) {
this.type = Syntax.SwitchStatement;
this.discriminant = discriminant;
this.cases = cases;
this.finish();
return this;
},
finishTaggedTemplateExpression: function (tag, quasi) {
this.type = Syntax.TaggedTemplateExpression;
this.tag = tag;
this.quasi = quasi;
this.finish();
return this;
},
finishTemplateElement: function (value, tail) {
this.type = Syntax.TemplateElement;
this.value = value;
this.tail = tail;
this.finish();
return this;
},
finishTemplateLiteral: function (quasis, expressions) {
this.type = Syntax.TemplateLiteral;
this.quasis = quasis;
this.expressions = expressions;
this.finish();
return this;
},
finishThisExpression: function () {
this.type = Syntax.ThisExpression;
this.finish();
return this;
},
finishThrowStatement: function (argument) {
this.type = Syntax.ThrowStatement;
this.argument = argument;
this.finish();
return this;
},
finishTryStatement: function (block, handler, finalizer) {
this.type = Syntax.TryStatement;
this.block = block;
this.guardedHandlers = [];
this.handlers = handler ? [handler] : [];
this.handler = handler;
this.finalizer = finalizer;
this.finish();
return this;
},
finishUnaryExpression: function (operator, argument) {
this.type = (operator === '++' || operator === '--') ? Syntax.UpdateExpression : Syntax.UnaryExpression;
this.operator = operator;
this.argument = argument;
this.prefix = true;
this.finish();
return this;
},
finishVariableDeclaration: function (declarations) {
this.type = Syntax.VariableDeclaration;
this.declarations = declarations;
this.kind = 'var';
this.finish();
return this;
},
finishLexicalDeclaration: function (declarations, kind) {
this.type = Syntax.VariableDeclaration;
this.declarations = declarations;
this.kind = kind;
this.finish();
return this;
},
finishVariableDeclarator: function (id, init) {
this.type = Syntax.VariableDeclarator;
this.id = id;
this.init = init;
this.finish();
return this;
},
finishWhileStatement: function (test, body) {
this.type = Syntax.WhileStatement;
this.test = test;
this.body = body;
this.finish();
return this;
},
finishWithStatement: function (object, body) {
this.type = Syntax.WithStatement;
this.object = object;
this.body = body;
this.finish();
return this;
},
finishExportSpecifier: function (local, exported) {
this.type = Syntax.ExportSpecifier;
this.exported = exported || local;
this.local = local;
this.finish();
return this;
},
finishImportDefaultSpecifier: function (local) {
this.type = Syntax.ImportDefaultSpecifier;
this.local = local;
this.finish();
return this;
},
finishImportNamespaceSpecifier: function (local) {
this.type = Syntax.ImportNamespaceSpecifier;
this.local = local;
this.finish();
return this;
},
finishExportNamedDeclaration: function (declaration, specifiers, src) {
this.type = Syntax.ExportNamedDeclaration;
this.declaration = declaration;
this.specifiers = specifiers;
this.source = src;
this.finish();
return this;
},
finishExportDefaultDeclaration: function (declaration) {
this.type = Syntax.ExportDefaultDeclaration;
this.declaration = declaration;
this.finish();
return this;
},
finishExportAllDeclaration: function (src) {
this.type = Syntax.ExportAllDeclaration;
this.source = src;
this.finish();
return this;
},
finishImportSpecifier: function (local, imported) {
this.type = Syntax.ImportSpecifier;
this.local = local || imported;
this.imported = imported;
this.finish();
return this;
},
finishImportDeclaration: function (specifiers, src) {
this.type = Syntax.ImportDeclaration;
this.specifiers = specifiers;
this.source = src;
this.finish();
return this;
},
finishYieldExpression: function (argument, delegate) {
this.type = Syntax.YieldExpression;
this.argument = argument;
this.delegate = delegate;
this.finish();
return this;
}
};
function recordError(error) {
var e, existing;
for (e = 0; e < extra.errors.length; e++) {
existing = extra.errors[e];
// Prevent duplicated error.
/* istanbul ignore next */
if (existing.index === error.index && existing.message === error.message) {
return;
}
}
extra.errors.push(error);
}
function constructError(msg, column) {
var error = new Error(msg);
try {
throw error;
} catch (base) {
/* istanbul ignore else */
if (Object.create && Object.defineProperty) {
error = Object.create(base);
Object.defineProperty(error, 'column', { value: column });
}
} finally {
return error;
}
}
function createError(line, pos, description) {
var msg, column, error;
column = pos - (scanning ? lineStart : lastLineStart) + 1;
msg = 'Line ' + line + ' Column '+column+': ' + description;
error = constructError(msg, column);
error.lineNumber = line;
error.description = description;
error.index = pos;
return error;
}
// Throw an exception
function throwError(messageFormat) {
var args, msg;
args = Array.prototype.slice.call(arguments, 1);
msg = messageFormat.replace(/%(\d)/g,
function (whole, idx) {
assert(idx < args.length, 'Message reference must be in range');
return args[idx];
}
);
throw createError(lastLineNumber, lastIndex, msg);
}
function tolerateError(messageFormat) {
var args, msg, error;
args = Array.prototype.slice.call(arguments, 1);
/* istanbul ignore next */
msg = messageFormat.replace(/%(\d)/g,
function (whole, idx) {
assert(idx < args.length, 'Message reference must be in range');
return args[idx];
}
);
error = createError(lineNumber, lastIndex, msg);
if (extra.errors) {
recordError(error);
} else {
throw error;
}
}
// Throw an exception because of the token.
function unexpectedTokenError(token, message) {
var value, msg = message || Messages.UnexpectedToken;
if (token) {
if (!message) {
msg = (token.type === Token.EOF) ? Messages.UnexpectedEOS :
(token.type === Token.Identifier) ? Messages.UnexpectedIdentifier :
(token.type === Token.NumericLiteral) ? Messages.UnexpectedNumber :
(token.type === Token.StringLiteral) ? Messages.UnexpectedString :
(token.type === Token.Template) ? Messages.UnexpectedTemplate :
Messages.UnexpectedToken;
if (token.type === Token.Keyword) {
if (isFutureReservedWord(token.value)) {
msg = Messages.UnexpectedReserved;
} else if (strict && isStrictModeReservedWord(token.value)) {
msg = Messages.StrictReservedWord;
}
}
}
value = (token.type === Token.Template) ? token.value.raw : token.value;
} else {
value = 'ILLEGAL';
}
msg = msg.replace('%0', value);
return (token && typeof token.lineNumber === 'number') ?
createError(token.lineNumber, token.start, msg) :
createError(scanning ? lineNumber : lastLineNumber, scanning ? index : lastIndex, msg);
}
function throwUnexpectedToken(token, message) {
throw unexpectedTokenError(token, message);
}
function tolerateUnexpectedToken(token, message) {
var error = unexpectedTokenError(token, message);
if (extra.errors) {
recordError(error);
} else {
throw error;
}
}
// Expect the next token to match the specified punctuator.
// If not, an exception will be thrown.
function expect(value) {
var token = lex();
if (token.type !== Token.Punctuator || token.value !== value) {
throwUnexpectedToken(token);
}
}
/**
* @name expectCommaSeparator
* @description Quietly expect a comma when in tolerant mode, otherwise delegates
* to <code>expect(value)</code>
* @since 2.0
*/
function expectCommaSeparator() {
var token;
if (extra.errors) {
token = lookahead;
if (token.type === Token.Punctuator && token.value === ',') {
lex();
} else if (token.type === Token.Punctuator && token.value === ';') {
lex();
tolerateUnexpectedToken(token);
} else {
tolerateUnexpectedToken(token, Messages.UnexpectedToken);
}
} else {
expect(',');
}
}
// Expect the next token to match the specified keyword.
// If not, an exception will be thrown.
function expectKeyword(keyword) {
var token = lex();
if (token.type !== Token.Keyword || token.value !== keyword) {
throwUnexpectedToken(token);
}
}
// Return true if the next token matches the specified punctuator.
function match(value) {
return lookahead.type === Token.Punctuator && lookahead.value === value;
}
// Return true if the next token matches the specified keyword
function matchKeyword(keyword) {
return lookahead.type === Token.Keyword && lookahead.value === keyword;
}
// Return true if the next token matches the specified contextual keyword
// (where an identifier is sometimes a keyword depending on the context)
function matchContextualKeyword(keyword) {
return lookahead.type === Token.Identifier && lookahead.value === keyword;
}
// Return true if the next token is an assignment operator
function matchAssign() {
var op;
if (lookahead.type !== Token.Punctuator) {
return false;
}
op = lookahead.value;
return op === '=' ||
op === '*=' ||
op === '/=' ||
op === '%=' ||
op === '+=' ||
op === '-=' ||
op === '<<=' ||
op === '>>=' ||
op === '>>>=' ||
op === '&=' ||
op === '^=' ||
op === '|=';
}
function consumeSemicolon() {
// Catch the very common case first: immediately a semicolon (U+003B).
if (source.charCodeAt(startIndex) === 0x3B || match(';')) {
lex();
return;
}
if (hasLineTerminator) {
return;
}
// FIXME(ikarienator): this is seemingly an issue in the previous location info convention.
lastIndex = startIndex;
lastLineNumber = startLineNumber;
lastLineStart = startLineStart;
if (lookahead.type !== Token.EOF && !match('}')) {
throwUnexpectedToken(lookahead);
}
}
// Cover grammar support.
//
// When an assignment expression position starts with an left parenthesis, the determination of the type
// of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead)
// or the first comma. This situation also defers the determination of all the expressions nested in the pair.
//
// There are three productions that can be parsed in a parentheses pair that needs to be determined
// after the outermost pair is closed. They are:
//
// 1. AssignmentExpression
// 2. BindingElements
// 3. AssignmentTargets
//
// In order to avoid exponential backtracking, we use two flags to denote if the production can be
// binding element or assignment target.
//
// The three productions have the relationship:
//
// BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression
//
// with a single exception that CoverInitializedName when used directly in an Expression, generates
// an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the
// first usage of CoverInitializedName and report it when we reached the end of the parentheses pair.
//
// isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not
// effect the current flags. This means the production the parser parses is only used as an expression. Therefore
// the CoverInitializedName check is conducted.
//
// inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates
// the flags outside of the parser. This means the production the parser parses is used as a part of a potential
// pattern. The CoverInitializedName check is deferred.
function isolateCoverGrammar(parser) {
var oldIsBindingElement = isBindingElement,
oldIsAssignmentTarget = isAssignmentTarget,
oldFirstCoverInitializedNameError = firstCoverInitializedNameError,
result;
isBindingElement = true;
isAssignmentTarget = true;
firstCoverInitializedNameError = null;
result = parser();
if (firstCoverInitializedNameError !== null) {
throwUnexpectedToken(firstCoverInitializedNameError);
}
isBindingElement = oldIsBindingElement;
isAssignmentTarget = oldIsAssignmentTarget;
firstCoverInitializedNameError = oldFirstCoverInitializedNameError;
return result;
}
function inheritCoverGrammar(parser) {
var oldIsBindingElement = isBindingElement,
oldIsAssignmentTarget = isAssignmentTarget,
oldFirstCoverInitializedNameError = firstCoverInitializedNameError,
result;
isBindingElement = true;
isAssignmentTarget = true;
firstCoverInitializedNameError = null;
result = parser();
isBindingElement = isBindingElement && oldIsBindingElement;
isAssignmentTarget = isAssignmentTarget && oldIsAssignmentTarget;
firstCoverInitializedNameError = oldFirstCoverInitializedNameError || firstCoverInitializedNameError;
return result;
}
// ECMA-262 13.3.3 Destructuring Binding Patterns
function parseArrayPattern(params, kind) {
var node = new Node(), elements = [], rest, restNode;
expect('[');
while (!match(']')) {
if (match(',')) {
lex();
elements.push(null);
} else {
if (match('...')) {
restNode = new Node();
lex();
params.push(lookahead);
rest = parseVariableIdentifier(kind);
elements.push(restNode.finishRestElement(rest));
break;
} else {
elements.push(parsePatternWithDefault(params, kind));
}
if (!match(']')) {
expect(',');
}
}
}
expect(']');
return node.finishArrayPattern(elements);
}
function parsePropertyPattern(params, kind) {
var node = new Node(), key, keyToken, computed = match('['), init;
if (lookahead.type === Token.Identifier) {
keyToken = lookahead;
key = parseVariableIdentifier();
if (match('=')) {
params.push(keyToken);
lex();
init = parseAssignmentExpression();
return node.finishProperty(
'init', key, false,
new WrappingNode(keyToken).finishAssignmentPattern(key, init), false, false);
} else if (!match(':')) {
params.push(keyToken);
return node.finishProperty('init', key, false, key, false, true);
}
} else {
key = parseObjectPropertyKey();
}
expect(':');
init = parsePatternWithDefault(params, kind);
return node.finishProperty('init', key, computed, init, false, false);
}
function parseObjectPattern(params, kind) {
var node = new Node(), properties = [];
expect('{');
while (!match('}')) {
properties.push(parsePropertyPattern(params, kind));
if (!match('}')) {
expect(',');
}
}
lex();
return node.finishObjectPattern(properties);
}
function parsePattern(params, kind) {
if (match('[')) {
return parseArrayPattern(params, kind);
} else if (match('{')) {
return parseObjectPattern(params, kind);
} else if (matchKeyword('let')) {
if (kind === 'const' || kind === 'let') {
tolerateUnexpectedToken(lookahead, Messages.UnexpectedToken);
}
}
params.push(lookahead);
return parseVariableIdentifier(kind);
}
function parsePatternWithDefault(params, kind) {
var startToken = lookahead, pattern, previousAllowYield, right;
pattern = parsePattern(params, kind);
if (match('=')) {
lex();
previousAllowYield = state.allowYield;
state.allowYield = true;
right = isolateCoverGrammar(parseAssignmentExpression);
state.allowYield = previousAllowYield;
pattern = new WrappingNode(startToken).finishAssignmentPattern(pattern, right);
}
return pattern;
}
// ECMA-262 12.2.5 Array Initializer
function parseArrayInitializer() {
var elements = [], node = new Node(), restSpread;
expect('[');
while (!match(']')) {
if (match(',')) {
lex();
elements.push(null);
} else if (match('...')) {
restSpread = new Node();
lex();
restSpread.finishSpreadElement(inheritCoverGrammar(parseAssignmentExpression));
if (!match(']')) {
isAssignmentTarget = isBindingElement = false;
expect(',');
}
elements.push(restSpread);
} else {
elements.push(inheritCoverGrammar(parseAssignmentExpression));
if (!match(']')) {
expect(',');
}
}
}
lex();
return node.finishArrayExpression(elements);
}
// ECMA-262 12.2.6 Object Initializer
function parsePropertyFunction(node, paramInfo, isGenerator) {
var previousStrict, body;
isAssignmentTarget = isBindingElement = false;
previousStrict = strict;
body = isolateCoverGrammar(parseFunctionSourceElements);
if (strict && paramInfo.firstRestricted) {
tolerateUnexpectedToken(paramInfo.firstRestricted, paramInfo.message);
}
if (strict && paramInfo.stricted) {
tolerateUnexpectedToken(paramInfo.stricted, paramInfo.message);
}
strict = previousStrict;
return node.finishFunctionExpression(null, paramInfo.params, paramInfo.defaults, body, isGenerator);
}
function parsePropertyMethodFunction() {
var params, method, node = new Node(),
previousAllowYield = state.allowYield;
state.allowYield = false;
params = parseParams();
state.allowYield = previousAllowYield;
state.allowYield = false;
method = parsePropertyFunction(node, params, false);
state.allowYield = previousAllowYield;
return method;
}
function parseObjectPropertyKey() {
var token, node = new Node(), expr;
token = lex();
// Note: This function is called only from parseObjectProperty(), where
// EOF and Punctuator tokens are already filtered out.
switch (token.type) {
case Token.StringLiteral:
case Token.NumericLiteral:
if (strict && token.octal) {
tolerateUnexpectedToken(token, Messages.StrictOctalLiteral);
}
return node.finishLiteral(token);
case Token.Identifier:
case Token.BooleanLiteral:
case Token.NullLiteral:
case Token.Keyword:
return node.finishIdentifier(token.value);
case Token.Punctuator:
if (token.value === '[') {
expr = isolateCoverGrammar(parseAssignmentExpression);
expect(']');
return expr;
}
break;
}
throwUnexpectedToken(token);
}
function lookaheadPropertyName() {
switch (lookahead.type) {
case Token.Identifier:
case Token.StringLiteral:
case Token.BooleanLiteral:
case Token.NullLiteral:
case Token.NumericLiteral:
case Token.Keyword:
return true;
case Token.Punctuator:
return lookahead.value === '[';
}
return false;
}
// This function is to try to parse a MethodDefinition as defined in 14.3. But in the case of object literals,
// it might be called at a position where there is in fact a short hand identifier pattern or a data property.
// This can only be determined after we consumed up to the left parentheses.
//
// In order to avoid back tracking, it returns `null` if the position is not a MethodDefinition and the caller
// is responsible to visit other options.
function tryParseMethodDefinition(token, key, computed, node) {
var value, options, methodNode, params,
previousAllowYield = state.allowYield;
if (token.type === Token.Identifier) {
// check for `get` and `set`;
if (token.value === 'get' && lookaheadPropertyName()) {
computed = match('[');
key = parseObjectPropertyKey();
methodNode = new Node();
expect('(');
expect(')');
state.allowYield = false;
value = parsePropertyFunction(methodNode, {
params: [],
defaults: [],
stricted: null,
firstRestricted: null,
message: null
}, false);
state.allowYield = previousAllowYield;
return node.finishProperty('get', key, computed, value, false, false);
} else if (token.value === 'set' && lookaheadPropertyName()) {
computed = match('[');
key = parseObjectPropertyKey();
methodNode = new Node();
expect('(');
options = {
params: [],
defaultCount: 0,
defaults: [],
firstRestricted: null,
paramSet: {}
};
if (match(')')) {
tolerateUnexpectedToken(lookahead);
} else {
state.allowYield = false;
parseParam(options);
state.allowYield = previousAllowYield;
if (options.defaultCount === 0) {
options.defaults = [];
}
}
expect(')');
state.allowYield = false;
value = parsePropertyFunction(methodNode, options, false);
state.allowYield = previousAllowYield;
return node.finishProperty('set', key, computed, value, false, false);
}
} else if (token.type === Token.Punctuator && token.value === '*' && lookaheadPropertyName()) {
computed = match('[');
key = parseObjectPropertyKey();
methodNode = new Node();
state.allowYield = true;
params = parseParams();
state.allowYield = previousAllowYield;
state.allowYield = false;
value = parsePropertyFunction(methodNode, params, true);
state.allowYield = previousAllowYield;
return node.finishProperty('init', key, computed, value, true, false);
}
if (key && match('(')) {
value = parsePropertyMethodFunction();
return node.finishProperty('init', key, computed, value, true, false);
}
// Not a MethodDefinition.
return null;
}
function parseObjectProperty(hasProto) {
var token = lookahead, node = new Node(), computed, key, maybeMethod, proto, value;
computed = match('[');
if (match('*')) {
lex();
} else {
key = parseObjectPropertyKey();
}
maybeMethod = tryParseMethodDefinition(token, key, computed, node);
if (maybeMethod) {
return maybeMethod;
}
if (!key) {
throwUnexpectedToken(lookahead);
}
// Check for duplicated __proto__
if (!computed) {
proto = (key.type === Syntax.Identifier && key.name === '__proto__') ||
(key.type === Syntax.Literal && key.value === '__proto__');
if (hasProto.value && proto) {
tolerateError(Messages.DuplicateProtoProperty);
}
hasProto.value |= proto;
}
if (match(':')) {
lex();
value = inheritCoverGrammar(parseAssignmentExpression);
return node.finishProperty('init', key, computed, value, false, false);
}
if (token.type === Token.Identifier) {
if (match('=')) {
firstCoverInitializedNameError = lookahead;
lex();
value = isolateCoverGrammar(parseAssignmentExpression);
return node.finishProperty('init', key, computed,
new WrappingNode(token).finishAssignmentPattern(key, value), false, true);
}
return node.finishProperty('init', key, computed, key, false, true);
}
throwUnexpectedToken(lookahead);
}
function parseObjectInitializer() {
var properties = [], hasProto = {value: false}, node = new Node();
expect('{');
while (!match('}')) {
properties.push(parseObjectProperty(hasProto));
if (!match('}')) {
expectCommaSeparator();
}
}
expect('}');
return node.finishObjectExpression(properties);
}
function reinterpretExpressionAsPattern(expr) {
var i;
switch (expr.type) {
case Syntax.Identifier:
case Syntax.MemberExpression:
case Syntax.RestElement:
case Syntax.AssignmentPattern:
break;
case Syntax.SpreadElement:
expr.type = Syntax.RestElement;
reinterpretExpressionAsPattern(expr.argument);
break;
case Syntax.ArrayExpression:
expr.type = Syntax.ArrayPattern;
for (i = 0; i < expr.elements.length; i++) {
if (expr.elements[i] !== null) {
reinterpretExpressionAsPattern(expr.elements[i]);
}
}
break;
case Syntax.ObjectExpression:
expr.type = Syntax.ObjectPattern;
for (i = 0; i < expr.properties.length; i++) {
reinterpretExpressionAsPattern(expr.properties[i].value);
}
break;
case Syntax.AssignmentExpression:
expr.type = Syntax.AssignmentPattern;
reinterpretExpressionAsPattern(expr.left);
break;
default:
// Allow other node type for tolerant parsing.
break;
}
}
// ECMA-262 12.2.9 Template Literals
function parseTemplateElement(option) {
var node, token;
if (lookahead.type !== Token.Template || (option.head && !lookahead.head)) {
throwUnexpectedToken();
}
node = new Node();
token = lex();
return node.finishTemplateElement({ raw: token.value.raw, cooked: token.value.cooked }, token.tail);
}
function parseTemplateLiteral() {
var quasi, quasis, expressions, node = new Node();
quasi = parseTemplateElement({ head: true });
quasis = [quasi];
expressions = [];
while (!quasi.tail) {
expressions.push(parseExpression());
quasi = parseTemplateElement({ head: false });
quasis.push(quasi);
}
return node.finishTemplateLiteral(quasis, expressions);
}
// ECMA-262 12.2.10 The Grouping Operator
function parseGroupExpression() {
var expr, expressions, startToken, i, params = [];
expect('(');
if (match(')')) {
lex();
if (!match('=>')) {
expect('=>');
}
return {
type: PlaceHolders.ArrowParameterPlaceHolder,
params: [],
rawParams: []
};
}
startToken = lookahead;
if (match('...')) {
expr = parseRestElement(params);
expect(')');
if (!match('=>')) {
expect('=>');
}
return {
type: PlaceHolders.ArrowParameterPlaceHolder,
params: [expr]
};
}
isBindingElement = true;
expr = inheritCoverGrammar(parseAssignmentExpression);
if (match(',')) {
isAssignmentTarget = false;
expressions = [expr];
while (startIndex < length) {
if (!match(',')) {
break;
}
lex();
if (match('...')) {
if (!isBindingElement) {
throwUnexpectedToken(lookahead);
}
expressions.push(parseRestElement(params));
expect(')');
if (!match('=>')) {
expect('=>');
}
isBindingElement = false;
for (i = 0; i < expressions.length; i++) {
reinterpretExpressionAsPattern(expressions[i]);
}
return {
type: PlaceHolders.ArrowParameterPlaceHolder,
params: expressions
};
}
expressions.push(inheritCoverGrammar(parseAssignmentExpression));
}
expr = new WrappingNode(startToken).finishSequenceExpression(expressions);
}
expect(')');
if (match('=>')) {
if (expr.type === Syntax.Identifier && expr.name === 'yield') {
return {
type: PlaceHolders.ArrowParameterPlaceHolder,
params: [expr]
};
}
if (!isBindingElement) {
throwUnexpectedToken(lookahead);
}
if (expr.type === Syntax.SequenceExpression) {
for (i = 0; i < expr.expressions.length; i++) {
reinterpretExpressionAsPattern(expr.expressions[i]);
}
} else {
reinterpretExpressionAsPattern(expr);
}
expr = {
type: PlaceHolders.ArrowParameterPlaceHolder,
params: expr.type === Syntax.SequenceExpression ? expr.expressions : [expr]
};
}
isBindingElement = false;
return expr;
}
// ECMA-262 12.2 Primary Expressions
function parsePrimaryExpression() {
var type, token, expr, node;
if (match('(')) {
isBindingElement = false;
return inheritCoverGrammar(parseGroupExpression);
}
if (match('[')) {
return inheritCoverGrammar(parseArrayInitializer);
}
if (match('{')) {
return inheritCoverGrammar(parseObjectInitializer);
}
type = lookahead.type;
node = new Node();
if (type === Token.Identifier) {
if (state.sourceType === 'module' && lookahead.value === 'await') {
tolerateUnexpectedToken(lookahead);
}
expr = node.finishIdentifier(lex().value);
} else if (type === Token.StringLiteral || type === Token.NumericLiteral) {
isAssignmentTarget = isBindingElement = false;
if (strict && lookahead.octal) {
tolerateUnexpectedToken(lookahead, Messages.StrictOctalLiteral);
}
expr = node.finishLiteral(lex());
} else if (type === Token.Keyword) {
if (!strict && state.allowYield && matchKeyword('yield')) {
return parseNonComputedProperty();
}
isAssignmentTarget = isBindingElement = false;
if (matchKeyword('function')) {
return parseFunctionExpression();
}
if (matchKeyword('this')) {
lex();
return node.finishThisExpression();
}
if (matchKeyword('class')) {
return parseClassExpression();
}
if (!strict && matchKeyword('let')) {
return node.finishIdentifier(lex().value);
}
throwUnexpectedToken(lex());
} else if (type === Token.BooleanLiteral) {
isAssignmentTarget = isBindingElement = false;
token = lex();
token.value = (token.value === 'true');
expr = node.finishLiteral(token);
} else if (type === Token.NullLiteral) {
isAssignmentTarget = isBindingElement = false;
token = lex();
token.value = null;
expr = node.finishLiteral(token);
} else if (match('/') || match('/=')) {
isAssignmentTarget = isBindingElement = false;
index = startIndex;
if (typeof extra.tokens !== 'undefined') {
token = collectRegex();
} else {
token = scanRegExp();
}
lex();
expr = node.finishLiteral(token);
} else if (type === Token.Template) {
expr = parseTemplateLiteral();
} else {
throwUnexpectedToken(lex());
}
return expr;
}
// ECMA-262 12.3 Left-Hand-Side Expressions
function parseArguments() {
var args = [], expr;
expect('(');
if (!match(')')) {
while (startIndex < length) {
if (match('...')) {
expr = new Node();
lex();
expr.finishSpreadElement(isolateCoverGrammar(parseAssignmentExpression));
} else {
expr = isolateCoverGrammar(parseAssignmentExpression);
}
args.push(expr);
if (match(')')) {
break;
}
expectCommaSeparator();
}
}
expect(')');
return args;
}
function parseNonComputedProperty() {
var token, node = new Node();
token = lex();
if (!isIdentifierName(token)) {
throwUnexpectedToken(token);
}
return node.finishIdentifier(token.value);
}
function parseNonComputedMember() {
expect('.');
return parseNonComputedProperty();
}
function parseComputedMember() {
var expr;
expect('[');
expr = isolateCoverGrammar(parseExpression);
expect(']');
return expr;
}
// ECMA-262 12.3.3 The new Operator
function parseNewExpression() {
var callee, args, node = new Node();
expectKeyword('new');
if (match('.')) {
lex();
if (lookahead.type === Token.Identifier && lookahead.value === 'target') {
if (state.inFunctionBody) {
lex();
return node.finishMetaProperty('new', 'target');
}
}
throwUnexpectedToken(lookahead);
}
callee = isolateCoverGrammar(parseLeftHandSideExpression);
args = match('(') ? parseArguments() : [];
isAssignmentTarget = isBindingElement = false;
return node.finishNewExpression(callee, args);
}
// ECMA-262 12.3.4 Function Calls
function parseLeftHandSideExpressionAllowCall() {
var quasi, expr, args, property, startToken, previousAllowIn = state.allowIn;
startToken = lookahead;
state.allowIn = true;
if (matchKeyword('super') && state.inFunctionBody) {
expr = new Node();
lex();
expr = expr.finishSuper();
if (!match('(') && !match('.') && !match('[')) {
throwUnexpectedToken(lookahead);
}
} else {
expr = inheritCoverGrammar(matchKeyword('new') ? parseNewExpression : parsePrimaryExpression);
}
for (;;) {
if (match('.')) {
isBindingElement = false;
isAssignmentTarget = true;
property = parseNonComputedMember();
expr = new WrappingNode(startToken).finishMemberExpression('.', expr, property);
} else if (match('(')) {
isBindingElement = false;
isAssignmentTarget = false;
args = parseArguments();
expr = new WrappingNode(startToken).finishCallExpression(expr, args);
} else if (match('[')) {
isBindingElement = false;
isAssignmentTarget = true;
property = parseComputedMember();
expr = new WrappingNode(startToken).finishMemberExpression('[', expr, property);
} else if (lookahead.type === Token.Template && lookahead.head) {
quasi = parseTemplateLiteral();
expr = new WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi);
} else {
break;
}
}
state.allowIn = previousAllowIn;
return expr;
}
// ECMA-262 12.3 Left-Hand-Side Expressions
function parseLeftHandSideExpression() {
var quasi, expr, property, startToken;
assert(state.allowIn, 'callee of new expression always allow in keyword.');
startToken = lookahead;
if (matchKeyword('super') && state.inFunctionBody) {
expr = new Node();
lex();
expr = expr.finishSuper();
if (!match('[') && !match('.')) {
throwUnexpectedToken(lookahead);
}
} else {
expr = inheritCoverGrammar(matchKeyword('new') ? parseNewExpression : parsePrimaryExpression);
}
for (;;) {
if (match('[')) {
isBindingElement = false;
isAssignmentTarget = true;
property = parseComputedMember();
expr = new WrappingNode(startToken).finishMemberExpression('[', expr, property);
} else if (match('.')) {
isBindingElement = false;
isAssignmentTarget = true;
property = parseNonComputedMember();
expr = new WrappingNode(startToken).finishMemberExpression('.', expr, property);
} else if (lookahead.type === Token.Template && lookahead.head) {
quasi = parseTemplateLiteral();
expr = new WrappingNode(startToken).finishTaggedTemplateExpression(expr, quasi);
} else {
break;
}
}
return expr;
}
// ECMA-262 12.4 Postfix Expressions
function parsePostfixExpression() {
var expr, token, startToken = lookahead;
expr = inheritCoverGrammar(parseLeftHandSideExpressionAllowCall);
if (!hasLineTerminator && lookahead.type === Token.Punctuator) {
if (match('++') || match('--')) {
// ECMA-262 11.3.1, 11.3.2
if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
tolerateError(Messages.StrictLHSPostfix);
}
if (!isAssignmentTarget) {
tolerateError(Messages.InvalidLHSInAssignment);
}
isAssignmentTarget = isBindingElement = false;
token = lex();
expr = new WrappingNode(startToken).finishPostfixExpression(token.value, expr);
}
}
return expr;
}
// ECMA-262 12.5 Unary Operators
function parseUnaryExpression() {
var token, expr, startToken;
if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
expr = parsePostfixExpression();
} else if (match('++') || match('--')) {
startToken = lookahead;
token = lex();
expr = inheritCoverGrammar(parseUnaryExpression);
// ECMA-262 11.4.4, 11.4.5
if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
tolerateError(Messages.StrictLHSPrefix);
}
if (!isAssignmentTarget) {
tolerateError(Messages.InvalidLHSInAssignment);
}
expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr);
isAssignmentTarget = isBindingElement = false;
} else if (match('+') || match('-') || match('~') || match('!')) {
startToken = lookahead;
token = lex();
expr = inheritCoverGrammar(parseUnaryExpression);
expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr);
isAssignmentTarget = isBindingElement = false;
} else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
startToken = lookahead;
token = lex();
expr = inheritCoverGrammar(parseUnaryExpression);
expr = new WrappingNode(startToken).finishUnaryExpression(token.value, expr);
if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
tolerateError(Messages.StrictDelete);
}
isAssignmentTarget = isBindingElement = false;
} else {
expr = parsePostfixExpression();
}
return expr;
}
function binaryPrecedence(token, allowIn) {
var prec = 0;
if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
return 0;
}
switch (token.value) {
case '||':
prec = 1;
break;
case '&&':
prec = 2;
break;
case '|':
prec = 3;
break;
case '^':
prec = 4;
break;
case '&':
prec = 5;
break;
case '==':
case '!=':
case '===':
case '!==':
prec = 6;
break;
case '<':
case '>':
case '<=':
case '>=':
case 'instanceof':
prec = 7;
break;
case 'in':
prec = allowIn ? 7 : 0;
break;
case '<<':
case '>>':
case '>>>':
prec = 8;
break;
case '+':
case '-':
prec = 9;
break;
case '*':
case '/':
case '%':
prec = 11;
break;
default:
break;
}
return prec;
}
// ECMA-262 12.6 Multiplicative Operators
// ECMA-262 12.7 Additive Operators
// ECMA-262 12.8 Bitwise Shift Operators
// ECMA-262 12.9 Relational Operators
// ECMA-262 12.10 Equality Operators
// ECMA-262 12.11 Binary Bitwise Operators
// ECMA-262 12.12 Binary Logical Operators
function parseBinaryExpression() {
var marker, markers, expr, token, prec, stack, right, operator, left, i;
marker = lookahead;
left = inheritCoverGrammar(parseUnaryExpression);
token = lookahead;
prec = binaryPrecedence(token, state.allowIn);
if (prec === 0) {
return left;
}
isAssignmentTarget = isBindingElement = false;
token.prec = prec;
lex();
markers = [marker, lookahead];
right = isolateCoverGrammar(parseUnaryExpression);
stack = [left, token, right];
while ((prec = binaryPrecedence(lookahead, state.allowIn)) > 0) {
// Reduce: make a binary expression from the three topmost entries.
while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
right = stack.pop();
operator = stack.pop().value;
left = stack.pop();
markers.pop();
expr = new WrappingNode(markers[markers.length - 1]).finishBinaryExpression(operator, left, right);
stack.push(expr);
}
// Shift.
token = lex();
token.prec = prec;
stack.push(token);
markers.push(lookahead);
expr = isolateCoverGrammar(parseUnaryExpression);
stack.push(expr);
}
// Final reduce to clean-up the stack.
i = stack.length - 1;
expr = stack[i];
markers.pop();
while (i > 1) {
expr = new WrappingNode(markers.pop()).finishBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
i -= 2;
}
return expr;
}
// ECMA-262 12.13 Conditional Operator
function parseConditionalExpression() {
var expr, previousAllowIn, consequent, alternate, startToken;
startToken = lookahead;
expr = inheritCoverGrammar(parseBinaryExpression);
if (match('?')) {
lex();
previousAllowIn = state.allowIn;
state.allowIn = true;
consequent = isolateCoverGrammar(parseAssignmentExpression);
state.allowIn = previousAllowIn;
expect(':');
alternate = isolateCoverGrammar(parseAssignmentExpression);
expr = new WrappingNode(startToken).finishConditionalExpression(expr, consequent, alternate);
isAssignmentTarget = isBindingElement = false;
}
return expr;
}
// ECMA-262 14.2 Arrow Function Definitions
function parseConciseBody() {
if (match('{')) {
return parseFunctionSourceElements();
}
return isolateCoverGrammar(parseAssignmentExpression);
}
function checkPatternParam(options, param) {
var i;
switch (param.type) {
case Syntax.Identifier:
validateParam(options, param, param.name);
break;
case Syntax.RestElement:
checkPatternParam(options, param.argument);
break;
case Syntax.AssignmentPattern:
checkPatternParam(options, param.left);
break;
case Syntax.ArrayPattern:
for (i = 0; i < param.elements.length; i++) {
if (param.elements[i] !== null) {
checkPatternParam(options, param.elements[i]);
}
}
break;
case Syntax.YieldExpression:
break;
default:
assert(param.type === Syntax.ObjectPattern, 'Invalid type');
for (i = 0; i < param.properties.length; i++) {
checkPatternParam(options, param.properties[i].value);
}
break;
}
}
function reinterpretAsCoverFormalsList(expr) {
var i, len, param, params, defaults, defaultCount, options, token;
defaults = [];
defaultCount = 0;
params = [expr];
switch (expr.type) {
case Syntax.Identifier:
break;
case PlaceHolders.ArrowParameterPlaceHolder:
params = expr.params;
break;
default:
return null;
}
options = {
paramSet: {}
};
for (i = 0, len = params.length; i < len; i += 1) {
param = params[i];
switch (param.type) {
case Syntax.AssignmentPattern:
params[i] = param.left;
if (param.right.type === Syntax.YieldExpression) {
if (param.right.argument) {
throwUnexpectedToken(lookahead);
}
param.right.type = Syntax.Identifier;
param.right.name = 'yield';
delete param.right.argument;
delete param.right.delegate;
}
defaults.push(param.right);
++defaultCount;
checkPatternParam(options, param.left);
break;
default:
checkPatternParam(options, param);
params[i] = param;
defaults.push(null);
break;
}
}
if (strict || !state.allowYield) {
for (i = 0, len = params.length; i < len; i += 1) {
param = params[i];
if (param.type === Syntax.YieldExpression) {
throwUnexpectedToken(lookahead);
}
}
}
if (options.message === Messages.StrictParamDupe) {
token = strict ? options.stricted : options.firstRestricted;
throwUnexpectedToken(token, options.message);
}
if (defaultCount === 0) {
defaults = [];
}
return {
params: params,
defaults: defaults,
stricted: options.stricted,
firstRestricted: options.firstRestricted,
message: options.message
};
}
function parseArrowFunctionExpression(options, node) {
var previousStrict, previousAllowYield, body;
if (hasLineTerminator) {
tolerateUnexpectedToken(lookahead);
}
expect('=>');
previousStrict = strict;
previousAllowYield = state.allowYield;
state.allowYield = true;
body = parseConciseBody();
if (strict && options.firstRestricted) {
throwUnexpectedToken(options.firstRestricted, options.message);
}
if (strict && options.stricted) {
tolerateUnexpectedToken(options.stricted, options.message);
}
strict = previousStrict;
state.allowYield = previousAllowYield;
return node.finishArrowFunctionExpression(options.params, options.defaults, body, body.type !== Syntax.BlockStatement);
}
// ECMA-262 14.4 Yield expression
function parseYieldExpression() {
var argument, expr, delegate, previousAllowYield;
argument = null;
expr = new Node();
expectKeyword('yield');
if (!hasLineTerminator) {
previousAllowYield = state.allowYield;
state.allowYield = false;
delegate = match('*');
if (delegate) {
lex();
argument = parseAssignmentExpression();
} else {
if (!match(';') && !match('}') && !match(')') && lookahead.type !== Token.EOF) {
argument = parseAssignmentExpression();
}
}
state.allowYield = previousAllowYield;
}
return expr.finishYieldExpression(argument, delegate);
}
// ECMA-262 12.14 Assignment Operators
function parseAssignmentExpression() {
var token, expr, right, list, startToken;
startToken = lookahead;
token = lookahead;
if (!state.allowYield && matchKeyword('yield')) {
return parseYieldExpression();
}
expr = parseConditionalExpression();
if (expr.type === PlaceHolders.ArrowParameterPlaceHolder || match('=>')) {
isAssignmentTarget = isBindingElement = false;
list = reinterpretAsCoverFormalsList(expr);
if (list) {
firstCoverInitializedNameError = null;
return parseArrowFunctionExpression(list, new WrappingNode(startToken));
}
return expr;
}
if (matchAssign()) {
if (!isAssignmentTarget) {
tolerateError(Messages.InvalidLHSInAssignment);
}
// ECMA-262 12.1.1
if (strict && expr.type === Syntax.Identifier) {
if (isRestrictedWord(expr.name)) {
tolerateUnexpectedToken(token, Messages.StrictLHSAssignment);
}
if (isStrictModeReservedWord(expr.name)) {
tolerateUnexpectedToken(token, Messages.StrictReservedWord);
}
}
if (!match('=')) {
isAssignmentTarget = isBindingElement = false;
} else {
reinterpretExpressionAsPattern(expr);
}
token = lex();
right = isolateCoverGrammar(parseAssignmentExpression);
expr = new WrappingNode(startToken).finishAssignmentExpression(token.value, expr, right);
firstCoverInitializedNameError = null;
}
return expr;
}
// ECMA-262 12.15 Comma Operator
function parseExpression() {
var expr, startToken = lookahead, expressions;
expr = isolateCoverGrammar(parseAssignmentExpression);
if (match(',')) {
expressions = [expr];
while (startIndex < length) {
if (!match(',')) {
break;
}
lex();
expressions.push(isolateCoverGrammar(parseAssignmentExpression));
}
expr = new WrappingNode(startToken).finishSequenceExpression(expressions);
}
return expr;
}
// ECMA-262 13.2 Block
function parseStatementListItem() {
if (lookahead.type === Token.Keyword) {
switch (lookahead.value) {
case 'export':
if (state.sourceType !== 'module') {
tolerateUnexpectedToken(lookahead, Messages.IllegalExportDeclaration);
}
return parseExportDeclaration();
case 'import':
if (state.sourceType !== 'module') {
tolerateUnexpectedToken(lookahead, Messages.IllegalImportDeclaration);
}
return parseImportDeclaration();
case 'const':
return parseLexicalDeclaration({inFor: false});
case 'function':
return parseFunctionDeclaration(new Node());
case 'class':
return parseClassDeclaration();
}
}
if (matchKeyword('let') && isLexicalDeclaration()) {
return parseLexicalDeclaration({inFor: false});
}
return parseStatement();
}
function parseStatementList() {
var list = [];
while (startIndex < length) {
if (match('}')) {
break;
}
list.push(parseStatementListItem());
}
return list;
}
function parseBlock() {
var block, node = new Node();
expect('{');
block = parseStatementList();
expect('}');
return node.finishBlockStatement(block);
}
// ECMA-262 13.3.2 Variable Statement
function parseVariableIdentifier(kind) {
var token, node = new Node();
token = lex();
if (token.type === Token.Keyword && token.value === 'yield') {
if (strict) {
tolerateUnexpectedToken(token, Messages.StrictReservedWord);
} if (!state.allowYield) {
throwUnexpectedToken(token);
}
} else if (token.type !== Token.Identifier) {
if (strict && token.type === Token.Keyword && isStrictModeReservedWord(token.value)) {
tolerateUnexpectedToken(token, Messages.StrictReservedWord);
} else {
if (strict || token.value !== 'let' || kind !== 'var') {
throwUnexpectedToken(token);
}
}
} else if (state.sourceType === 'module' && token.type === Token.Identifier && token.value === 'await') {
tolerateUnexpectedToken(token);
}
return node.finishIdentifier(token.value);
}
function parseVariableDeclaration(options) {
var init = null, id, node = new Node(), params = [];
id = parsePattern(params, 'var');
// ECMA-262 12.2.1
if (strict && isRestrictedWord(id.name)) {
tolerateError(Messages.StrictVarName);
}
if (match('=')) {
lex();
init = isolateCoverGrammar(parseAssignmentExpression);
} else if (id.type !== Syntax.Identifier && !options.inFor) {
expect('=');
}
return node.finishVariableDeclarator(id, init);
}
function parseVariableDeclarationList(options) {
var list = [];
do {
list.push(parseVariableDeclaration({ inFor: options.inFor }));
if (!match(',')) {
break;
}
lex();
} while (startIndex < length);
return list;
}
function parseVariableStatement(node) {
var declarations;
expectKeyword('var');
declarations = parseVariableDeclarationList({ inFor: false });
consumeSemicolon();
return node.finishVariableDeclaration(declarations);
}
// ECMA-262 13.3.1 Let and Const Declarations
function parseLexicalBinding(kind, options) {
var init = null, id, node = new Node(), params = [];
id = parsePattern(params, kind);
// ECMA-262 12.2.1
if (strict && id.type === Syntax.Identifier && isRestrictedWord(id.name)) {
tolerateError(Messages.StrictVarName);
}
if (kind === 'const') {
if (!matchKeyword('in') && !matchContextualKeyword('of')) {
expect('=');
init = isolateCoverGrammar(parseAssignmentExpression);
}
} else if ((!options.inFor && id.type !== Syntax.Identifier) || match('=')) {
expect('=');
init = isolateCoverGrammar(parseAssignmentExpression);
}
return node.finishVariableDeclarator(id, init);
}
function parseBindingList(kind, options) {
var list = [];
do {
list.push(parseLexicalBinding(kind, options));
if (!match(',')) {
break;
}
lex();
} while (startIndex < length);
return list;
}
function tokenizerState() {
return {
index: index,
lineNumber: lineNumber,
lineStart: lineStart,
hasLineTerminator: hasLineTerminator,
lastIndex: lastIndex,
lastLineNumber: lastLineNumber,
lastLineStart: lastLineStart,
startIndex: startIndex,
startLineNumber: startLineNumber,
startLineStart: startLineStart,
lookahead: lookahead,
tokenCount: extra.tokens ? extra.tokens.length : 0
};
}
function resetTokenizerState(ts) {
index = ts.index;
lineNumber = ts.lineNumber;
lineStart = ts.lineStart;
hasLineTerminator = ts.hasLineTerminator;
lastIndex = ts.lastIndex;
lastLineNumber = ts.lastLineNumber;
lastLineStart = ts.lastLineStart;
startIndex = ts.startIndex;
startLineNumber = ts.startLineNumber;
startLineStart = ts.startLineStart;
lookahead = ts.lookahead;
if (extra.tokens) {
extra.tokens.splice(ts.tokenCount, extra.tokens.length);
}
}
function isLexicalDeclaration() {
var lexical, ts;
ts = tokenizerState();
lex();
lexical = (lookahead.type === Token.Identifier) || match('[') || match('{') ||
matchKeyword('let') || matchKeyword('yield');
resetTokenizerState(ts);
return lexical;
}
function parseLexicalDeclaration(options) {
var kind, declarations, node = new Node();
kind = lex().value;
assert(kind === 'let' || kind === 'const', 'Lexical declaration must be either let or const');
declarations = parseBindingList(kind, options);
consumeSemicolon();
return node.finishLexicalDeclaration(declarations, kind);
}
function parseRestElement(params) {
var param, node = new Node();
lex();
if (match('{')) {
throwError(Messages.ObjectPatternAsRestParameter);
}
params.push(lookahead);
param = parseVariableIdentifier();
if (match('=')) {
throwError(Messages.DefaultRestParameter);
}
if (!match(')')) {
throwError(Messages.ParameterAfterRestParameter);
}
return node.finishRestElement(param);
}
// ECMA-262 13.4 Empty Statement
function parseEmptyStatement(node) {
expect(';');
return node.finishEmptyStatement();
}
// ECMA-262 12.4 Expression Statement
function parseExpressionStatement(node) {
var expr = parseExpression();
consumeSemicolon();
return node.finishExpressionStatement(expr);
}
// ECMA-262 13.6 If statement
function parseIfStatement(node) {
var test, consequent, alternate;
expectKeyword('if');
expect('(');
test = parseExpression();
expect(')');
consequent = parseStatement();
if (matchKeyword('else')) {
lex();
alternate = parseStatement();
} else {
alternate = null;
}
return node.finishIfStatement(test, consequent, alternate);
}
// ECMA-262 13.7 Iteration Statements
function parseDoWhileStatement(node) {
var body, test, oldInIteration;
expectKeyword('do');
oldInIteration = state.inIteration;
state.inIteration = true;
body = parseStatement();
state.inIteration = oldInIteration;
expectKeyword('while');
expect('(');
test = parseExpression();
expect(')');
if (match(';')) {
lex();
}
return node.finishDoWhileStatement(body, test);
}
function parseWhileStatement(node) {
var test, body, oldInIteration;
expectKeyword('while');
expect('(');
test = parseExpression();
expect(')');
oldInIteration = state.inIteration;
state.inIteration = true;
body = parseStatement();
state.inIteration = oldInIteration;
return node.finishWhileStatement(test, body);
}
function parseForStatement(node) {
var init, forIn, initSeq, initStartToken, test, update, left, right, kind, declarations,
body, oldInIteration, previousAllowIn = state.allowIn;
init = test = update = null;
forIn = true;
expectKeyword('for');
expect('(');
if (match(';')) {
lex();
} else {
if (matchKeyword('var')) {
init = new Node();
lex();
state.allowIn = false;
declarations = parseVariableDeclarationList({ inFor: true });
state.allowIn = previousAllowIn;
if (declarations.length === 1 && matchKeyword('in')) {
init = init.finishVariableDeclaration(declarations);
lex();
left = init;
right = parseExpression();
init = null;
} else if (declarations.length === 1 && declarations[0].init === null && matchContextualKeyword('of')) {
init = init.finishVariableDeclaration(declarations);
lex();
left = init;
right = parseAssignmentExpression();
init = null;
forIn = false;
} else {
init = init.finishVariableDeclaration(declarations);
expect(';');
}
} else if (matchKeyword('const') || matchKeyword('let')) {
init = new Node();
kind = lex().value;
if (!strict && lookahead.value === 'in') {
init = init.finishIdentifier(kind);
lex();
left = init;
right = parseExpression();
init = null;
} else {
state.allowIn = false;
declarations = parseBindingList(kind, {inFor: true});
state.allowIn = previousAllowIn;
if (declarations.length === 1 && declarations[0].init === null && matchKeyword('in')) {
init = init.finishLexicalDeclaration(declarations, kind);
lex();
left = init;
right = parseExpression();
init = null;
} else if (declarations.length === 1 && declarations[0].init === null && matchContextualKeyword('of')) {
init = init.finishLexicalDeclaration(declarations, kind);
lex();
left = init;
right = parseAssignmentExpression();
init = null;
forIn = false;
} else {
consumeSemicolon();
init = init.finishLexicalDeclaration(declarations, kind);
}
}
} else {
initStartToken = lookahead;
state.allowIn = false;
init = inheritCoverGrammar(parseAssignmentExpression);
state.allowIn = previousAllowIn;
if (matchKeyword('in')) {
if (!isAssignmentTarget) {
tolerateError(Messages.InvalidLHSInForIn);
}
lex();
reinterpretExpressionAsPattern(init);
left = init;
right = parseExpression();
init = null;
} else if (matchContextualKeyword('of')) {
if (!isAssignmentTarget) {
tolerateError(Messages.InvalidLHSInForLoop);
}
lex();
reinterpretExpressionAsPattern(init);
left = init;
right = parseAssignmentExpression();
init = null;
forIn = false;
} else {
if (match(',')) {
initSeq = [init];
while (match(',')) {
lex();
initSeq.push(isolateCoverGrammar(parseAssignmentExpression));
}
init = new WrappingNode(initStartToken).finishSequenceExpression(initSeq);
}
expect(';');
}
}
}
if (typeof left === 'undefined') {
if (!match(';')) {
test = parseExpression();
}
expect(';');
if (!match(')')) {
update = parseExpression();
}
}
expect(')');
oldInIteration = state.inIteration;
state.inIteration = true;
body = isolateCoverGrammar(parseStatement);
state.inIteration = oldInIteration;
return (typeof left === 'undefined') ?
node.finishForStatement(init, test, update, body) :
forIn ? node.finishForInStatement(left, right, body) :
node.finishForOfStatement(left, right, body);
}
// ECMA-262 13.8 The continue statement
function parseContinueStatement(node) {
var label = null, key;
expectKeyword('continue');
// Optimize the most common form: 'continue;'.
if (source.charCodeAt(startIndex) === 0x3B) {
lex();
if (!state.inIteration) {
throwError(Messages.IllegalContinue);
}
return node.finishContinueStatement(null);
}
if (hasLineTerminator) {
if (!state.inIteration) {
throwError(Messages.IllegalContinue);
}
return node.finishContinueStatement(null);
}
if (lookahead.type === Token.Identifier) {
label = parseVariableIdentifier();
key = '$' + label.name;
if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
throwError(Messages.UnknownLabel, label.name);
}
}
consumeSemicolon();
if (label === null && !state.inIteration) {
throwError(Messages.IllegalContinue);
}
return node.finishContinueStatement(label);
}
// ECMA-262 13.9 The break statement
function parseBreakStatement(node) {
var label = null, key;
expectKeyword('break');
// Catch the very common case first: immediately a semicolon (U+003B).
if (source.charCodeAt(lastIndex) === 0x3B) {
lex();
if (!(state.inIteration || state.inSwitch)) {
throwError(Messages.IllegalBreak);
}
return node.finishBreakStatement(null);
}
if (hasLineTerminator) {
if (!(state.inIteration || state.inSwitch)) {
throwError(Messages.IllegalBreak);
}
} else if (lookahead.type === Token.Identifier) {
label = parseVariableIdentifier();
key = '$' + label.name;
if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
throwError(Messages.UnknownLabel, label.name);
}
}
consumeSemicolon();
if (label === null && !(state.inIteration || state.inSwitch)) {
throwError(Messages.IllegalBreak);
}
return node.finishBreakStatement(label);
}
// ECMA-262 13.10 The return statement
function parseReturnStatement(node) {
var argument = null;
expectKeyword('return');
if (!state.inFunctionBody) {
tolerateError(Messages.IllegalReturn);
}
// 'return' followed by a space and an identifier is very common.
if (source.charCodeAt(lastIndex) === 0x20) {
if (isIdentifierStart(source.charCodeAt(lastIndex + 1))) {
argument = parseExpression();
consumeSemicolon();
return node.finishReturnStatement(argument);
}
}
if (hasLineTerminator) {
// HACK
return node.finishReturnStatement(null);
}
if (!match(';')) {
if (!match('}') && lookahead.type !== Token.EOF) {
argument = parseExpression();
}
}
consumeSemicolon();
return node.finishReturnStatement(argument);
}
// ECMA-262 13.11 The with statement
function parseWithStatement(node) {
var object, body;
if (strict) {
tolerateError(Messages.StrictModeWith);
}
expectKeyword('with');
expect('(');
object = parseExpression();
expect(')');
body = parseStatement();
return node.finishWithStatement(object, body);
}
// ECMA-262 13.12 The switch statement
function parseSwitchCase() {
var test, consequent = [], statement, node = new Node();
if (matchKeyword('default')) {
lex();
test = null;
} else {
expectKeyword('case');
test = parseExpression();
}
expect(':');
while (startIndex < length) {
if (match('}') || matchKeyword('default') || matchKeyword('case')) {
break;
}
statement = parseStatementListItem();
consequent.push(statement);
}
return node.finishSwitchCase(test, consequent);
}
function parseSwitchStatement(node) {
var discriminant, cases, clause, oldInSwitch, defaultFound;
expectKeyword('switch');
expect('(');
discriminant = parseExpression();
expect(')');
expect('{');
cases = [];
if (match('}')) {
lex();
return node.finishSwitchStatement(discriminant, cases);
}
oldInSwitch = state.inSwitch;
state.inSwitch = true;
defaultFound = false;
while (startIndex < length) {
if (match('}')) {
break;
}
clause = parseSwitchCase();
if (clause.test === null) {
if (defaultFound) {
throwError(Messages.MultipleDefaultsInSwitch);
}
defaultFound = true;
}
cases.push(clause);
}
state.inSwitch = oldInSwitch;
expect('}');
return node.finishSwitchStatement(discriminant, cases);
}
// ECMA-262 13.14 The throw statement
function parseThrowStatement(node) {
var argument;
expectKeyword('throw');
if (hasLineTerminator) {
throwError(Messages.NewlineAfterThrow);
}
argument = parseExpression();
consumeSemicolon();
return node.finishThrowStatement(argument);
}
// ECMA-262 13.15 The try statement
function parseCatchClause() {
var param, params = [], paramMap = {}, key, i, body, node = new Node();
expectKeyword('catch');
expect('(');
if (match(')')) {
throwUnexpectedToken(lookahead);
}
param = parsePattern(params);
for (i = 0; i < params.length; i++) {
key = '$' + params[i].value;
if (Object.prototype.hasOwnProperty.call(paramMap, key)) {
tolerateError(Messages.DuplicateBinding, params[i].value);
}
paramMap[key] = true;
}
// ECMA-262 12.14.1
if (strict && isRestrictedWord(param.name)) {
tolerateError(Messages.StrictCatchVariable);
}
expect(')');
body = parseBlock();
return node.finishCatchClause(param, body);
}
function parseTryStatement(node) {
var block, handler = null, finalizer = null;
expectKeyword('try');
block = parseBlock();
if (matchKeyword('catch')) {
handler = parseCatchClause();
}
if (matchKeyword('finally')) {
lex();
finalizer = parseBlock();
}
if (!handler && !finalizer) {
throwError(Messages.NoCatchOrFinally);
}
return node.finishTryStatement(block, handler, finalizer);
}
// ECMA-262 13.16 The debugger statement
function parseDebuggerStatement(node) {
expectKeyword('debugger');
consumeSemicolon();
return node.finishDebuggerStatement();
}
// 13 Statements
function parseStatement() {
var type = lookahead.type,
expr,
labeledBody,
key,
node;
if (type === Token.EOF) {
throwUnexpectedToken(lookahead);
}
if (type === Token.Punctuator && lookahead.value === '{') {
return parseBlock();
}
isAssignmentTarget = isBindingElement = true;
node = new Node();
if (type === Token.Punctuator) {
switch (lookahead.value) {
case ';':
return parseEmptyStatement(node);
case '(':
return parseExpressionStatement(node);
default:
break;
}
} else if (type === Token.Keyword) {
switch (lookahead.value) {
case 'break':
return parseBreakStatement(node);
case 'continue':
return parseContinueStatement(node);
case 'debugger':
return parseDebuggerStatement(node);
case 'do':
return parseDoWhileStatement(node);
case 'for':
return parseForStatement(node);
case 'function':
return parseFunctionDeclaration(node);
case 'if':
return parseIfStatement(node);
case 'return':
return parseReturnStatement(node);
case 'switch':
return parseSwitchStatement(node);
case 'throw':
return parseThrowStatement(node);
case 'try':
return parseTryStatement(node);
case 'var':
return parseVariableStatement(node);
case 'while':
return parseWhileStatement(node);
case 'with':
return parseWithStatement(node);
default:
break;
}
}
expr = parseExpression();
// ECMA-262 12.12 Labelled Statements
if ((expr.type === Syntax.Identifier) && match(':')) {
lex();
key = '$' + expr.name;
if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
throwError(Messages.Redeclaration, 'Label', expr.name);
}
state.labelSet[key] = true;
labeledBody = parseStatement();
delete state.labelSet[key];
return node.finishLabeledStatement(expr, labeledBody);
}
consumeSemicolon();
return node.finishExpressionStatement(expr);
}
// ECMA-262 14.1 Function Definition
function parseFunctionSourceElements() {
var statement, body = [], token, directive, firstRestricted,
oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesisCount,
node = new Node();
expect('{');
while (startIndex < length) {
if (lookahead.type !== Token.StringLiteral) {
break;
}
token = lookahead;
statement = parseStatementListItem();
body.push(statement);
if (statement.expression.type !== Syntax.Literal) {
// this is not directive
break;
}
directive = source.slice(token.start + 1, token.end - 1);
if (directive === 'use strict') {
strict = true;
if (firstRestricted) {
tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral);
}
} else {
if (!firstRestricted && token.octal) {
firstRestricted = token;
}
}
}
oldLabelSet = state.labelSet;
oldInIteration = state.inIteration;
oldInSwitch = state.inSwitch;
oldInFunctionBody = state.inFunctionBody;
oldParenthesisCount = state.parenthesizedCount;
state.labelSet = {};
state.inIteration = false;
state.inSwitch = false;
state.inFunctionBody = true;
state.parenthesizedCount = 0;
while (startIndex < length) {
if (match('}')) {
break;
}
body.push(parseStatementListItem());
}
expect('}');
state.labelSet = oldLabelSet;
state.inIteration = oldInIteration;
state.inSwitch = oldInSwitch;
state.inFunctionBody = oldInFunctionBody;
state.parenthesizedCount = oldParenthesisCount;
return node.finishBlockStatement(body);
}
function validateParam(options, param, name) {
var key = '$' + name;
if (strict) {
if (isRestrictedWord(name)) {
options.stricted = param;
options.message = Messages.StrictParamName;
}
if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) {
options.stricted = param;
options.message = Messages.StrictParamDupe;
}
} else if (!options.firstRestricted) {
if (isRestrictedWord(name)) {
options.firstRestricted = param;
options.message = Messages.StrictParamName;
} else if (isStrictModeReservedWord(name)) {
options.firstRestricted = param;
options.message = Messages.StrictReservedWord;
} else if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) {
options.stricted = param;
options.message = Messages.StrictParamDupe;
}
}
options.paramSet[key] = true;
}
function parseParam(options) {
var token, param, params = [], i, def;
token = lookahead;
if (token.value === '...') {
param = parseRestElement(params);
validateParam(options, param.argument, param.argument.name);
options.params.push(param);
options.defaults.push(null);
return false;
}
param = parsePatternWithDefault(params);
for (i = 0; i < params.length; i++) {
validateParam(options, params[i], params[i].value);
}
if (param.type === Syntax.AssignmentPattern) {
def = param.right;
param = param.left;
++options.defaultCount;
}
options.params.push(param);
options.defaults.push(def);
return !match(')');
}
function parseParams(firstRestricted) {
var options;
options = {
params: [],
defaultCount: 0,
defaults: [],
firstRestricted: firstRestricted
};
expect('(');
if (!match(')')) {
options.paramSet = {};
while (startIndex < length) {
if (!parseParam(options)) {
break;
}
expect(',');
}
}
expect(')');
if (options.defaultCount === 0) {
options.defaults = [];
}
return {
params: options.params,
defaults: options.defaults,
stricted: options.stricted,
firstRestricted: options.firstRestricted,
message: options.message
};
}
function parseFunctionDeclaration(node, identifierIsOptional) {
var id = null, params = [], defaults = [], body, token, stricted, tmp, firstRestricted, message, previousStrict,
isGenerator, previousAllowYield;
previousAllowYield = state.allowYield;
expectKeyword('function');
isGenerator = match('*');
if (isGenerator) {
lex();
}
if (!identifierIsOptional || !match('(')) {
token = lookahead;
id = parseVariableIdentifier();
if (strict) {
if (isRestrictedWord(token.value)) {
tolerateUnexpectedToken(token, Messages.StrictFunctionName);
}
} else {
if (isRestrictedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictFunctionName;
} else if (isStrictModeReservedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictReservedWord;
}
}
}
state.allowYield = !isGenerator;
tmp = parseParams(firstRestricted);
params = tmp.params;
defaults = tmp.defaults;
stricted = tmp.stricted;
firstRestricted = tmp.firstRestricted;
if (tmp.message) {
message = tmp.message;
}
previousStrict = strict;
body = parseFunctionSourceElements();
if (strict && firstRestricted) {
throwUnexpectedToken(firstRestricted, message);
}
if (strict && stricted) {
tolerateUnexpectedToken(stricted, message);
}
strict = previousStrict;
state.allowYield = previousAllowYield;
return node.finishFunctionDeclaration(id, params, defaults, body, isGenerator);
}
function parseFunctionExpression() {
var token, id = null, stricted, firstRestricted, message, tmp,
params = [], defaults = [], body, previousStrict, node = new Node(),
isGenerator, previousAllowYield;
previousAllowYield = state.allowYield;
expectKeyword('function');
isGenerator = match('*');
if (isGenerator) {
lex();
}
state.allowYield = !isGenerator;
if (!match('(')) {
token = lookahead;
id = (!strict && !isGenerator && matchKeyword('yield')) ? parseNonComputedProperty() : parseVariableIdentifier();
if (strict) {
if (isRestrictedWord(token.value)) {
tolerateUnexpectedToken(token, Messages.StrictFunctionName);
}
} else {
if (isRestrictedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictFunctionName;
} else if (isStrictModeReservedWord(token.value)) {
firstRestricted = token;
message = Messages.StrictReservedWord;
}
}
}
tmp = parseParams(firstRestricted);
params = tmp.params;
defaults = tmp.defaults;
stricted = tmp.stricted;
firstRestricted = tmp.firstRestricted;
if (tmp.message) {
message = tmp.message;
}
previousStrict = strict;
body = parseFunctionSourceElements();
if (strict && firstRestricted) {
throwUnexpectedToken(firstRestricted, message);
}
if (strict && stricted) {
tolerateUnexpectedToken(stricted, message);
}
strict = previousStrict;
state.allowYield = previousAllowYield;
return node.finishFunctionExpression(id, params, defaults, body, isGenerator);
}
// ECMA-262 14.5 Class Definitions
function parseClassBody() {
var classBody, token, isStatic, hasConstructor = false, body, method, computed, key;
classBody = new Node();
expect('{');
body = [];
while (!match('}')) {
if (match(';')) {
lex();
} else {
method = new Node();
token = lookahead;
isStatic = false;
computed = match('[');
if (match('*')) {
lex();
} else {
key = parseObjectPropertyKey();
if (key.name === 'static' && (lookaheadPropertyName() || match('*'))) {
token = lookahead;
isStatic = true;
computed = match('[');
if (match('*')) {
lex();
} else {
key = parseObjectPropertyKey();
}
}
}
method = tryParseMethodDefinition(token, key, computed, method);
if (method) {
method['static'] = isStatic; // jscs:ignore requireDotNotation
if (method.kind === 'init') {
method.kind = 'method';
}
if (!isStatic) {
if (!method.computed && (method.key.name || method.key.value.toString()) === 'constructor') {
if (method.kind !== 'method' || !method.method || method.value.generator) {
throwUnexpectedToken(token, Messages.ConstructorSpecialMethod);
}
if (hasConstructor) {
throwUnexpectedToken(token, Messages.DuplicateConstructor);
} else {
hasConstructor = true;
}
method.kind = 'constructor';
}
} else {
if (!method.computed && (method.key.name || method.key.value.toString()) === 'prototype') {
throwUnexpectedToken(token, Messages.StaticPrototype);
}
}
method.type = Syntax.MethodDefinition;
delete method.method;
delete method.shorthand;
body.push(method);
} else {
throwUnexpectedToken(lookahead);
}
}
}
lex();
return classBody.finishClassBody(body);
}
function parseClassDeclaration(identifierIsOptional) {
var id = null, superClass = null, classNode = new Node(), classBody, previousStrict = strict;
strict = true;
expectKeyword('class');
if (!identifierIsOptional || lookahead.type === Token.Identifier) {
id = parseVariableIdentifier();
}
if (matchKeyword('extends')) {
lex();
superClass = isolateCoverGrammar(parseLeftHandSideExpressionAllowCall);
}
classBody = parseClassBody();
strict = previousStrict;
return classNode.finishClassDeclaration(id, superClass, classBody);
}
function parseClassExpression() {
var id = null, superClass = null, classNode = new Node(), classBody, previousStrict = strict;
strict = true;
expectKeyword('class');
if (lookahead.type === Token.Identifier) {
id = parseVariableIdentifier();
}
if (matchKeyword('extends')) {
lex();
superClass = isolateCoverGrammar(parseLeftHandSideExpressionAllowCall);
}
classBody = parseClassBody();
strict = previousStrict;
return classNode.finishClassExpression(id, superClass, classBody);
}
// ECMA-262 15.2 Modules
function parseModuleSpecifier() {
var node = new Node();
if (lookahead.type !== Token.StringLiteral) {
throwError(Messages.InvalidModuleSpecifier);
}
return node.finishLiteral(lex());
}
// ECMA-262 15.2.3 Exports
function parseExportSpecifier() {
var exported, local, node = new Node(), def;
if (matchKeyword('default')) {
// export {default} from 'something';
def = new Node();
lex();
local = def.finishIdentifier('default');
} else {
local = parseVariableIdentifier();
}
if (matchContextualKeyword('as')) {
lex();
exported = parseNonComputedProperty();
}
return node.finishExportSpecifier(local, exported);
}
function parseExportNamedDeclaration(node) {
var declaration = null,
isExportFromIdentifier,
src = null, specifiers = [];
// non-default export
if (lookahead.type === Token.Keyword) {
// covers:
// export var f = 1;
switch (lookahead.value) {
case 'let':
case 'const':
declaration = parseLexicalDeclaration({inFor: false});
return node.finishExportNamedDeclaration(declaration, specifiers, null);
case 'var':
case 'class':
case 'function':
declaration = parseStatementListItem();
return node.finishExportNamedDeclaration(declaration, specifiers, null);
}
}
expect('{');
while (!match('}')) {
isExportFromIdentifier = isExportFromIdentifier || matchKeyword('default');
specifiers.push(parseExportSpecifier());
if (!match('}')) {
expect(',');
if (match('}')) {
break;
}
}
}
expect('}');
if (matchContextualKeyword('from')) {
// covering:
// export {default} from 'foo';
// export {foo} from 'foo';
lex();
src = parseModuleSpecifier();
consumeSemicolon();
} else if (isExportFromIdentifier) {
// covering:
// export {default}; // missing fromClause
throwError(lookahead.value ?
Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
} else {
// cover
// export {foo};
consumeSemicolon();
}
return node.finishExportNamedDeclaration(declaration, specifiers, src);
}
function parseExportDefaultDeclaration(node) {
var declaration = null,
expression = null;
// covers:
// export default ...
expectKeyword('default');
if (matchKeyword('function')) {
// covers:
// export default function foo () {}
// export default function () {}
declaration = parseFunctionDeclaration(new Node(), true);
return node.finishExportDefaultDeclaration(declaration);
}
if (matchKeyword('class')) {
declaration = parseClassDeclaration(true);
return node.finishExportDefaultDeclaration(declaration);
}
if (matchContextualKeyword('from')) {
throwError(Messages.UnexpectedToken, lookahead.value);
}
// covers:
// export default {};
// export default [];
// export default (1 + 2);
if (match('{')) {
expression = parseObjectInitializer();
} else if (match('[')) {
expression = parseArrayInitializer();
} else {
expression = parseAssignmentExpression();
}
consumeSemicolon();
return node.finishExportDefaultDeclaration(expression);
}
function parseExportAllDeclaration(node) {
var src;
// covers:
// export * from 'foo';
expect('*');
if (!matchContextualKeyword('from')) {
throwError(lookahead.value ?
Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
}
lex();
src = parseModuleSpecifier();
consumeSemicolon();
return node.finishExportAllDeclaration(src);
}
function parseExportDeclaration() {
var node = new Node();
if (state.inFunctionBody) {
throwError(Messages.IllegalExportDeclaration);
}
expectKeyword('export');
if (matchKeyword('default')) {
return parseExportDefaultDeclaration(node);
}
if (match('*')) {
return parseExportAllDeclaration(node);
}
return parseExportNamedDeclaration(node);
}
// ECMA-262 15.2.2 Imports
function parseImportSpecifier() {
// import {<foo as bar>} ...;
var local, imported, node = new Node();
imported = parseNonComputedProperty();
if (matchContextualKeyword('as')) {
lex();
local = parseVariableIdentifier();
}
return node.finishImportSpecifier(local, imported);
}
function parseNamedImports() {
var specifiers = [];
// {foo, bar as bas}
expect('{');
while (!match('}')) {
specifiers.push(parseImportSpecifier());
if (!match('}')) {
expect(',');
if (match('}')) {
break;
}
}
}
expect('}');
return specifiers;
}
function parseImportDefaultSpecifier() {
// import <foo> ...;
var local, node = new Node();
local = parseNonComputedProperty();
return node.finishImportDefaultSpecifier(local);
}
function parseImportNamespaceSpecifier() {
// import <* as foo> ...;
var local, node = new Node();
expect('*');
if (!matchContextualKeyword('as')) {
throwError(Messages.NoAsAfterImportNamespace);
}
lex();
local = parseNonComputedProperty();
return node.finishImportNamespaceSpecifier(local);
}
function parseImportDeclaration() {
var specifiers = [], src, node = new Node();
if (state.inFunctionBody) {
throwError(Messages.IllegalImportDeclaration);
}
expectKeyword('import');
if (lookahead.type === Token.StringLiteral) {
// import 'foo';
src = parseModuleSpecifier();
} else {
if (match('{')) {
// import {bar}
specifiers = specifiers.concat(parseNamedImports());
} else if (match('*')) {
// import * as foo
specifiers.push(parseImportNamespaceSpecifier());
} else if (isIdentifierName(lookahead) && !matchKeyword('default')) {
// import foo
specifiers.push(parseImportDefaultSpecifier());
if (match(',')) {
lex();
if (match('*')) {
// import foo, * as foo
specifiers.push(parseImportNamespaceSpecifier());
} else if (match('{')) {
// import foo, {bar}
specifiers = specifiers.concat(parseNamedImports());
} else {
throwUnexpectedToken(lookahead);
}
}
} else {
throwUnexpectedToken(lex());
}
if (!matchContextualKeyword('from')) {
throwError(lookahead.value ?
Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
}
lex();
src = parseModuleSpecifier();
}
consumeSemicolon();
return node.finishImportDeclaration(specifiers, src);
}
// ECMA-262 15.1 Scripts
function parseScriptBody() {
var statement, body = [], token, directive, firstRestricted;
while (startIndex < length) {
token = lookahead;
if (token.type !== Token.StringLiteral) {
break;
}
statement = parseStatementListItem();
body.push(statement);
if (statement.expression.type !== Syntax.Literal) {
// this is not directive
break;
}
directive = source.slice(token.start + 1, token.end - 1);
if (directive === 'use strict') {
strict = true;
if (firstRestricted) {
tolerateUnexpectedToken(firstRestricted, Messages.StrictOctalLiteral);
}
} else {
if (!firstRestricted && token.octal) {
firstRestricted = token;
}
}
}
while (startIndex < length) {
statement = parseStatementListItem();
/* istanbul ignore if */
if (typeof statement === 'undefined') {
break;
}
body.push(statement);
}
return body;
}
function parseProgram() {
var body, node;
peek();
node = new Node();
body = parseScriptBody();
return node.finishProgram(body, state.sourceType);
}
function filterTokenLocation() {
var i, entry, token, tokens = [];
for (i = 0; i < extra.tokens.length; ++i) {
entry = extra.tokens[i];
token = {
type: entry.type,
value: entry.value
};
if (entry.regex) {
token.regex = {
pattern: entry.regex.pattern,
flags: entry.regex.flags
};
}
if (extra.range) {
token.range = entry.range;
}
if (extra.loc) {
token.loc = entry.loc;
}
tokens.push(token);
}
extra.tokens = tokens;
}
function tokenize(code, options, delegate) {
var toString,
tokens;
toString = String;
if (typeof code !== 'string' && !(code instanceof String)) {
code = toString(code);
}
source = code;
index = 0;
lineNumber = (source.length > 0) ? 1 : 0;
lineStart = 0;
startIndex = index;
startLineNumber = lineNumber;
startLineStart = lineStart;
length = source.length;
lookahead = null;
state = {
allowIn: true,
allowYield: true,
labelSet: {},
inFunctionBody: false,
inIteration: false,
inSwitch: false,
lastCommentStart: -1,
curlyStack: []
};
extra = {};
// Options matching.
options = options || {};
// Of course we collect tokens here.
options.tokens = true;
extra.tokens = [];
extra.tokenValues = [];
extra.tokenize = true;
extra.delegate = delegate;
// The following two fields are necessary to compute the Regex tokens.
extra.openParenToken = -1;
extra.openCurlyToken = -1;
extra.range = (typeof options.range === 'boolean') && options.range;
extra.loc = (typeof options.loc === 'boolean') && options.loc;
if (typeof options.comment === 'boolean' && options.comment) {
extra.comments = [];
}
if (typeof options.tolerant === 'boolean' && options.tolerant) {
extra.errors = [];
}
try {
peek();
if (lookahead.type === Token.EOF) {
return extra.tokens;
}
lex();
while (lookahead.type !== Token.EOF) {
try {
lex();
} catch (lexError) {
if (extra.errors) {
recordError(lexError);
// We have to break on the first error
// to avoid infinite loops.
break;
} else {
throw lexError;
}
}
}
tokens = extra.tokens;
if (typeof extra.errors !== 'undefined') {
tokens.errors = extra.errors;
}
} catch (e) {
throw e;
} finally {
extra = {};
}
return tokens;
}
function parse(code, options) {
var program, toString;
toString = String;
if (typeof code !== 'string' && !(code instanceof String)) {
code = toString(code);
}
source = code;
index = 0;
lineNumber = (source.length > 0) ? 1 : 0;
lineStart = 0;
startIndex = index;
startLineNumber = lineNumber;
startLineStart = lineStart;
length = source.length;
lookahead = null;
state = {
allowIn: true,
allowYield: true,
labelSet: {},
inFunctionBody: false,
inIteration: false,
inSwitch: false,
lastCommentStart: -1,
curlyStack: [],
sourceType: 'script'
};
strict = false;
extra = {};
if (typeof options !== 'undefined') {
extra.range = (typeof options.range === 'boolean') && options.range;
extra.loc = (typeof options.loc === 'boolean') && options.loc;
extra.attachComment = (typeof options.attachComment === 'boolean') && options.attachComment;
if (extra.loc && options.source !== null && options.source !== undefined) {
extra.source = toString(options.source);
}
if (typeof options.tokens === 'boolean' && options.tokens) {
extra.tokens = [];
}
if (typeof options.comment === 'boolean' && options.comment) {
extra.comments = [];
}
if (typeof options.tolerant === 'boolean' && options.tolerant) {
extra.errors = [];
}
if (extra.attachComment) {
extra.range = true;
extra.comments = [];
extra.bottomRightStack = [];
extra.trailingComments = [];
extra.leadingComments = [];
}
if (options.sourceType === 'module') {
// very restrictive condition for now
state.sourceType = options.sourceType;
strict = true;
}
}
try {
program = parseProgram();
if (typeof extra.comments !== 'undefined') {
program.comments = extra.comments;
}
if (typeof extra.tokens !== 'undefined') {
filterTokenLocation();
program.tokens = extra.tokens;
}
if (typeof extra.errors !== 'undefined') {
program.errors = extra.errors;
}
} catch (e) {
throw e;
} finally {
extra = {};
}
return program;
}
// Sync with *.json manifests.
exports.version = '2.7.0';
exports.tokenize = tokenize;
exports.parse = parse;
// Deep copy.
/* istanbul ignore next */
exports.Syntax = (function () {
var name, types = {};
if (typeof Object.create === 'function') {
types = Object.create(null);
}
for (name in Syntax) {
if (Syntax.hasOwnProperty(name)) {
types[name] = Syntax[name];
}
}
if (typeof Object.freeze === 'function') {
Object.freeze(types);
}
return types;
}());
}));
/* vim: set sw=4 ts=4 et tw=80 : */
};
BundleModuleCode['jam/sandbox']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 1-1-17 by sbosse.
** $RCS: $Id: sandbox.js,v 1.2 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.6.2
**
** $INFO:
**
** JavaScript AIOS Sandbox Function Constructor
**
** Two version: (1) with eval (2) with new Function
** Evaluated code is sometimes slower than a constructed function, though with(mask){} is a performance
* leak, too.
**
** $ENDOFINFO
*/
var Json = Require('jam/jsonfn');
/** Returns a new function f' without global references
* except them provided in modules, and the masked environemnt.
* An optional checkpointing function call cp can be optionally injected.
*
* typeof @inject = {cp?:string,rt?:string}
* with cp is a checkpointing functions, rt is an exception catcher rethrower
*/
function sandbox(f,modules,inject,env)
{
var p,
F,
mask,
source;
// set up an object to serve as the context for the code
// being evaluated.
mask = {mask:undefined,modules:undefined,require:undefined,cp:undefined,f:undefined};
// mask global properties
for (p in this)
mask[p] = undefined;
for (p in modules)
mask[p]=modules[p];
if (env) for (p in env)
mask[p]=env[p];
// execute script in private context
// (new Function( "with(this) { " + scr + "}")).call(mask);
if (typeof f == 'function') source = f.toString(true); // try minification (true) if supported
else source=f;
if (inject.cp) {
// CP injection
var regex1= /while[\s]*\(([^\)]+)\)/g;
var regex2= /for[\s]*\(([^\)]+)\)/g;
var regex3= /function([^\{]+)\{/g;
source=source.replace(regex1,"while (($1) && "+inject.cp+"())")
.replace(regex2,"for ($1,"+inject.cp+"())")
.replace(regex3,"function $1{"+inject.cp+"();");
}
if (inject.rt) {
var regex4 = /catch[\s]*\([\s]*([a-zA-Z0-9_]+)[\s]*\)[\s]*\{/g;
source=source.replace(regex4,'catch ($1) {'+inject.rt+'($1);');
}
function evalInContext(context, js) {
return eval('with(context) { "use strict"; F=' + js + ' }');
}
// with (mask) {
// eval('"use strict"; F='+source);
//}
mask.eval=undefined;
evalInContext(mask,source);
return {fun:F,mask:mask,size:source.length};
}
/** Returns a new function f' without global references
* except them provided in modules, and the masked environemnt.
* An optional checkpointing function call cp can be optionally injected.
*
* typeof @inject = {cp?:string,rt?:string}
* with cp is a checkpointing functions, rt is an exception catcher rethrower
*/
function Sandbox(f,modules,inject,env) {
var p,mask={},_mask='process';
for(p in global) {
if (p.indexOf('Array')>0) continue;
_mask = _mask + ',' + p;
}
for (p in modules)
mask[p]=modules[p];
if (env) for (p in env)
mask[p]=env[p];
if (typeof f == 'function') source = f.toString(true); // try minification (true) if supported
else source=f;
if (inject.cp) {
// CP injection
var regex1= /while[\s]*\(([^\)]+)\)/g;
var regex2= /for[\s]*\(([^\)]+)\)/g;
var regex3= /function([^\{]+)\{/g;
source=source.replace(regex1,"while (($1) && "+inject.cp+"())")
.replace(regex2,"for ($1,"+inject.cp+"())")
.replace(regex3,"function $1{"+inject.cp+"();");
}
if (inject.rt) {
var regex4 = /catch[\s]*\([\s]*([a-zA-Z0-9_]+)[\s]*\)[\s]*\{/g;
source=source.replace(regex4,'catch ($1) {'+inject.rt+'($1);');
}
mask.eval=undefined;_mask += ',eval'
mask.require=undefined;_mask += ',require'
var F = new Function(_mask,'"use strict"; with(this) { f=('+source+').bind(this)} return f')
.bind(mask);
return {fun:F(),mask:mask,_mask:_mask};
}
function sandboxObject(obj,mask) {
var objS;
if (typeof obj == 'object') obj=Json.stringify(obj);
return Json.parse(obj,mask);
}
module.exports = {
sandbox:sandbox,
sandboxObject:sandboxObject,
Sandbox:Sandbox
}
module.exports = function () {return sandbox}
};
BundleModuleCode['jam/sig']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2024 bLAB
** $CREATED: 15/1/16 by sbosse.
** $RCS: $Id: sig.js,v 1.4 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.6.1
**
** $INFO:
**
** JavaScript AIOS Agent Signal Sub-System
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios = none;
var options = {
debug : {},
version:'1.5.3'
}
/** Search a channel that is connected to node 'destnode'
*
*/
function lookup(node,destnode) {
var chan,url;
if (node.connections.ip && node.connections.ip.lookup) {
url=node.connections.ip.lookup(destnode);
if (url) return {
chan:node.connections.ip,
url:url
};
}
// (sejam2) virtual connections
if (node.connections.path && node.connections.path._connected) {
if (node.connections.path._connected.indexOf(destnode)!=-1) {
return {
chan:node.connections.path,
url:Aios.DIR.NODE(destnode)
}
}
}
}
var sig = {
broadcast: function (ac,range,sig,arg) {
var delivered=0;
// Currently only range=0 is supported => local agents
if (!Comp.obj.isString(ac)) {current.error='broadcast, invalid class '+ac;throw 'SIGNAL'};
if (!Comp.obj.isString(sig) && !Comp.obj.isNumber(sig)) {current.error='broadcast, invalid signal '+sig;throw 'SIGNAL'};
if (range!=0) {current.error='broadcast, invalid range '+range;throw 'SIGNAL'};
for (var p in current.node.processes.table) {
var proc=current.node.processes.table[p];
if (proc && proc.agent.ac == ac && proc.agent.on[sig]) {
proc.signals.push([sig,arg,current.process.agent.id]);
delivered++;
}
}
return delivered;
},
// 'to' is the destination agent id
// 'from' indicates source agent id and remote signal propagation (from node.handle)
send: function (to,sig,arg,from,hop) {
var p,node,delivered,curtime;
// Local agent?
var pid=current.node.processes.lookup(to);
if (options.debug.send) console.log('sig.send',to,sig,arg,from,hop,pid);
if (!Comp.obj.isString(sig) && !Comp.obj.isNumber(sig)) {current.error='send, invalid signal';throw 'SIGNAL'};
current.node.stats.signal++;
if (pid!=none) {
// [sig,arg,from]
// Check AIOS signals:
switch (sig) {
case 'PROC.KILL':
Aios.kill(to);
return true;
}
current.node.processes.table[pid].signals.push([sig,arg,from||current.process.agent.id]);
// ?? Aios.emit('schedule',current.node);
return true;
} else {
// console.log('send',current.node.id,to,sig,arg,current.node.processes.gone[to])
// Agent migrated and still cached?
if (current.node.processes.gone[to]) {
if (options.debug.send) print('sig.send',to,sig,arg,from,hop,current.node.processes.gone[to].dir);
curtime=Aios.time()-current.world.lag;
// path is in use; update timeout significantly to avoid a lost of the signal path
current.node.processes.gone[to].timeout = curtime + current.node.TMO*10;
return route(current.node.processes.gone[to].dir,
to,sig,arg,from||current.process.agent.id);
}
// coupled nodes via grouping? (used in virtual world/simulation)
// Prevent children-parent ping-pong if agent was not found
if (hop>2) return true;
if (current.node.parent) {
return current.node.parent.handle({to:to,sig:sig,arg:arg,from:from,hop:hop?hop+1:1})
} else if (current.node.children) {
delivered=false;
for(p in current.node.children) {
node=current.node.children[p];
delivered=node.handle({to:to,sig:sig,arg:arg,from:from,hop:hop?hop+1:1});
if (delivered) break;
}
if (delivered) return true;
}
if (current.node.signals[to]) {
curtime=Aios.time()-current.world.lag;
// path is in use; update timeout significantly to avoid a lost of the signal path
current.node.signals[to].timeout = curtime + current.node.TMO*10;
return route(current.node.signals[to].dir,
to,sig,arg,from||current.process.agent.id);
}
}
return false;
},
// Send a signal to agents on a specific remote destination node, e.g., to=DIR.DELTA([-1,-2])
sendto: function (to,sig,arg,from,node) {
var delivered=0,i;
if (!Comp.obj.isString(sig) && !Comp.obj.isNumber(sig)) {current.error='sendto, invalid signal '+sig;throw 'SIGNAL'};
if ((to.tag||to).indexOf('DIR') != 0) {current.error='sendto, invalid destination '+to; throw 'SIGNAL'};
// console.debug('sendto',to,sig,arg,from,node,current.node.id)
if (to == Aios.DIR.ORIGIN || (to.delta && Comp.array.zero(to.delta) || to.node==current.node.id)) {
if (sig=='TS.SIG') {
// copy/collect from remote TS
for(i in arg) {
Aios.Ts.agent.out(arg[i]);
}
} else for (var p in current.node.processes.table) {
var proc=current.node.processes.table[p];
if (proc && proc.agent.on && proc.agent.on[sig]) {
proc.signals.push([sig,arg,from||current.process.agent.id,node]);
delivered++;
}
}
return delivered;
} else {
return route(to,
none,sig,arg,current.process.agent.id,node||current.node.id);
}
},
sleep: function (tmo) {
current.process.suspend(tmo?Aios.time()-current.world.lag+tmo:0);
},
// Returns signal name
timer: {
// Add a oneshot or repeating timer raising a signal 'sig' after timeout 'tmo'.
add : function (tmo,sig,arg,repeat) {
if (!Comp.obj.isNumber(tmo)) {current.error='timer, invalid timeout '+tmo; throw 'SIGNAL'};
if (!Comp.obj.isString(sig)) {current.error='timer, invalid signal '+sig; throw 'SIGNAL'};
current.node.timers.push([current.process,(Aios.time()-current.world.lag+tmo),sig,arg,repeat?tmo:0]);
return sig;
},
delete: function (sig) {
current.node.timers=current.node.timers.filter(function (t) {
return t[2]!=sig
});
}
},
// return process timeout (absolute time)
timeout: function (tmo) { return Aios.time()-current.world.lag+tmo },
wakeup: function (process) {
if (!process) current.process.wakeup();
else process.wakeup();
}
}
/** Route signal to next node
* from:agentid, node:source nodeid
*/
function route(dir,to,sig,arg,from,node) {
var node1=current.node,
chan=none,
dest,
stat,
alive = function () {return 1},
sigobj = {sig:sig,to:to||dir,from:from,arg:arg,back:Aios.DIR.opposite(dir,true),node:node},
msg;
// console.debug('route',dir,to,sig,arg,from,node)
switch (dir.tag||dir) {
case Aios.DIR.NORTH: chan=node1.connections.north; break;
case Aios.DIR.SOUTH: chan=node1.connections.south; break;
case Aios.DIR.WEST: chan=node1.connections.west; break;
case Aios.DIR.EAST: chan=node1.connections.east; break;
case Aios.DIR.UP: chan=node1.connections.up; break;
case Aios.DIR.DOWN: chan=node1.connections.down; break;
case Aios.DIR.NW: chan=node1.connections.nw; break;
case Aios.DIR.NE: chan=node1.connections.ne; break;
case Aios.DIR.SE: chan=node1.connections.se; break;
case Aios.DIR.SW: chan=node1.connections.sw; break;
case 'DIR.IP': chan=node1.connections.ip; dest=dir.ip; break;
case 'DIR.DELTA':
// Simple Delta routing: Minimize [x,y,..] -> [0,0,..] with {x,y,..}
sigobj.to=Comp.obj.copy(sigobj.to);
if (dir.delta[0]>0 && node1.connections.east && node1.connections.east.status())
sigobj.to.delta[0]--,chan=node1.connections.east;
else if (dir.delta[0]<0 && node1.connections.west && node1.connections.west.status())
sigobj.to.delta[0]++,chan=node1.connections.west;
else if (dir.delta[1]>0 && node1.connections.south && node1.connections.south.status())
sigobj.to.delta[1]--,chan=node1.connections.south;
else if (dir.delta[1]<0 && node1.connections.north && node1.connections.north.status())
sigobj.to.delta[1]++,chan=node1.connections.north;
else if (dir.delta[2]>0 && node1.connections.up && node1.connections.up.status())
sigobj.to.delta[2]--,chan=node1.connections.up;
else if (dir.delta[2]<0 && node1.connections.down && node1.connections.down.status())
sigobj.to.delta[2]++,chan=node1.connections.down;
break;
case 'DIR.PATH':
chan=node1.connections.path; dest=dir.path;
break;
case 'DIR.CAP':
if (!current.network) {current.error='No connection to server '+dir.cap; return false;};
chan=node1.connections.dos; dest=Net.Parse.capability(dir.cap).cap;
break;
case 'DIR.NODE':
if (node1.connections.range &&
node1.connections.range[dir.node] &&
node1.connections.range[dir.node].status())
chan=node1.connections.range[dir.node],dest=dir.node;
else {
// Find node name -> channel mapping
dest=lookup(node1,dir.node);
// console.debug('route',dir.node,node1,dest)
if (dest) chan=dest.chan,dest=dest.url;
}
break;
default: return false;
}
switch (dir.tag||dir) {
// One hop to next neighbour only?
case Aios.DIR.NORTH:
case Aios.DIR.SOUTH:
case Aios.DIR.WEST:
case Aios.DIR.EAST:
case Aios.DIR.UP:
case Aios.DIR.DOWN:
case Aios.DIR.NW:
case Aios.DIR.NE:
case Aios.DIR.SE:
case Aios.DIR.SW:
sigobj.to=Aios.DIR.ORIGIN; // After messaging signal has arrived
break;
}
if (options.debug.route) console.log('sig.route',node1.id,dir,sigobj,chan!=null);
if (chan==none || !chan.status(dest) /* OLDCOMM || !chan.signal*/) {
current.error='No connection to direction '+dir;
return false;
};
node1.stats.signal++;
if (Aios.options.fastcopy && chan.virtual) msg=sigobj;
else msg=Aios.Code.toString(sigobj);
/** OLDCOMM
chan.signal(msg,dest);
*/
/* NEWCOMM */
chan.send({signal:msg,to:dest});
return true;
}
module.exports = {
agent:sig,
options:options,
route:route,
current:function (module) { current=module.current; Aios=module; }
}
};
BundleModuleCode['jam/node']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2021 bLAB
** $CREATED: 15-1-16 by sbosse.
** $RCS: $Id: node.js,v 1.4 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.13.2
**
** $INFO:
**
** JavaScript AIOS Agent Node Sub-System
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var Security = Require('jam/security');
var current=none;
var Aios = none;
var options = {
debug:{},
verbose:0,
version:'1.13.2'
}
function aid(process) { return process.agent.id+':'+process.pid }
function min0(a,b) { return a==0?b:(b==0?a:Comp.pervasives.min(a,b)) };
/** Create a node.
* typeof options = {id,maxpro,maxts,position:{x,y},defaultLevel?,TMO?}
*
*/
var node= function (options) {
var self=this;
options=checkOptions(options,{});
this.options=options;
this.id = checkOption(this.options.id,Aios.aidgen());
this.position = checkOption(this.options.position,Aios.DIR.ORIGIN);
this.type = checkOption(this.options.type,'generic');
this.verbose = checkOption(this.options.verbose,0);
// Default AIOS privilege level for received agent snapshots
this.defaultLevel=checkOption(this.options.defaultLevel,Aios.options.LEVEL);
this.processes={
free:none,
max:checkOption(this.options.maxpro,100),
// (proc|undefined) []
table:[],
// number|undefined []
hash:[],
top:0,
used:0,
// a cache of migrated agents [id]={dir,timeout}
gone:[]
};
this.processes.lookup = function (aid) {
if(self.processes.hash[aid]!=undefined)
return self.processes.hash[aid];
else
return none;
};
this.processes.process = function (aid) {
if (self.processes.hash[aid]!=_)
return self.processes.table[self.processes.hash[aid]];
else
return none;
};
// Signal propagation cache [from]={dir:timeout}
this.signals=[];
// [agent,tmo,sig,arg]
this.timers=[];
/** Connections to other nodes using P2P/IP/DOS links
* type link = {recv: function (callback),send:function(data),
* status: function() -> bool,count:number}
*/
this.connections={north:none,south:none,west:none,east:none};
// tuple spaces
this.ts = Aios.Ts.create({maxn:checkOption(this.options.maxts,8),
id:this.id,node:self});
// Random ports for negotiation and node security
this.random = {};
this.port = Security.Port.unique();
this.random[this.port]=Security.Port.unique();
// Code dictionary shared by agents
this.library = {};
// Location (geo) and position (virtual) information
// location : { ip:string,
// gps:{lat:number, lon:number},
// geo:{city:string,country:string,countryCode:string,region:string,zip:string} }
this.location = null;
this.position = options.position;
this.stats = {
cpu:0,
create:0,
fastcopy:0,
fork:0,
handled:0,
migrate:0,
received:0,
signal:0,
error:0,
tsout:0,
tsin:0,
agents:0,
}
// Agent migration (gone) cache timeout
this.TMO = checkOption(this.options.TMO,100000);
// Needs node's service?
this.timeout = 0;
Aios.emit('node+',self);
};
/** Clean-up and destroy this node. Terminate all agent processes.
*/
node.prototype.destroy = function () {
var p,pro,_node=current.node,self=this;
this.connections={};
if (this.verbose>2) console.log('node.destroy',this)
current.node=this;
for(p in this.processes.table) {
pro=this.processes.table[p];
if (!pro) continue;
pro.kill=true;
this.unregister(pro);
}
this.processes.gone=[];
this.ts = none;
current.node=_node;
// unlink this node in an optional parent node (simulation)
if (this.parent) {
this.parent.children=this.parent.children.filter(function (node) {
return node!=self
});
}
// unlink this node from optional child nodes (simulation)
if (this.children) {
this.children.forEach(function (node) {
node.parent=null;
})
}
Aios.emit('node-',self);
}
/** Export of code library
*
*/
node.prototype.export = function (name,code) {
// copy and sandbox code
if (!this.library[name])
this.library[name]=Aios.Code.toString(code);
}
/** Find an agent of the node by it's id and/or class, or agents matching a regular expression.
*
*/
node.prototype.getAgent = function (id,ac) {
var pros=this.getAgentProcess(id,ac);
if (pros && Comp.obj.isArray(pros))
return Comp.array.map(pros,function (pro) {return pro.agent});
else if (pros) return pros.agent;
};
node.prototype.getAgentProcess = function (id,ac) {
var matches=Comp.obj.isRegex(id)?[]:undefined,
table=this.processes.table,p;
if (!matches && this.processes.hash[id]!=undefined) {
p=table[this.processes.hash[id]];
if (!ac || p.agent.ac==ac) return p;
}
if (typeof id == 'number') return table[id];
for(var p in table) {
if (!table[p]) continue;
if (!matches && table[p].agent.id==id && (!ac || table[p].agent.ac==ac))
return table[p];
if (matches && id.test(table[p].agent.id) && (!ac || table[p].agent.ac==ac))
matches.push(table[p]);
}
return matches;
};
/** Receive a signal to be passed to an agent located here or routed to another node.
* Message is in JSOB text format or a JS object (fastcopy mode).
*
* typeof sigobj = {to,sig,arg,from,back?}
* from is an agent id!
*
*/
node.prototype.handle = function (msg) {
var delivered,tmo,curtime=Aios.time()-current.world.lag,
_node=current.node,self=this,
sigobj=(typeof msg == 'string')?Aios.Code.ofString(msg,{}):msg;
if (options.debug.handle) console.log('node.handle',this.id,sigobj);
if (!sigobj) return; // Error
current.node=this;
delivered=(Aios.Mobi.DIR.isDir(sigobj.to)?Aios.Sig.agent.sendto:Aios.Sig.agent.send)
(sigobj.to,sigobj.sig,sigobj.arg,sigobj.from,sigobj.to?sigobj.node:sigobj.hop);
if (delivered && sigobj.back) {
// Update signal route cache
tmo=curtime+this.TMO;
this.signals[sigobj.from]={dir:sigobj.back,timeout:tmo};
this.timeout=min0(this.timeout,tmo);
};
this.stats.handled++;
current.node=_node;
Aios.emit('schedule',self);
return delivered;
}
/** Import code from library.
* Returns a sandboxed code copy.
*
*/
node.prototype.import = function (name) {
var code;
if (this.library[name]) code=Aios.Code.ofString(this.library[name],current.process.mask);
return code;
}
/** Get node statistics
*
*/
node.prototype.info = function () {
var self=this,
p,
obj = {};
ovj.stats = this.stats;
obj.id = this.id;
obj.position = this.position;
obj.agents={};
var update=function (obj) {
var p;
for (p in obj) {
if (p != '_update') delete obj[p];
}
for (p in self.processes.hash) {
if (self.processes.hash[p]!=_)
obj[p]=self.processes.table[self.processes.hash[p]];
};
}
obj.agents._update=update;
update(obj.agents);
obj.signals=this.signals;
obj.timers=this.timers;
obj.ts=this.ts;
obj.connections=this.connections;
return obj;
}
/** Print node statistics
*
*/
node.prototype.print = function (summary) {
var i,blocked,pending,total,ghost=0;
var str='==== NODE '+this.id+' ===='+NL;
str += 'SYSTIME='+Aios.time()+NL;
str += 'PROCESS TABLE >>'+NL;
if (summary) {
blocked=0; pending=0; total=0; ghost=0;
for (i in this.processes.table) {
if (this.processes.table[i]!=_) {
total++;
if (this.processes.table[i].blocked) blocked++;
if (this.processes.table[i].signals.length>0) pending++;
if (this.processes.table[i].agent.next==undefined) ghost++;
};
}
str += ' TOTAL='+total+' BLOCKED='+blocked+' DYING='+ghost+' SIGPEND='+pending+NL;
} else {
for (i in this.processes.table) {
if (this.processes.table[i]!=_) {
str += ' ['+aid(this.processes.table[i])+'] '+
'NEXT='+this.processes.table[i].agent.next+' '+
this.processes.table[i].print();
};
}
}
if (this.timers.length>0) {
str += 'TIMER TABLE >>'+NL;
for (i in this.timers) {
str += ' ['+aid(this.timers[i][0])+'] TMO='+this.timers[i][1]+' SIG='+this.timers[i][2]+NL;
}
}
str += 'TUPLE SPACES >>'+NL;
if (summary) str += ' '+this.ts.print(summary); else str += this.ts.print(summary);
return str;
}
/** Receive migrated agent text code and create a process container registered on this node.
* If start=false then the next activity is computed here.
*
*/
node.prototype.receive = function (msg,start,from) {
// Save context
var _process=current.process,
_node=current.node,
self=this,
process,agent;
if (options.debug.receive) console.log ('node.receive (start='+start+',length='+msg.length+'):\n<'+msg+'>');
if (typeof msg !== 'object') process=Aios.Code.toCode(msg,this.defaultLevel);
else process=Aios.Code.ofObject(msg); // Virtual migration, same physical JAM
if (!process) return; // Error
agent=process.agent;
agent['self']=agent;
this.register(process);
this.stats.received++;
if (process.dir || process.delta) {
/* TODO migration if this node is not the destination */
return;
};
if ((!process.back||process.back.tag=='DIR.IP') && from && from.address && from.port)
// update or store back path; don't use old IP entry (along extended paths), it is not reachable from here!
process.back=Aios.DIR.IP(from.address+':'+from.port);
if (process.back && process.agent.parent) { // register child-to-parent signal path
tmo=Aios.time()-current.world.lag+this.TMO;
this.signals[process.agent.parent]={dir:process.back,timeout:tmo};
this.timeout=min0(this.timeout,tmo);
}
// console.log('node.receive '+this.position.x+','+this.position.y);
if (process.schedule.length == 0) {
// Compute next activity on THIS node
current.node=this;
current.process=process;
try {
if (!start)
agent.next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent):
agent.trans[agent.next];
if (process.blocked) throw 'BLOCKING';
//console.log(agent.next);
} catch (e) {
Aios.aios.log ('Node.receive: Agent '+agent.id+' ['+agent.ac+'] in transition '+agent.next+
' failed:\n'+e+(current.error?' / '+current.error:', in: \n'+Aios.Code.print(agent.trans[agent.next]))+
'\nat:\n'+Io.sprintstack(e));
this.unregister(process);
};
// Restore context
current.node=_node;
current.process=_process;
}
}
/** Register agent code and assign a process container.
*
*/
node.prototype.register = function (process) {
var i,p,
self=this,
agent=process.agent;
if (this.processes.free==none) {
loop: for (i in this.processes.table) {
if (this.processes.table[i]==_) { this.processes.free=i; break loop};
}
}
if (this.processes.free!=none) {
this.processes.table[this.processes.free]=process;
process.pid=this.processes.free;
process.agent=agent;
this.processes.free=none;
} else {
this.processes.table[this.processes.top]=process;
process.agent=agent;
process.pid=this.processes.top;
this.processes.top++;
}
if (agent.id==undefined) agent.id=Aios.aidgen();
this.processes.hash[agent.id]=process.pid;
this.processes.used++;
this.stats.agents++;
if (this.processes.gone[process.agent.id])
// Agent returned again!
this.processes.gone[process.agent.id]=undefined;
process.node=this;
Aios.emit('agent+',{agent:agent,proc:process,node:self},self);
Aios.emit('schedule',self);
}
/** Node Garbage Collection and Timeout Service
*
*/
node.prototype.service = function (curtime) {
var nexttime=0,p,pro,sig;
// TS cleanup management
this.ts.service(curtime);
if (curtime<this.timeout) return;
for (p in this.processes.gone) {
pro=this.processes.gone[p];
if (pro==undefined) continue;
if (pro.timeout < curtime) {
this.processes.gone[p]=undefined;
}
else
nexttime=min0(nexttime,pro.timeout);
}
for (p in this.signals) {
sig=this.signals[p];
if (sig==undefined) continue;
if (sig.timeout < curtime) {
this.signals[p]=undefined;
}
else
nexttime=min0(nexttime,sig.timeout);
}
this.timeout=nexttime;
}
// RPC STD request handlers
node.prototype.std_info = function (what) {
if (what==undefined) return {
defaultLevel:this.defaultLevel,
id:this.id,
location:this.location,
position:this.position,
type:this.type,
}
if (what.agent) {
}
}
node.prototype.std_status = function (what) {
var data,self=this;
if (!what || what=='node') return this.stats;
if (what == 'agents') return Comp.array.filtermap(this.processes.table,function (pro,index) {
if (pro) return {
index:index,
agent:pro.agent.id,
blocked:pro.blocked,
suspended:pro.suspended,
signals:pro.signals.length,
next:pro.agent.next,
}
})
if (what == 'links') {
data={}
for(var p in this.connections) {
if (!this.connections[p]) continue;
if (p=='ip') data.ip=Comp.array.map(this.connections[p].links, function (link) {
return {
ip:link.ip,
stats:link.stats(),
status:link.status('%'),
}
})
}
return data;
}
}
/** Release a proecss container. If the process migrated,
* move the process container to a cache (signal and group comm.)
*
*/
node.prototype.unregister = function (process) {
var i,p,remove,
self=this,tmo,
agent=process.agent,
curtime=Aios.time()-current.world.lag;
// Check pending timers
remove=false;
Comp.array.iter(this.timers,function (timer,i) {
if (timer && timer[0].pid==process.pid) {
self.timers[i]=_;
remove=true;
}
});
if (remove)
this.timers =
Comp.array.filter(this.timers,function (timer) {
return timer!=undefined;
});
// Unlink process
this.processes.table[process.pid]=_;
delete this.processes.hash[agent.id];
if (this.processes.free==none) this.processes.free=process.pid;
this.ts.cleanup(process);
process.pid=none;
process.signals=[];
process.dead=true;
this.processes.used--;
this.stats.agents--;
if (process.move) {
// Cache migrated process
tmo=curtime+this.TMO;
this.processes.gone[process.agent.id]={dir:process.move,timeout:tmo};
// Maganged in world GC
this.timeout=min0(this.timeout,tmo);
}
Aios.emit('agent-',{agent:agent,node:self,proc:process},self);
}
/** Create a new node object.
* If setcurrent is set, the new node will be set as the current node.
*/
var Node = function (options,setcurrent) {
var obj=new node(options);
if (setcurrent) current.node=obj;
return obj;
}
module.exports = {
isNode: function (o) { return o instanceof Node },
Node:Node,
options:options,
current:function (module) { current=module.current; Aios=module; }
}
};
BundleModuleCode['jam/proc']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 15-1-16 by sbosse.
** $RCS: $Id: proc.js,v 1.2 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.7.2
**
** $INFO:
**
** JavaScript AIOS Agent Process Module
**
** $ENDOFINFO
*/
var Comp = Require('com/compat');
var current=none;
var Aios = none;
var options = {
debug : {},
version:'1.7.2'
}
var PRIO = {
LOW:0,
NORMAL:1,
HIGH:2
}
/*
** Agent process - must be compatible with scheduler context process!
*/
var proc = function (properties) {
// Agent code
this.agent={};
// Internal scheudling blocks - can'tmigrate - if any
// Handled by global scheduler (DOS)
this.block=[];
// Process execution suspended?
this.blocked=false;
// Process blocking timeout (absolute time)
this.timeout=0;
// For soft checkpointing
this.runtime=0;
// Ressource control (node constraints)
this.resources = {
start:Aios.clock('ms'), // start time on this host
consumed:0, // total processing time consumed
memory:0, // total memory (code+data) consumed
tuples:0, // total tuple generation
agents:0, // total agents created
}
this.level=undefined;
// Dynamic process priority effecting scheduling order
this.priority = PRIO.NORMAL;
// Agent scheduling blocks - can migrate!
// Handled by AIOS scheduler only!
// function []
this.schedule=[];
// Agent activity suspended, waiting for an event?
this.suspended=false;
this.suspendedIn=undefined;
this.error=none;
// process id
this.pid=none;
// process parent id
this.gid=none;
this.id='agent';
this.mask={};
// [sig,arg,from] []
this.signals=[];
// Did we moved?
this.move=none;
// Killing state
this.kill=false;
// Dead state
this.dead=false;
// Pending next transition computatuion?
this.transition=false;
// Prevent transition
this.notransition=false;
for (var p in properties) {
if (properties[p]!=undefined) this[p]=properties[p];
}
// Used in simulators only: A geometric shape object
this.shape=undefined;
if (current.world) this.parent = current.world.context;
this.node=current.node;
}
/** Execute a callback function in this agent process context immediately (should invoke scheduler and CB!)
*
*/
proc.prototype.callback = function (cb,args) {
var _process=current.process,_node=current.node, res;
current.node=this.node;
current.process=this;
try {
res=cb.apply(this.agent,args||[]);
} catch (e) {
Aios.aios.log('Caught callback error: '+e);
}
current.process=_process;
current.node=_node;
return res;
}
/** Execute this process immediately
*
*/
proc.prototype.exec = function() {
var _process=current.process,_node=current.node, res;
current.node=this.node;
res = Aios.schedule(this);
current.process=_process;
current.node=_node;
return res;
}
/** Finalize this process
*
*/
proc.prototype.finalize = function() {
this.kill=true;
this.suspended=false;
current.node.unregister(this);
}
/** Fork an agent process.
* Returns child process.
* If level is not specified, the parent process level is used.
*/
proc.prototype.fork = function(parameters,level,dirty) {
var code,
_process=current.process,
agent=current.process.agent,
process_,
agent_,
p;
parameters=parameters||{};
current.process.resources.agents++;
if (dirty && level!=undefined) dirty=false; // Dirty process copy with level change not possible!
if (level==undefined) level=current.process.mask.privilege();
else level=Math.min(current.process.mask.privilege(),level);
if (!dirty) {
code = Aios.Code.forkCode(current.process);
process_ = Aios.Code.toCode(code,level);
} else {
process_ = Aios.Code.copyProcess(current.process);
}
agent_ = process_.agent
agent_.id=Aios.aidgen();
agent_.parent=current.process.agent.id;
process_.init({gid:current.process.pid});
current.process=process_;
current.node.register(process_);
// Update forked child agent parameters only if they already exist
// First we have to restore nullified attributes that can be lost on copy!!!
for (p in agent) {
if (agent[p]===null) agent_[p]=null;
}
for (p in parameters) {
if (Comp.obj.hasProperty(agent_,p)) agent_[p]=parameters[p];
}
// Should next activity computed in scheduler by setting process.transition ???
// compute next activity after fork if there is no scheduling block,
// no parameter next set,
// and forkCode should always discard all current schedule blocks!
if (!parameters.next) try {
agent_.next=(typeof agent_.trans[agent_.next] == 'function')?agent_.trans[agent_.next].call(agent_):
agent_.trans[agent_.next];
} catch (e) { /*kill agent?*/ process_.kill=true; };
this.node.stats.fork++;
current.process=_process;
return process_;
}
proc.prototype.init = function (properties) {
for (var p in properties) {
if (this[p]!=undefined) this[p]=properties[p];
}
}
proc.prototype.print = function () {
var str='',
agent=this.agent;
str = 'PID='+this.pid+
(this.gid?' GID='+this.gid:'')+
(this.timeout?(' TMO='+this.timeout):'')+
(this.blocked?' BLOCKED':'')+
(this.suspended?' SUSP':'')+
(this.kill?' KILL':'')+
(this.dead?' DEAD':'');
if (this.schedule.length>0) str += ' SCHEDULE='+this.schedule.length;
if (this.block.length>0) str += ' BLOCK='+this.block.length;
if (this.signals.length>0) str += ' SIGNALS('+this.signals.length+'):'+
Comp.printf.list(this.signals,function (s) {return s[0]});
if (this.transition) str += ' TRANS';
if (this.consumed|0) str += ' CONS='+(this.consumed|0);
if (agent) str += ' AGENT '+agent.id+' next='+agent.next;
return str;
}
/**
* Suspend agent activity processing, but not internal block scheduling!
*/
proc.prototype.suspend = function (timeout,transition,suspendedIn){
if (!this.kill && !this.dead) {
this.suspended=true;
if (timeout!=undefined) this.timeout=timeout;
if (transition) this.transition=true; // pending next computation
this.suspendedIn = suspendedIn;
}
}
proc.prototype.update = function (properties) {
for (var p in properties) {
this[p]=properties[p];
}
}
// Change the mask environment of an agent process with a new AIOS level API
proc.prototype.upgrade = function (level) {
var aios;
switch (level) {
case undefined:
case 0: aios=Aios.aios0; break;
case 1: aios=Aios.aios1; break;
case 2: aios=Aios.aios2; break;
case 3: aios=Aios.aios3; break;
default: aios=Aios.aios0; break;
}
for (p in aios) {
this.mask[p]=aios[p];
}
}
/**
* Wakeup agent process from a previous suspend call (sleep)
*/
proc.prototype.wakeup = function (immediate){
this.suspended=false;
this.timeout=0;
if (!this.kill && !this.dead && (immediate || this.schedule.length == 0)) {
var _process=current.process,_node=current.node;
current.node=this.node;
if (this.suspendedIn=='ts') this.node.ts.cleanup(this,true); // Have to call callback handler to inform about timeout!?
this.suspendedIn=undefined;
this.transition=this.schedule.length == 0;
// Re-entering the scheduler is a bad idea!?
Aios.schedule(this);
current.process=_process;
current.node=_node;
} else Aios.scheduled++;
}
function Proc(properties) {
var obj = new proc(properties);
return obj;
}
module.exports = {
agent: {
fork:function fork(parameters) {
return current.process.fork(parameters);
}
},
isProc: function (o) { return o instanceof Proc },
Proc:Proc,
PRIO:PRIO,
current:function (module) { current=module.current; Aios=module; },
options:options
}
};
BundleModuleCode['jam/ts']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 15-1-16 by sbosse.
** $RCS: $Id: ts.js,v 1.4 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.11.2
**
** $INFO:
**
** JavaScript Agent Tuple-Space Sub-System
**
** New: out/amrk now create copies of mutable object and array elements!
** New: Global lifetime limit of all tuples
** New: xx.try function synonyms (inp.try, rd.try,..) and try functions now fire callback on timeout
** New: testandset
** New: eval/listen
** New: Patterns can contain regular expression! (p_i instanceof RegExp)
** New: A rd/inp operation can return all matching tuples
** New: alt operation supporting listening on multiple patterns
** New: Distributed TS with collect, copyto, store
**
** Exeception: 'TS' | 'EOL'
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios = none;
var verbose=false;
var options = {
debug:{},
version:'1.11.2',
// global lifetime limit of tuples
timeout : 0,
}
function aid(process) { return process.agent.id+':'+process.pid }
function log(tsi,process,msg) {
if (verbose && process) Aios.aios.log('[TS'+(tsi.i)+':'+current.node.ts.id+'] Ag '
+ aid(process)+ ' '+msg);
else if (verbose) Io.log('[TS'+(tsi.i)+':'+current.node.ts.id+'] SYS'+msg);
}
function min0(a,b) { return a==0?b:(b==0?a:Comp.pervasives.min(a,b)) };
/*******************************************
** Waiter management
*******************************************/
/** Add a waiter to a tuple space data base
*
*/
var addwaiter = function (tsi,waiter) {
var index,key;
if (tsi.waiters.free.length==0) {
index=tsi.waiters.length;
tsi.waiters.push(waiter);
} else {
index=tsi.waiters.free[0];
tsi.waiters[index]=waiter;
tsi.waiters.free.shift();
}
if (typeof (key=waiter.pat[0]) == 'string') switch (waiter.op) {
case 'listen':
tsi.waiters.hash[key]=index; break;
}
}
/** Check for waiting agent processes and try to match the provided tuple.
* Readers can read multiple copies of the tuple, whereby consumers can only read the tuple one time.
* Consumers (in-op) can be in a waiting list (next/prev). If one waiter in a list consumes
* a tuple, all waiters must be removed. The other waiters (the same process, but different patterns; alt-op)
* can be in different tuple data bases!
*
*/
var checkwaiter = function (tsi,tuple,callback) {
var res,consumed=false,
i,waiter;
// Create agent callback
function cb(waiter,res) {
Aios.CB(waiter.pro,function () {waiter.cb.call(waiter.pro.agent,res)});
}
for(i=0;i<tsi.waiters.length;i++) {
if (!tsi.waiters[i]) continue;
waiter=tsi.waiters[i];
if (!consumed) switch (waiter.op) {
case 'rd':
case 'rd-all':
res=match(tuple,waiter.pat);
log(tsi,current.process,' rd waiter? '+res);
if (res!=none) {
cb(waiter,(waiter.op=='rd'?res:[res])),
waiter.pro.wakeup();
removewaiter(tsi,i);
}
break;
case 'in':
case 'in-all':
res=match(tuple,waiter.pat);
log(tsi,current.process,' in waiter? '+res);
if (res!=none) {
cb(waiter,(waiter.op=='in'?res:[res])),
waiter.pro.wakeup();
consumed=true;
removewaiter(tsi,i);
}
break;
case 'listen':
res=match(tuple,waiter.pat);
log(tsi,current.process,' listen waiter? '+res);
if (res!=none) {
res=waiter.pro.callback(waiter.cb,[res]);
if (callback) callback.apply(current.process.agent,[res]);
consumed=true;
}
break;
} else break;
}
if (!consumed && current.node.ts.consumers.length>0) {
consumed = Comp.array.findmap(current.node.ts.consumers,function (consumer) {
return consumer(tuple);
});
}
return consumed;
}
var findwaiter = function (tsi,waiter) {
var i;
for(i=0;i<tsi.waiters.length;i++) {
if (!tsi.waiters[i]) continue;
if (tsi.waiters[i].pro.pid!=waiter.pro.pid) continue;
if (equal(tsi.waiters[i].pat,waiter.pat)) return i;
}
return;
}
var removewaiter = function (tsi,index) {
var waiter=tsi.waiters[index],_tsi,_index;
tsi.waiters[index]=undefined;
tsi.waiters.free.push(index);
// Waiter in a chained waiter list?
if (waiter.prev) {
waiter.prev.next=undefined;
_tsi=current.node.ts.db[waiter.prev.pat.length];
_index=findwaiter(_tsi,waiter.prev);
if (_index != undefined) removewaiter(_tsi,_index);
};
if (waiter.next) {
waiter.next.prev=undefined;
_tsi=current.node.ts.db[waiter.next.pat.length];
_index=findwaiter(_tsi,waiter.next);
if (_index != undefined) removewaiter(_tsi,_index);
};
}
var count = function (tsi) {
var data=tsi.data,i,n=0;
for (i in data) {if (data[i] != undefined) n++};
return n;
}
/** Find one/all matching tuple(s) in the database based on pattern matching
*
*/
var lookup = function (pat,all) {
var tsi,nary=pat.length,res=none;
if (nary>current.node.ts.n || nary==0) return;
tsi=current.node.ts.db[nary];
if (!all && Comp.isString(pat[0]) && tsi.hash[pat[0]]!=undefined) {
// Speedup trial with hash key
res=match(tsi.data[tsi.hash[pat[0]]],pat);
}
if (res==none) {
res = (all?Comp.array.filtermap:Comp.array.findmap)(tsi.data,function (tuple) {
if (tuple==_) return none;
else return match(tuple,pat);
});
if (res && res.length==0) res=none;
if (res == none && current.node.ts.providers.length>0) {
res = Comp.array.findmap(current.node.ts.providers,function (provider) {
return provider(pat);
});
}
}
return res;
}
// return only the table index of matching data table entry
var lookupIndex = function (pat,all) {
var tsi,nary=pat.length,res=none;
if (nary>current.node.ts.n || nary==0) return;
tsi=current.node.ts.db[nary];
if (!all && Comp.isString(pat[0]) && tsi.hash[pat[0]]!=undefined) {
// Speedup trial with hash key
res=match(tsi.data[tsi.hash[pat[0]]],pat);
if (res!=none) return tsi.hash[pat[0]];
}
if (res==none) {
res = (all?Comp.array.filtermap:Comp.array.findmap)(tsi.data,function (tuple,index) {
if (tuple==_) return none;
else return match(tuple,pat)?index:none;
});
if (res && res.length==0) res=none;
}
return res;
}
/*******************************************
** Tuple management
*******************************************/
/**
* Compare two values, check equiality
*/
var equal = function(x,y) {
var i;
if(x==y) return true;
if (Comp.obj.isArray(x) && Comp.obj.isArray(y)) {
if (x.length!=y.length) return false;
for(i in x) {
if (x[i] != y[i]) return false;
}
return true;
}
return false;
}
/** Match a tuple element with a template pattern element y.
*
*/
var match1 = function (x,y) {
if (y==any) return true;
if (x==y) return true;
if ((x instanceof Array) && (y instanceof Array)) return match(x,y)!=none;
if (y instanceof RegExp && typeof x == 'string' && y.test(x)) return true;
return false;
}
/** Match a tuple with a template and return none or the original tuple (equivalence result?)
*
*/
var match = function (tuple,templ) {
var i;
if (tuple.length != templ.length) return none;
for(i in tuple) {
if (!match1(tuple[i],templ[i])) return none;
};
return tuple;
}
/** Find and remove one/all matching tuple(s) from the database based on pattern matching
*
*/
var remove = function (pat,all) {
var tsi,nary=pat.length,res=none,removed=false,hashed=_;
if (nary>current.node.ts.n || nary==0) return;
tsi=current.node.ts.db[nary];
if (!all && Comp.isString(pat[0])) hashed=tsi.hash[pat[0]];
if (hashed != _) {
// Speedup trial with hash key
res=match(tsi.data[hashed],pat);
if (res) {
// invalidate matching tuple in data list
removed=true;
tsi.data[hashed]=_;
tsi.tmo[hashed]=0;
// remember the free slot in the data list
if (tsi.free==none) tsi.free=hashed;
// invalidate hash entry - tuple is consumed
delete tsi.hash[pat[0]];
}
}
if (res==none || removed==false) {
res = (all?Comp.array.filtermap:Comp.array.findmap)(tsi.data,function (tuple,i) {
if (tuple==_) return none;
var res_=match(tuple,pat);
if (res_!=none) {
if (Comp.isString(pat[0]) && tsi.hash[pat[0]]==i) {
// Invalidate hash - tuple is consumed
delete tsi.hash[pat[0]];
}
tsi.data[i]=_;
tsi.tmo[i]=0;
if (tsi.free==none) tsi.free=i;
return res_;
} else return none;
});
if (res && res.length==0) res=none;
}
return res;
}
/*******************************************
** Tuple Space Agent/Client API
*******************************************/
var ts = {
// consuming - tmo <> 0 => try_alt
alt: function (pats,callback,all,tmo) {
var tsi,nary,res,
i,p,pat,waiters=none,last=none;
for(i in pats) {
pat=pats[i];
nary=pat.length;
if (nary>current.node.ts.n || nary==0) return none;
res = remove(pat,all);
if (res && res.length) current.node.stats.tsin += (all?res.length:1);
if (res && callback) {
callback.call(current.process.agent,res);
return;
}
else if (res) return res;
}
if (callback && tmo==0) return callback();
if (tmo==0) return;
// No matching tuple found - go to sleep
for(i in pats) {
pat=pats[i];
nary=pat.length;
if (callback && (tmo==undefined||tmo>0)) {
if (waiters==none)
waiters={pat:pat,
pro:current.process,
cb:callback,
op:'in'+(all?'-all':''),
tmo:tmo>0?Aios.time()-current.world.lag+tmo:0
},last=waiters;
else {
last.next={pat:pat,
pro:current.process,
cb:callback,
op:'in'+(all?'-all':''),
tmo:tmo>0?Aios.time()-current.world.lag+tmo:0,
prev:last
},last=last.next;
}
}
}
if (waiters!=none) {
p=waiters;
while(p) {
tsi=current.node.ts.db[p.pat.length];
addwaiter(tsi,p);
p=p.next;
}
log(tsi,current.process,' +waiter');
current.process.suspend(tmo>0?Aios.time()-current.world.lag+tmo:0,_,'ts');
}
},
// The collect primitive moves tuples from this source TS that match template
// pattern into destination TS specified by path 'to' (a node destination).
collect: function (to,pat) {
var tsi,nary=pat.length,res;
if (nary>current.node.ts.n || nary==0) return none;
tsi=current.node.ts.db[nary];
res = remove(pat,true);
if (res.length>0) {
current.node.stats.tsin += res.length;
Aios.Sig.agent.sendto(to,'TS.SIG',res);
}
return res.length;
},
// Copy all matching tuples form this source TS to a remote destination TS
// specified by path 'to' (a node destination).
copyto: function (to,pat) {
var tsi,nary=pat.length,res;
if (nary>current.node.ts.n || nary==0) return 0;
tsi=current.node.ts.db[nary];
res = lookup(pat,true);
if (res.length>0) {
Aios.Sig.agent.sendto(to,'TS.SIG',res);
}
return res.length;
},
// Access a tuple evaluator - non-blocking: no listener -> callback(null)
// TODO blocking/tmo
evaluate: function (pat,callback,tmo) {
var tsi,nary=pat.length,res;
if (nary>current.node.ts.n || nary==0) return none;
tsi=current.node.ts.db[nary];
consumed=checkwaiter(tsi,pat,callback);
if (!consumed && callback) callback.call(current.process.agent,null);
},
// Test tuple existence
exists: function (pat) {
var tsi,nary=pat.length,res;
if (nary>current.node.ts.n || nary==0) return none;
tsi=current.node.ts.db[nary];
res = lookup(pat);
return res!=none;
},
// consuming - tmo <> 0 => try_in
inp: function (pat,callback,all,tmo) {
var tsi,nary=pat.length,res;
if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS';
tsi=current.node.ts.db[nary];
res = remove(pat,all);
log(tsi,current.process,' in? '+res+' []='+count(tsi));
if (res && res.length) current.node.stats.tsin += (all?res.length:1);
if (res==none && callback && (tmo==undefined||tmo>0)) {
addwaiter(tsi,{pat:pat,
pro:current.process,
cb:callback,
op:'in'+(all?'-all':''),
tmo:tmo>0?Aios.time()-current.world.lag+tmo:0
});
log(tsi,current.process,' +waiter');
current.process.suspend(tmo>0?Aios.time()-current.world.lag+tmo:0,_,'ts');
return none;
} else if (callback) callback.call(current.process.agent,res); else return res;
},
// Provide a tuple evaluator
listen: function (pat,callback) {
var tsi,nary=pat.length,res;
if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS';
tsi=current.node.ts.db[nary];
addwaiter(tsi,{pat:pat,
pro:current.process,
cb:callback,
op:'listen',
tmo:0
});
},
// Store time-limited tuples
mark: function (tuple,tmo) {
var p,tsi,nary=tuple.length,consumed=false;
if (nary>current.node.ts.n || nary==0 || typeof tuple != 'object') throw 'TS';
if (current.process && (current.process.resources.tuples+1) > (current.process.resources.TS||Aios.options.TSPOOL)) throw 'EOL';
tuple=Comp.copy(tuple); // decouple producer context
tsi=current.node.ts.db[nary];
current.node.stats.tsout++;
// Check waiters
consumed=checkwaiter(tsi,tuple);
if (!consumed) {
if (tsi.free==none) {
loop: for (var i in tsi.data) {
if (tsi.data[i]==_) {tsi.free=i; break loop}
}
}
if (options.timeout) tmo=min0(options.timeout,tmo);
tmo=Aios.time()-current.world.lag+tmo;
if (tsi.free!=none) {
tsi.data[tsi.free]=tuple;
tsi.tmo[tsi.free]=tmo;
current.node.ts.timeout=min0(current.node.ts.timeout,tsi.tmo[tsi.free]);
if (Comp.obj.isString(tuple[0]))
tsi.hash[tuple[0]]=tsi.free;
tsi.free=none;
} else {
tsi.data.push(tuple);
tsi.tmo.push(tmo);
// hash is only a first guess to find a tuple
if (Comp.obj.isString(tuple[0]))
tsi.hash[tuple[0]]=tsi.data.length-1;
}
} else current.node.stats.tsin++;
},
// Store a tuple in this TS
out: function (tuple) {
var tsi,tmo=0,nary=tuple.length,consumed=false,res;
if (nary>current.node.ts.n || nary==0 || typeof tuple != 'object') throw 'TS';
if (current.process && (current.process.resources.tuples+1) > (current.process.resources.TS||Aios.options.TSPOOL)) throw 'EOL';
tsi=current.node.ts.db[nary];
tuple=Comp.copy(tuple); // decouple producer context
current.node.stats.tsout++;
// Check waiters
consumed=checkwaiter(tsi,tuple);
if (!consumed) {
if (tsi.free==none) {
loop: for (var i in tsi.data) {
if (tsi.data[i]==_) {tsi.free=i; break loop}
}
}
if (options.timeout) tmo=Aios.time()-current.world.lag+options.timeout;
if (tmo) current.node.ts.timeout=min0(current.node.ts.timeout,tmo);
if (tsi.free!=none) {
tsi.data[tsi.free]=tuple;
tsi.tmo[tsi.free]=tmo;
if (Comp.obj.isString(tuple[0]))
tsi.hash[tuple[0]]=tsi.free;
tsi.free=none;
}
else {
tsi.data.push(tuple);
tsi.tmo.push(tmo);
// hash is only a first guess to find a tuple
if (Comp.obj.isString(tuple[0]))
tsi.hash[tuple[0]]=tsi.data.length-1;
}
} else current.node.stats.tsin++;
log(tsi,current.process,' out '+tuple+' ['+nary+'] consumed='+consumed+' []='+count(tsi));
},
// not consuming - tmo <> undefined => try_rd [0: immed.]
rd: function (pat,callback,all,tmo) {
var tsi,nary=pat.length,res;
if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS';
tsi=current.node.ts.db[nary];
res = lookup(pat,all);
if (res==none && callback && (tmo==_||tmo>0)) {
addwaiter(tsi,{pat:pat,
pro:current.process,
cb:callback,
op:'rd'+(all?'-all':''),
tmo:tmo>0?Aios.time()-current.world.lag+tmo:0
});
current.process.suspend(tmo>0?Aios.time()-current.world.lag+tmo:0,_,'ts');
return none;
} else if (callback) callback.call(current.process.agent,res); else return res;
},
// consuming
rm: function (pat,all) {
var tsi,nary=pat.length,res;
if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS';
tsi=current.node.ts.db[nary];
res = remove(pat,all);
if (res && res.length) current.node.stats.tsin += (all?res.length:1);
return (res!=none);
},
// Remote tuple storage
store: function (to,tuple) {
Aios.Sig.agent.sendto(to,'TS.SIG',[tuple]);
return 1;
},
// Test and Set: Atomic modification of a tuple - non blocking
// Updates timeout of markings and time limited tuples, too.
// typeof @callbackOrTimeout: function (tuple) -> tuple | timeout number
ts: function (pat,callbackOrTimeout) {
var tsi,nary=pat.length,index,res,ret,tmo;
if (nary>current.node.ts.n || nary==0 || !Comp.obj.isArray(pat)) throw 'TS';
tsi=current.node.ts.db[nary];
index = lookupIndex(pat,false);
if (index!=none) res=tsi.data[index]; else return;
log(tsi,current.process,' test? '+res+' []='+count(tsi));
if (typeof callbackOrTimeout == 'function') {
if (current.process)
ret=callbackOrTimeout.call(current.process.agent,res);
else
ret=callbackOrTimeout(res);
// update the modified tuple
if (ret && ret.length==res.length) Comp.array.copy(ret,res);
res=ret;
} else if (typeof callbackOrTimeout == 'number') {
// update timestamp
tmo=min0(options.timeout,callbackOrTimeout);
tmo=Aios.time()-current.world.lag+tmo;
tsi.tmo[index]=Math.max(tmo,tsi.tmo[index]);
}
return res;
},
try : {
alt : function (tmo,pats,callback,all) {
if (typeof callback == 'boolean') {
all=callback;
callback=null;
}
return ts.alt(pats,callback,all,tmo);
},
evaluate : function (tmo,pat,callback) {
return ts.evaluate(pat,callback,tmo);
},
inp : function (tmo,pat,callback,all) {
if (typeof callback == 'boolean') {
all=callback;
callback=null;
}
return ts.inp(pat,callback,all,tmo);
},
rd : function (tmo,pat,callback,all) {
if (typeof callback == 'boolean') {
all=callback;
callback=null;
}
return ts.rd(pat,callback,all,tmo);
}
}
}
// Synonyms
ts.alt.try = ts.try.alt
ts.evaluate.try = ts.try.evaluate
ts.inp.try = ts.try.inp
ts.rd.try = ts.try.rd
/*******************************************
** Tuple Space Data Base
*******************************************/
var tsd = function (options) {
var self=this;
if (!options) options={};
this.n=options.maxn||8;
this.id=options.id||'TS';
this.timeout=0;
this.db=Comp.array.init(this.n+1,function (i) {
var tsi;
if (i==0) return none;
tsi = {
i:i,
hash:[],
// number|none
free:none,
// [*] []
data:[],
// number []
tmo:[],
// [pattern,agent,callback,kind]
waiters:[]
};
tsi.waiters.free=[];
tsi.waiters.hash={}; // Hash tuple key for consuming waiter
return tsi;
});
/*
** Additional external tuple providers implementing a match function.
*/
this.providers=[];
/*
** Additional external tuple consumers implementing a match function.
*/
this.consumers=[];
this.node=options.node;
// External API w/o blocking and callbacks (i.e., try_ versions with tmo=0)
// Can be called from any context
this.extern = {
exists: function (pat,callback) {
var res,_node=current.node;
current.node=self.node||_node;
res=ts.exists(pat,callback);
current.node=_node;
return res;
},
inp: function (pat,all) {
var res,tsi,nary=pat.length,_node=current.node;
current.node=self.node||_node;
if (nary>current.node.ts.n || nary==0) return none;
tsi=current.node.ts.db[nary];
res = remove(pat,all);
if (res && res.length) current.node.stats.tsin += (all?res.length:1);
current.node=_node;
return res;
},
mark: function (pat,tmo) {
var res,_node=current.node,_proc=current.process;
current.node=self.node||_node; current.process=null;
res = ts.mark(pat,tmo);
current.node=_node;
current.process=_proc;
return res;
},
out: function (pat) {
var res,_node=current.node,_proc=current.process;
current.node=self.node||_node; current.process=null;
res = ts.out(pat)
current.node=_node;
current.process=_proc;
return res;
},
rd: function (pat,all) {
var res,tsi,nary=pat.length,_node=current.node;
current.node=self.node||_node;
if (nary>current.node.ts.n || nary==0) return none;
tsi=current.node.ts.db[nary];
res = lookup(pat,all);
if (res && res.length) current.node.stats.tsin += (all?res.length:1);
current.node=_node;
return res;
},
rm: function (pat,all) {
var res,_node=current.node;
current.node=self.node||_node;
res=ts.rm(pat,all);
current.node=_node;
return res;
},
ts: function (pat,callback) {
var res,_node=current.node;
current.node=self.node||_node;
res=ts.ts(pat,callback);
current.node=_node;
return res;
},
}
}
var create = function (options) {
return new tsd(options);
}
tsd.prototype.checkwaiter = function (tuple) {
var tsi,nary=tuple.length;
if (nary>this.n || nary==0) return none;
tsi=current.node.ts.db[nary];
return checkwaiter(tsi,tuple);
}
/** Remove an agent process from waiter queues.
* If doCallback is set, a pending operation callback handler is executed here (e.g., on timeout or interruption).
*
*/
tsd.prototype.cleanup = function (process,doCallback) {
var i,j,tsi,p,waiter,node=process.node;
function cb(waiter) {
Aios.CB(waiter.pro,function () {waiter.cb.call(waiter.pro.agent,null)});
}
for (i in node.ts.db) {
if (i==0) continue;
tsi=node.ts.db[i];
for(j=0;j<tsi.waiters.length;j++) {
if (!tsi.waiters[j]) continue;
waiter=tsi.waiters[j];
if (waiter.pro.pid==process.pid) {
if (doCallback && waiter.cb) cb(waiter);
removewaiter(tsi,j);
}
}
}
}
/**
* Register an external tuple provider (function).
* The provider can immediately return a matching tuple,
* or can deliver it later on calling the checkwaiter loop
* which delivers the tuple to the agent.
*
* type of func : provider|consumer
* type provider : function ('pat) -> 'tuple
* type consumer : function ('pat) -> boolean
*/
tsd.prototype.register = function (func,consumer) {
if (consumer) this.consumers.push(func)
else this.providers.push(func);
};
tsd.prototype.print = function (summary) {
var i,tsi,num,str='',sep='';
if (summary) {
str += '[';
for (i in current.node.ts.db) {
if (i==0) continue;
tsi=current.node.ts.db[i];
num = count(tsi);
if (num>0) {
str += sep+'TS'+(int(i)+1)+'='+num;
sep=' ';
}
}
str += ']'+NL;
}
else for (i in current.node.ts.db) {
if (i==0) continue;
tsi=current.node.ts.db[i];
str += '['+Comp.printf.sprintf('%2d',tsi.i)+
' free='+(tsi.free?Comp.printf.sprintf('%4d',tsi.free):'none')+
' data='+Comp.printf.sprintf('%4d(%4d)',count(tsi),tsi.data.length)+
' waiters='+Comp.printf.sprintf('%4d',tsi.waiters.length)+']'+NL;
}
return str;
}
/** Tuple Space Garbage Collection and Timeout Service
*
*/
tsd.prototype.service = function (curtime) {
var i,hashed,tsi,nexttime=0;
// TODO: if (curtime<this.timeout) return;
for (i in this.db) {
tsi=this.db[i];
hashed;
if (tsi==_) continue;
Comp.array.iter(tsi.tmo,function (tmo,i) {
var tuple=tsi.data[i];
if (tuple && tmo) {
if (tmo < curtime) {
if (Comp.isString(tuple[0])) hashed=tsi.hash[tuple[0]];
if (hashed != _ && hashed==i) delete tsi.hash[tuple[0]];
tsi.data[i]=_;
tsi.tmo[i]=0;
if (tsi.free==none) tsi.free=i;
} else nexttime=min0(nexttime,tmo);
}
});
}
this.timeout=nexttime;
}
module.exports = {
agent:ts,
count:count,
create: create,
current:function (module) { current=module.current; Aios=module; },
match:match,
options:options
}
};
BundleModuleCode['jam/world']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2019 bLAB
** $CREATED: 15-1-16 by sbosse.
** $RCS: $Id: world.js,v 1.3 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.11.3
**
** $INFO:
**
** JavaScript AIOS Agent World Module
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios=none;
var options = {
debug:{},
version:'1.11.3',
verbose:0,
}
/** Word object
*
* typeof options = {
* classes?,
* id?:string,
* scheduler?,
* verbose?
* }
*/
var world= function (nodes,options) {
var main=this;
options=checkOptions(options,{});
this.options=options;
this.nodes=nodes||[];
this.verbose=checkOption(this.options.verbose,0);
this.hash={};
this.id=checkOption(this.options.id,Aios.aidgen().toUpperCase());
this.classes=checkOption(this.options.classes,[]);
// A time lag (offset), required for simulation
this.lag=0;
this.scheduler = this.options.scheduler;
this.log = Aios.log;
this.out = function (msg) { main.log('[JAW '+this.id+'] '+msg)};
/* Create a task context for the scheduler
*/
this.thread = function (arg) {
var thr = this;
var dying=false;
this.nexttime=0;
this.number=arg;
this.curtime=0;
this.init = function () {
main.out('JAM World is starting ..');
};
this.run = function () {
thr.nexttime=Aios.scheduler();
thr.curtime=Aios.time();
if (main.verbose>3) main.out(' .. nexttime = '+thr.nexttime+
' ('+(thr.nexttime>0?thr.nexttime-thr.curtime:0)+')');
};
this.sleep = function () {
var delta;
thr.curtime=Aios.time();
delta=thr.nexttime>0?thr.nexttime-thr.curtime:1000;
if (main.verbose>3) main.out(' .. sleeping for '+delta+' ms');
main.scheduler.Delay(delta);
};
this.transitions = function () {
var trans;
trans =
[
[undefined, this.init, function (thr) {
return true
}],
[this.init, this.run, function (thr) {
return true
}],
[this.run, this.run, function (thr) {
return thr.nexttime<0;
}],
[this.run, this.sleep, function (thr) {
return !dying;
}],
[this.run, this.terminate, function (thr) {
return dying
}],
[this.sleep, this.run, function (thr) {
return true;
}]
];
return trans;
};
this.context = main.scheduler.TaskContext('JAM World'+main.id, thr);
}
};
// Add an agent class constructor (@env can contain resolved constructor function variables).
// typepf constructor = function|string
world.prototype.addClass = function (name,constructor,env) {
this.classes[name]=[
Aios.Code.makeSandbox(constructor,0,env),
Aios.Code.makeSandbox(constructor,1,env),
Aios.Code.makeSandbox(constructor,2,env),
Aios.Code.makeSandbox(constructor,3,env)
];
}
/** Add a node to the world.
*
*/
world.prototype.addNode = function (node) {
this.nodes.push(node);
if (node.id) this.hash[node.id]=node;
if (options.verbose) Io.out('World.addNode <'+node.id+'>');
};
/** Connect two nodes in directions dir:node1->node2 and dir':node2->node1
* with two virtual channel links that are created here.
*
*/
world.prototype.connect = function (dir,node1,node2,options) {
if (!options) options={};
var chan=Aios.Chan.Virtual(node1,node2,dir,options);
switch (dir) {
case Aios.DIR.NORTH:
node1.connections.north=chan.link1;
node2.connections.south=chan.link2;
break;
case Aios.DIR.SOUTH:
node1.connections.south=chan.link1;
node2.connections.north=chan.link2;
break;
case Aios.DIR.WEST:
node1.connections.west=chan.link1;
node2.connections.east=chan.link2;
break;
case Aios.DIR.EAST:
node1.connections.east=chan.link1;
node2.connections.west=chan.link2;
break;
case Aios.DIR.NE:
node1.connections.ne=chan.link1;
node2.connections.sw=chan.link2;
break;
case Aios.DIR.NW:
node1.connections.nw=chan.link1;
node2.connections.se=chan.link2;
break;
case Aios.DIR.SE:
node1.connections.se=chan.link1;
node2.connections.nw=chan.link2;
break;
case Aios.DIR.SW:
node1.connections.sw=chan.link1;
node2.connections.ne=chan.link2;
break;
case Aios.DIR.UP:
node1.connections.up=chan.link1;
node2.connections.down=chan.link2;
break;
case Aios.DIR.DOWN:
node1.connections.down=chan.link1;
node2.connections.up=chan.link2;
break;
default:
if (current) current.error='EINVALID';
throw 'CONNECT';
}
chan.link2.on('agent',node1.receive.bind(node2));
chan.link1.on('agent',node2.receive.bind(node1));
chan.link2.on('signal',node1.handle.bind(node2));
chan.link1.on('signal',node2.handle.bind(node1));
chan.link1.end=node2.id;
chan.link2.end=node1.id;
return chan;
};
/** Connect node via a port in direction dir:node->*. The endpoint node * will be
* connected if the @snd parameter is specified. Otherwise only an unconnected port is created.
* An endpoint can be later connected using the world.connectTo method (if provided by the interface).
*
* One uni- or bidirectional physical link is created and attached to the given node.
*
* typeof options={
* compress?:boolean,
* oneway?:boolean,
* proto:'udp'|'tcp'|'http'|'device',
* device?:string,
* rcv:url is node endpoint,
* snd?:url is remote endpoint
* secure?:string,
* pem?:{cert:string,key:string},
* }
* with type url = "<name>:<ipport>" | "<ip>:<ipport>" | "<ipport>"
* and ipport = (1-65535) | "*"
*/
world.prototype.connectPhy = function (dir,node,options) {
var self=this,chan,name=Aios.DIR.to(dir);
if (!options) options={};
chan=Aios.Chan.Physical(node,dir,options);
switch (dir.tag||dir) {
case 'DIR.IP':
// Update routing table of router!
if (!node.connections.ip) node.connections.ip=new Aios.Chan.iprouter();
node.connections.ip.addLink(chan.link);
chan.router=node.connections.ip;
break;
default:
if (!name) {
if (current) current.error='ENOCHANNEL';
throw 'CONNECT';
}
node.connections[name]=chan.link;
}
chan.link.on('agent',node.receive.bind(node));
chan.link.on('signal',node.handle.bind(node));
chan.link.on('class',function (obj){ for(var p in obj) self.addClass(p,obj[p].fun,obj[p].env)});
return chan;
};
/** Connect a physical link of node @node to a remote endpoint (if curerently not connected) specified by the @dir parameter.
* typeof @dir = {tag,ip?,device?} with tag='DIR.IP'|'DIR.NORTH',..
*
*/
world.prototype.connectTo = function (dir,node,options) {
var chan,tokens,to=dir.ip,name=Aios.DIR.to(dir);
if (!node) node=current.node;
chan=node.connections[name];
if (chan && (chan.status(to) || !chan.connect)) chan=undefined;
if (chan) chan.connect(to,options);
}
/** Check connectivity to a specific node or a set of nodes
*
*/
world.prototype.connected = function (dir,node) {
var name=Aios.DIR.to(dir),list;
chan=node.connections[name];
switch (dir.tag||dir) {
case Aios.DIR.tag.PATH:
return chan && chan.status(dir.path);
break;
case Aios.DIR.tag.IP:
// DIR.IP('*') returns all linked IP routes
// DIR.IP('%') returns all linked nodes (names)
return chan && chan.status(dir.ip);
break;
case Aios.DIR.tag.NODE:
// DIR.NODE('*') returns all linked nodes on all connections!
if (dir.node=='*') {
// Check all conenctions for remote node information
list=[];
if (node.connections.ip) list=list.concat(node.connections.ip.status('%'));
return list;
} else if (typeof dir.node == 'string') {
// Return link (IP)
if (node.connections.ip && node.connections.ip.lookup) {
found=node.connections.ip.lookup(dir.node);
return found?Aios.DIR.IP(found):none;
}
}
break;
case Aios.DIR.tag.DELTA:
// a rough guess (no nw/sw/se/ne)
if (dir.delta[0]==1) chan=node.connections.east;
else if (dir.delta[0]==-1) chan=node.connections.west;
else if (dir.delta[1]==1) chan=node.connections.north;
else if (dir.delta[1]==-1) chan=node.connections.south;
else if (dir.delta[2]==1) chan=node.connections.up;
else if (dir.delta[2]==-1) chan=node.connections.down;
return chan && chan.status();
break;
default:
return (chan && chan.status(dir.ip||dir.node||dir.path))||false;
}
}
/** Disconnect a physical link of node @node to a remote endpoint (if curerently connected) specified by the @dir parameter.
*
*/
world.prototype.disconnect = function (dir,node) {
var chan;
switch (dir.tag||dir) {
case 'DIR.IP':
if (node.connections.ip &&
node.connections.ip.status(dir.ip) &&
node.connections.ip.disconnect)
node.connections.ip.disconnect(dir.ip);
break;
}
}
/** Find an agent in the world by it's id and class (option),
* or agents matching a regular expression by their id.
*
*/
world.prototype.getAgent = function (id,ac) {
var res = this.getAgentProcess(id,ac);
if (res && Comp.obj.isArray(res)) return res.map(function (ap) { return ap.agent });
if (res) return res.agent;
return;
};
world.prototype.getAgentProcess = function (id,ac) {
var matches=Comp.obj.isRegex(id)?[]:undefined;
for(var n in this.nodes) {
var table=this.nodes[n].processes.table;
for(var p in table) {
if (!table[p]) continue;
if (!matches && table[p].agent.id==id && (!ac || table[p].agent.ac==ac))
return table[p];
if (matches && id.test(table[p].agent.id) && (!ac || table[p].agent.ac==ac))
matches.push(table[p]);
}
}
return matches;
};
/** Find a node in the world by it's id or nodes matching a regular expression.
*
*/
world.prototype.getNode = function (nodeid) {
if (Comp.obj.isRegex(nodeid)) {
var res=[];
for(var n in this.nodes) {
if (nodeid.test(this.nodes[n].id)) res.push(this.nodes[n]);
}
return res;
} else {
if (!this.hash[nodeid] && options.verbose) Io.out('World.getNode: not found <'+nodeid+'>');
return this.hash[nodeid];
}
};
world.prototype.info = function () {
var obj={},stat;
obj.agents=0;
obj.transferred=0;
obj.links=0;
obj.ports=0;
for(var n in this.nodes) {
obj.agents += this.nodes[n].processes.used;
for (var l in this.nodes[n].connections) {
if (this.nodes[n].connections[l]) {
obj.ports++;
obj.transferred += this.nodes[n].connections[l].count();
if (this.nodes[n].connections[l].stats) {
stat = this.nodes[n].connections[l].stats();
obj.links += (stat.links||0);
}
}
}
}
return obj;
}
world.prototype.init = function () {
}
/** Lookup nodes (using patterns and providing broker support)
*
*/
world.prototype.lookup = function (dir,callback,node) {
switch (dir.tag||dir) {
case Aios.DIR.tag.PATH:
if (node.connections.ip && node.connections.ip.lookup) return node.connections.ip.lookup(dir.path,callback);
break;
default:
if (callback) callback();
}
}
world.prototype.print = function (summary) {
var str='**** WORLD '+this.id+' ****'+NL;
var res = Io.mem();
str += 'DATA='+int(res.data/1024)+' MB HEAP='+int(res.heap/1024)+' MB'+NL;
for(var n in this.nodes) {
str += this.nodes[n].print(summary);
}
return str;
}
/** Disconnect and remove a node from the world.
* The node must be destroyed explicitly.
*
*/
world.prototype.removeNode = function (nodeid) {
var c,c2,conn,thenode,chan,node2;
this.nodes=Comp.array.filter(this.nodes,function (node) {
if (node.id==nodeid) thenode=node;
return node.id!=nodeid;
});
this.hash[nodeid]=undefined;
if (thenode) for(c in thenode.connections) {
conn=thenode.connections[c];
if (conn && conn.end) {
node2=this.getNode(conn.end);
if (node2) for (c2 in node2.connections) {
// Unlink?
if (node2.connections[c2] && node2.connections[c2].end==nodeid)
node2.connections[c2]=undefined;
}
}
}
if (options.verbose) Io.out('World.removeNode <'+nodeid+'>');
};
world.prototype.start = function () {
var self=this;
if (this.scheduler) {
proc = new this.thread(0);
this.context=proc.context;
this.scheduler.Add(proc.context);
}
this.gc = setInterval(function () {
var node,n,p;
if (self.verbose>3) self.out('GC');
for(n in self.nodes) {
node=self.nodes[n];
for(p in node.processes.gone) {
if (node.processes.gone[p]) {
node.processes.gone[p].tmo -= 500;
if (node.processes.gone[p].tmo<=0) node.processes.gone[p]=undefined;
}
}
}
},500);
}
world.prototype.stop = function () {
}
var World = function (nodes,options) {
var obj=new world(nodes,options);
current.world=obj;
return obj;
}
module.exports = {
options:options,
World:World,
current:function (module) { current=module.current; Aios=module}
}
};
BundleModuleCode['jam/mobi']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: 15-1-16 by sbosse.
** $RCS: $Id: mobi.js,v 1.3 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.10.2
**
** $INFO:
**
** JavaScript AIOS Agent Mobilityn Module
**
** $ENDOFINFO
*/
var Comp = Require('com/compat');
var Net;
var options = {
debug:{},
version:'1.10.2'
}
function addr2url(addr) {
return typeof addr=='number'?String(addr):
(addr.proto?addr.proto+'://':'')+addr.address+':'+addr.port;
}
if (global.config.dos) Net=Require('dos/network');
var current=none;
var Aios = none;
/** Direction type; used with move and link? operations
* The link operation can eitehr return a boolean value or
* a list of reachable destiantions (PATH/IP only).
* NORTH, ..., are used for P2P connections only.
*/
var DIRS= ['NORTH','SOUTH','WEST','EAST','LEFT','RIGHT','UP','DOWN','ORIGIN','NW','NE','SW','SE',
'DELTA','RANGE','NODE','IP','PATH','CAP'];
/*
enum DIR = {
NORTH , SOUTH , .. ,
IP(ip:string) , ..
} : dir
tyoe dir = NORTH | SOUTH | .. | IP {tag,ip:string } | ..
*/
var DIR = {
NORTH:'DIR.NORTH',
SOUTH:'DIR.SOUTH',
WEST:'DIR.WEST',
EAST:'DIR.EAST',
LEFT:'DIR.LEFT',
RIGHT:'DIR.RIGHT',
UP:'DIR.UP',
DOWN:'DIR.DOWN',
ORIGIN:'DIR.ORIGIN',
NW:'DIR.NW',
NE:'DIR.NE',
SW:'DIR.SW',
SE:'DIR.SE',
BACKWARD:'DIR.BACKWARD',
FORWARD:'DIR.FORWARD',
OPPOSITE:'DIR.OPPOSITE',
// Assuming: z-> x N
// | W+E U(+z)/D(-z)
// v y S
DELTA: function (addr) { return {tag:"DIR.DELTA",delta:addr} },
// Only for link? operation
RANGE: function (r) { return {tag:"DIR.RANGE",radius:r} },
// Address a node (identifier name) directly
NODE: function (node) { return {tag:"DIR.NODE",node:node} },
IP:function (addr) { return {tag:"DIR.IP",ip:addr} },
/*
**
** Path can contain filter, e.g. range /distance[0-5], /distance[5], ..
** or sets of destinations, e.g., /node*
** or a hopping array [dest1,dest2,..]
** type of path = string | string array
*/
PATH:function (path) { return {tag:"DIR.PATH",path:path} },
CAP:function (cap) { return {tag:"DIR.CAP",cap:cap} }
}
DIR.tag = {
NORTH:'DIR.NORTH',
SOUTH:'DIR.SOUTH',
WEST:'DIR.WEST',
EAST:'DIR.EAST',
LEFT:'DIR.LEFT',
RIGHT:'DIR.RIGHT',
UP:'DIR.UP',
DOWN:'DIR.DOWN',
ORIGIN:'DIR.ORIGIN',
NW:'DIR.NW',
NE:'DIR.NE',
SW:'DIR.SW',
SE:'DIR.SE',
BACKWARD:'DIR.BACKWARD',
FORWARD:'DIR.FORWARD',
OPPOSITE:'DIR.OPPOSITE',
DELTA:'DIR.DELTA',
RANGE:'DIR.RANGE',
NODE:'DIR.NODE',
IP:'DIR.IP',
PATH:'DIR.PATH',
CAP:'DIR.CAP',
}
/** Back direction. In case of IP, the remote address on receiving agent code is used.
*/
function opposite (dir,next) {
var chan;
switch (dir.tag||dir) {
case DIR.NORTH: return DIR.SOUTH;
case DIR.SOUTH: return DIR.NORTH;
case DIR.WEST: return DIR.EAST;
case DIR.EAST: return DIR.WEST;
case DIR.LEFT: return DIR.RIGHT;
case DIR.RIGHT: return DIR.LEFT;
case DIR.UP: return DIR.DOWN;
case DIR.DOWN: return DIR.UP;
case DIR.NW: return DIR.SE;
case DIR.NE: return DIR.SW;
case DIR.SE: return DIR.NW;
case DIR.SW: return DIR.NE;
case DIR.BACKWARD: return DIR.FORWARD;
case DIR.FORWARD: return DIR.BACKWARD;
case DIR.OPPOSITE: return DIR.OPPOSITE;
case DIR.tag.DELTA:
if (!next) return DIR.DELTA(dir.delta.map(function (v) {return -v}));
else return;
case DIR.tag.IP:
// try to use current process back attribute containing remote IP address upon receiving
if (current.process && current.process.back && current.process.back.tag==DIR.tag.IP) return current.process.back;
else return none;
case DIR.tag.NODE:
// try to use current process back attribute containing remote IP address upon receiving
if (current.process && current.process.back) {
switch (current.process.back.tag) {
case DIR.tag.IP:
// Try to resolve node name
if (current.node && current.node.connections.ip && current.node.connections.ip.reverse)
return DIR.NODE(current.node.connections.ip.reverse(current.process.back.ip));
else
return current.process.back;
case DIR.tag.NODE:
return current.process.back;
default:
return none;
}
} else return none;
case 'DIR.PATH':
// TODO: this node name/path!
return none;
case 'DIR.CAP':
// TODO: this node capability!
return none;
default:
return none;
}
};
// Create a valid DIR compatible type from a lowercase name specifier (e.g., north -> DIR.NORTH
DIR.from = function (name) {
var Dir=name.toUpperCase();
if (DIRS.indexOf(Dir) == -1) return;
return {tag:'DIR.'+Dir}
}
// Create a valid lowercase name specifier from DIR (e.g. DIR.NORTH -> north)
DIR.to = function (dir) {
if ((dir.tag||dir).substr(0,4)!='DIR.') return;
return (dir.tag||dir).substr(4).toLowerCase();
}
DIR.isDir = function (o) {
return (o.tag||o).indexOf('DIR')==0;
}
DIR.opposite=opposite;
DIR.print = function (dir) {
if (!dir) return 'undefined';
var name=(dir.tag||dir).substring(4);
switch (dir.tag||dir) {
case 'DIR.DELTA':
return name+'('+Comp.printf.list(dir.delta)+')';
case 'DIR.RANGE':
return name+'('+dir.radius+')';
case 'DIR.NODE':
return name+'('+dir.node+')';
case 'DIR.IP':
return name+'('+(dir.ip==undefined?'*':dir.ip)+')';
case 'DIR.PATH':
return name+'('+dir.path+')';
case 'DIR.CAP':
return name+'('+dir.cao+')';
default:
if (!dir.ip) return name
else return name+'('+addr2url(dir.ip)+')';
}
};
DIR.toString = function (dir) {
function format(ip) {
if (ip==undefined) return '';
if (typeof ip == 'number') return ip;
if (typeof ip == 'string') return '"'+ip+'"';
}
switch (dir.tag) {
case DIR.NORTH: return 'DIR.North('+format(dir.ip)+')';
case DIR.SOUTH: return 'DIR.South('+format(dir.ip)+')';
case DIR.WEST: return 'DIR.West('+format(dir.ip)+')';
case DIR.EAST: return 'DIR.East('+format(dir.ip)+')';
case DIR.tag.IP: return 'DIR.IP('+format(dir.ip)+')';
}
return dir;
}
/** Search a channel that is connected to node 'destnode'
*
*/
function lookup(node,destnode) {
var chan,path;
if (node.connections.ip && node.connections.ip.lookup) {
path=node.connections.ip.lookup(destnode);
if (path) return {chan:node.connections.ip,dest:path};
}
}
/** Move current agent to new node
*
*/
function move(dir) {
var node1=current.node,
chan=none,
dest,
stat,
path,
alive = function () {return 1},
nokill=false,
name=DIR.to(dir),
msg;
switch (dir.tag||dir) {
case 'DIR.IP':
chan=node1.connections.ip;
dest=dir.ip;
break;
case 'DIR.DELTA':
current.process.dir=Comp.obj.copy(dir);
if (dir.delta[0]>0 && node1.connections.east && node1.connections.east.status())
current.process.dir.delta[0]--,chan=node1.connections.east;
else if (dir.delta[0]<0 && node1.connections.west && node1.connections.west.status())
current.process.dir.delta[0]++,chan=node1.connections.west;
else if (dir.delta[1]>0 && node1.connections.south && node1.connections.south.status())
current.process.dir.delta[1]--,chan=node1.connections.south;
else if (dir.delta[1]<0 && node1.connections.north && node1.connections.north.status())
current.process.dir.delta[1]++,chan=node1.connections.north;
else if (dir.delta[2]>0 && node1.connections.up && node1.connections.up.status())
current.process.dir.delta[2]--,chan=node1.connections.up;
else if (dir.delta[2]<0 && node1.connections.down && node1.connections.down.status())
current.process.dir.delta[2]++,chan=node1.connections.down;
break;
case 'DIR.NODE':
if (node1.connections.range &&
node1.connections.range[dir.node] &&
node1.connections.range[dir.node].status())
chan=node1.connections.range[dir.node],dest=dir.node;
else {
// Find node name -> channel mapping
dest=lookup(node1,dir.node);
if (dest) chan=dest.chan,dest=dest.dest;
}
break;
case 'DIR.PATH':
// TODO
// if (!current.network) {current.error='No connection to path '+dir.path; throw 'MOVE'};
if (Comp.obj.isArray(dir.path)) {
path=Comp.array.pop(dir.path);
} else path = dir.path;
chan=node1.connections.path; dest=path;
nokill=true;
break;
case 'DIR.CAP':
// TODO
if (!current.network) {current.error='No connection to server '+dir.cap; throw 'MOVE'};
chan=node1.connections.dos; dest=Net.Parse.capability(dir.cap).cap;
nokill=true;
break;
default:
if (!name) {
current.error='ENOCHANNEL';
throw 'MOVE';
}
chan=node1.connections[name];
}
// print(node1.connections);
// print(chan)
if (chan==none || !chan.status(dest)) {
current.error='No connection to direction '+DIR.print(dir); throw 'MOVE'
};
if (!current.process.back) current.process.back=Aios.DIR.opposite(dir);
node1.stats.migrate++;
if (Aios.options.fastcopy && chan.virtual) msg=Aios.Code.toObject(current.process);
else msg=Aios.Code.ofCode(current.process,false);
current.process.move=dir;
if (options.debug.move) console.log(dest,msg);
/* NEWCOMM | context is current process !!!! */
chan.send({agent:msg,to:dest,context:current.process});
// kill or supend ghost agent
if (!nokill) current.process.kill=true; // discard process now
else current.process.suspended=true; // discard process after send op finished
//print(current.process.print());
}
module.exports = {
agent:{
move:move,
opposite:opposite,
DIR:DIR
},
current:function (module) { current=module.current; Aios=module; },
DIR:DIR,
DIRS:DIRS,
options:options
}
};
BundleModuleCode['jam/watchdog']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 6-12-17 by sbosse.
** $RCS: $Id: watchdog.js,v 1.1 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.2.1
**
** $INFO:
**
** JavaScript AIOS native platform Watchdog Interface
**
**
** A watchdog provides a timer and some kind of protected envrionment executing a function.
** If the function execution time exceeds the timeout of the timer, an exception is thrown (pre-emptive).
** This exception can be handled by a scheduler for round-robinson scheduling.
** There are different watchdog functionalities provided by different JS VM platforms:
**
** 1A. Full (jvm) {start,stop}
** 1B. Full (jxcore+,pl3) {start,stop,protect}
** 2. Protected (Node + watchdog.node module) {start, stop, protect}
** 3. Partial with injected checkpointing (jxcore) {start, stop, checkpoint}
** 4. No (generic node, browser) {}
**
** $ENDOFINFO
*/
var version = '1.2.2'
if (typeof process != 'undefined' && process.env && !process.env.BOOTSTRAP) PATH.push('/opt/jam');
function search(index,module) {
if (PATH.length==index) return module;
var path=PATH[index];
if (Fs.existsSync(path+'/'+module)) return path+'/'+module;
else return search(index+1,module);
}
try {
var watchdog;
var Fs = Require('fs');
try {watchdog = process && process.binding && process.binding && process.binding('watchdog')} catch (e){}; // JX/JX+
if (!watchdog) watchdog = process && process.watchdog; // JX+
if (!watchdog && process && process.startWatchdog) watchdog={
// JVM
start:process.startWatchdog,
stop:process.stopWatchdog,
init:process.initWatchdog,
tick:process.tick
};
if (!watchdog) {
try { watchdog = require(search(0,'watchdog.node')) } catch (e) { }
}
if (!watchdog && process && process.version && Fs) {
// NODE
var nativePath,platformVersion;
if (process.version.match(/^v0.12/)) platformVersion="0.12";
else if (process.version.match(/^v3/)) platformVersion="3.x";
else if (process.version.match(/^v4/)) platformVersion="4.x";
else if (process.version.match(/^v5/)) platformVersion="5.x";
else if (process.version.match(/^v6/)) platformVersion="6.x";
else if (process.version.match(/^v7/)) platformVersion="7.x";
else if (process.version.match(/^v8/)) platformVersion="8.x";
else if (process.version.match(/^v9/)) platformVersion="9.x";
if (platformVersion && process.platform && process.arch)
nativePath = 'native/'+process.platform+'/'+platformVersion+'/' + process.arch + '/watchdog.node';
if (PATH)
if (nativePath) {
var _watchdog = require(search(0,nativePath));
watchdog = {
start : _watchdog.start,
stop : _watchdog.clear,
protect : _watchdog.protect,
init : function () {},
}
}
}
} catch (e) {
// console.log(e)
}
if (watchdog) {
module.exports={
start:watchdog.start||watchdog.startWatchdog,
stop:watchdog.stop||watchdog.stopWatchdog,
init:watchdog.init||watchdog.initWatchdog,
checkPoint:watchdog.checkPoint,
tick:watchdog.tick,
protect:watchdog.protect,
version : version
}
} else module=undefined;
};
BundleModuleCode['geoip/gps5']=function (module,exports){
/* GPS location based on an external database lookup */
/* requires https */
var serviceHost="location.services.mozilla.com",
servicePath="/v1/geolocate?key=test";
function geolocate (cb) {
var https;
if (typeof require == 'function') try {
https = require('https');
} catch (e) { /* TODO Browser */ }
if (!https) return cb(new Error('ENETWORK'));
var req = https.request({
hostname: serviceHost,
port: 443,
path: servicePath,
method: 'GET'
}, function (res) {
res.on('data', function (d){
try {
var json = JSON.parse(d);
cb(json)
} catch (e) { cb(e) };
});
})
req.on('error', function (e) {
console.error(e);
cb(e);
});
req.end();
}
module.exports = { geolocate : geolocate };
};
BundleModuleCode['geoip/geoloc5']=function (module,exports){
var Options = {
// stage1
locate : {
primary : {http:'ag-0.de:9999',https:'ag-0.de:9998',timeout:1000},
secondary : {http:'ip-api.com:80',https:'ip-api.com:80',path:'/json',timeout:2000},
},
// stage2
locate5 : {
secondary : {https:'location.services.mozilla.com:443',path:'/v1/geolocate?key=test'},
},
// stage 3: lat,lon -> location info lookup
reverse : {
primary : {https:'api.opencagedata.com',path:'/geocode/v1/json?q=LAT+LON&key=8e7c3730678842468d6acf450ecbca16'},
secondary: {https:'nominatim.openstreetmap.org',path:'/reverse/?format=json&lat=LAT&lon=LON'},
},
verbose : 0
}
var http=Require("http"),https; /* https on demand */
function ip(url) { return url.split(':')[0] }
function port(url) { return url.split(':')[1] }
// 1. GPS/GEO via IP and external database service
function stage1 (cb,options,location) {
var e,r;
if (!http.xhr && http.request) {
// node.js
e={hostname:ip(options.http),
path:options.path||'',method:"GET",
port:port(options.http)};
if (Options.verbose) console.log('locate.stage1',e);
r=http.request(e,function(a){
a.setEncoding("utf8");
var c="";
a.on("data",function(a){c+=a}),
a.on("end",function(){
try {
var info=JSON.parse(c);
if (Options.verbose) console.log('locate.stage1.res',info);
Object.assign(location,{
ip:info.query,
gps:{lat:info.lat,lon:info.lon},
geo:{city:info.city,country:info.country,countryCode:info.countryCode,region:info.region,zip:info.zip}
})
return cb(location);
} catch (err) {
return cb(err);
}
})
});
r.on("error",function(a) {
if (Options.verbose) console.log('locate.stage1.err',a);
return cb(a);
})
r.setTimeout(options.timeout,function() {
if (Options.verbose) console.log('locate.stage1.err',"ETIMEOUT");
return cb(new Error("ETIMEOUT"));
})
r.end();
} else {
// Browser
var proto=document.URL.indexOf('https')==0?'https':'http';
e={uri:proto+'://'+options[proto]+'/'+options.path,
method:"GET",
headers:{}};
if (Options.verbose) console.log('stage1',e);
http.request(e,function(err,xhr,body){
if (err) {
if (Options.verbose) console.log('locate.stage1.err',err);
return cb(err);
}
try {
var info=JSON.parse(body);
if (Options.verbose) console.log('locate.stage1.res',info);
Object.assign(location,{
ip:info.query,
gps:{lat:info.lat,lon:info.lon},
geo:{city:info.city,country:info.country,countryCode:info.countryCode,region:info.region,zip:info.zip}
})
return cb(location);
} catch (err) {
if (Options.verbose) console.log('locate.stage1.err',err);
return cb(err);
}
})
}
}
// 2. GPS via IP and external database service
function stage2 (cb,options,location) {
if (!https || !https.request) return cb(new Error('ENETWORK'));
if (!https.xhr && https.request) {
var e = {
hostname: ip(options.https),
port: port(options.https),
path: options.path,
method: 'GET'
}
if (Options.verbose) console.log('locate.stage2',e);
var req = https.request(e, function (res) {
res.on('data', function (d){
try {
var pos = JSON.parse(d);
if (Options.verbose) console.log('locate.stage3.res',pos);
location.gps5 = { lat: pos.location.lat, lon:pos.location.lng }
cb(location)
} catch (e) { if (Options.verbose) console.log('locate.stage2.err',e); cb(e) };
});
})
req.on('error', function (e) {
cb(e);
});
req.end();
} else {
// Browser
e={uri:'https://'+options.https+'/'+options.path,
method:"GET",
headers:{}};
if (Options.verbose) console.log('locate.stage2',e);
https.request(e,function(err,xhr,body){
if (err) {
if (Options.verbose) console.log('locate.stage2.err',err);
return cb(err);
}
try {
var pos = JSON.parse(body);
if (Options.verbose) console.log('locate.stage2.res',pos);
location.gps5 = { lat: pos.location.lat, lon:pos.location.lng }
return cb(location);
} catch (err) {
if (Options.verbose) console.log('locate.stage2.err',err);
return cb(err);
}
})
}
}
// GPS -> GEO mapping by external database service
function stage3 (cb,options,location) {
if (!https || !https.request) return cb(new Error('ENETWORK'));
options.path=options.path
.replace(/LAT/,location.gps5.lat)
.replace(/LON/,location.gps5.lon);
if (!https.xhr && https.request) {
var e = {
hostname: ip(options.https),
port: port(options.https),
path: options.path,
method: 'GET'
}
if (Options.verbose) console.log('locate.stage3',e);
var req = https.request(e, function (res) {
res.on('data', function (d){
try {
var res = JSON.parse(d);
var loc;
if (Options.verbose) console.log('locate.stage3.res',res);
if (res.address) loc=res.address;
else if (res.results && res.results[0]) loc=res.results[0].components;
location.geo5 = {
city:loc.city,
zip:loc.postcode,
street:loc.road,
number:loc.house_number,
country:loc.country
}
cb(location)
} catch (e) { if (Options.verbose) console.log('locate.stage3.err',e); cb(e) };
});
})
req.on('error', function (e) {
if (Options.verbose) console.log('locate.stage3.err',e);
cb(e);
});
req.end();
} else {
// Browser
e={uri:'https://'+options.https+'/'+options.path,
method:"GET",
headers:{}};
if (Options.verbose) console.log('locate.stage3',e);
https.request(e,function(err,xhr,body){
if (err) {
if (Options.verbose) console.log('locate.stage3.err',err);
return cb(err);
}
try {
var res = JSON.parse(body);
var loc;
if (Options.verbose) console.log('locate.stage3.res',res);
if (res.address) loc=res.address;
else if (res.results && res.results[0]) loc=res.results[0].components;
location.geo5 = {
city:loc.city,
zip:loc.postcode,
street:loc.road,
number:loc.house_number,
country:loc.country
}
return cb(location);
} catch (err) {
if (Options.verbose) console.log('locate.stage3.err',err);
return cb(err);
}
})
}
}
var todo = {
// 1. Direct ISP - IP - GPS/GEO lookup
// 1a. with proxy
stage1A: function (cb,errors,location) {
stage1(function (res) {
if (res instanceof Error) {
errors.push({url:Options.locate.primary,error:res});
todo.stage1B(cb,errors,location);
} else {
todo.stage2A(cb,errors,location);
}
},Options.locate.primary,location);
},
// 1b. w/o proxy
stage1B: function (cb,errors,location) {
stage1(function (res) {
if (res instanceof Error) {
errors.push({url:Options.locate.secondary,error:res});
todo.stage2A(cb,errors,location);
} else {
todo.stage2A(cb,errors,location);
}
},Options.locate.secondary,location);
},
// 2. Get geo position (lat,lon)
stage2A: function (cb,errors,location) {
stage2(function (res) {
if (res instanceof Error) {
errors.push({url:Options.locate5.secondary,error:res});
todo.finalize(cb,errors,location);
} else {
todo.stage2B(cb,errors,location);
}
},Options.locate5.secondary,location);
},
// 3. Get geo location (country,..)
stage2B: function (cb,errors,location) {
stage3(function (res) {
if (res instanceof Error) {
errors.push({url:Options.reverse.primary,x:1,error:res});
todo.finalize(cb,errors,location);
} else {
todo.finalize(cb,errors,location);
}
},Options.reverse.primary,location);
},
finalize : function (cb,errors,location) {
cb(location,errors);
}
}
function locate (cb,options) {
var e;
if (options) Options=Object.assign(Options,options);
if (typeof require == 'function' && !https) try {
https = require('https');
} catch (e) { /* TODO Browser */ } else if (http.xhr) https = http;
if (!http) return cb(new Error('ENOTSUPPORTED'));
todo.stage1A(cb,[],{});
return;
}
module.exports={locate:locate,options:Options};
};
BundleModuleCode['os/platform']=function (module,exports){
/*!
* Platform.js
* get OS/Cpu/Arch/memory/.. information in nodejs and browser
* Copyright 2014-2018 Benjamin Tan
* Copyright 2011-2013 John-David Dalton
* Available under MIT license
* Modified by @blab
* Ver. 1.2.3
*/
if (global.TARGET=='browser') {
/*
Returns:
platform.name; // 'Safari'
platform.version; // '5.1'
platform.product; // 'iPad'
platform.manufacturer; // 'Apple'
platform.engine; // 'WebKit'
platform.os; // 'iOS 5.0'
platform.description; // 'Safari 5.1 on Apple iPad (iOS 5.0)'
platform.mobile; // 'true'
platform.touch; // 'false'
platform.geoloc; // 'true'
*/
/** Used to determine if values are of the language type `Object`. */
var objectTypes = {
'function': true,
'object': true
};
/** Used as a reference to the global object. */
var root = (objectTypes[typeof window] && window) || this;
/** Backup possible global object. */
var oldRoot = root;
/** Detect free variable `exports`. */
var freeExports = objectTypes[typeof exports] && exports;
/** Detect free variable `module`. */
var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
/** Detect free variable `global` from Node.js or Browserified code and use it as `root`. */
var freeGlobal = freeExports && freeModule && typeof global == 'object' && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) {
root = freeGlobal;
}
/**
* Used as the maximum length of an array-like object.
* See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength)
* for more details.
*/
var maxSafeInteger = Math.pow(2, 53) - 1;
/** Regular expression to detect Opera. */
var reOpera = /\bOpera/;
/** Possible global object. */
var thisBinding = this;
/** Used for native method references. */
var objectProto = Object.prototype;
/** Used to check for own properties of an object. */
var hasOwnProperty = objectProto.hasOwnProperty;
/** Used to resolve the internal `[[Class]]` of values. */
var toString = objectProto.toString;
/*--------------------------------------------------------------------------*/
/**
* Capitalizes a string value.
*
* @private
* @param {string} string The string to capitalize.
* @returns {string} The capitalized string.
*/
function capitalize(string) {
string = String(string);
return string.charAt(0).toUpperCase() + string.slice(1);
}
/**
* A utility function to clean up the OS name.
*
* @private
* @param {string} os The OS name to clean up.
* @param {string} [pattern] A `RegExp` pattern matching the OS name.
* @param {string} [label] A label for the OS.
*/
function cleanupOS(os, pattern, label) {
// Platform tokens are defined at:
// http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
// http://web.archive.org/web/20081122053950/http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx
var data = {
'10.0': '10',
'6.4': '10 Technical Preview',
'6.3': '8.1',
'6.2': '8',
'6.1': 'Server 2008 R2 / 7',
'6.0': 'Server 2008 / Vista',
'5.2': 'Server 2003 / XP 64-bit',
'5.1': 'XP',
'5.01': '2000 SP1',
'5.0': '2000',
'4.0': 'NT',
'4.90': 'ME'
};
// Detect Windows version from platform tokens.
if (pattern && label && /^Win/i.test(os) && !/^Windows Phone /i.test(os) &&
(data = data[/[\d.]+$/.exec(os)])) {
os = 'Windows ' + data;
}
// Correct character case and cleanup string.
os = String(os);
if (pattern && label) {
os = os.replace(RegExp(pattern, 'i'), label);
}
os = format(
os.replace(/ ce$/i, ' CE')
.replace(/\bhpw/i, 'web')
.replace(/\bMacintosh\b/, 'Mac OS')
.replace(/_PowerPC\b/i, ' OS')
.replace(/\b(OS X) [^ \d]+/i, '$1')
.replace(/\bMac (OS X)\b/, '$1')
.replace(/\/(\d)/, ' $1')
.replace(/_/g, '.')
.replace(/(?: BePC|[ .]*fc[ \d.]+)$/i, '')
.replace(/\bx86\.64\b/gi, 'x86_64')
.replace(/\b(Windows Phone) OS\b/, '$1')
.replace(/\b(Chrome OS \w+) [\d.]+\b/, '$1')
.split(' on ')[0]
);
return os;
}
/**
* An iteration utility for arrays and objects.
*
* @private
* @param {Array|Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
*/
function each(object, callback) {
var index = -1,
length = object ? object.length : 0;
if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {
while (++index < length) {
callback(object[index], index, object);
}
} else {
forOwn(object, callback);
}
}
/**
* Trim and conditionally capitalize string values.
*
* @private
* @param {string} string The string to format.
* @returns {string} The formatted string.
*/
function format(string) {
string = trim(string);
return /^(?:webOS|i(?:OS|P))/.test(string)
? string
: capitalize(string);
}
/**
* Iterates over an object's own properties, executing the `callback` for each.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} callback The function executed per own property.
*/
function forOwn(object, callback) {
for (var key in object) {
if (hasOwnProperty.call(object, key)) {
callback(object[key], key, object);
}
}
}
/**
* Gets the internal `[[Class]]` of a value.
*
* @private
* @param {*} value The value.
* @returns {string} The `[[Class]]`.
*/
function getClassOf(value) {
return value == null
? capitalize(value)
: toString.call(value).slice(8, -1);
}
/**
* Host objects can return type values that are different from their actual
* data type. The objects we are concerned with usually return non-primitive
* types of "object", "function", or "unknown".
*
* @private
* @param {*} object The owner of the property.
* @param {string} property The property to check.
* @returns {boolean} Returns `true` if the property value is a non-primitive, else `false`.
*/
function isHostType(object, property) {
var type = object != null ? typeof object[property] : 'number';
return !/^(?:boolean|number|string|undefined)$/.test(type) &&
(type == 'object' ? !!object[property] : true);
}
/**
* Prepares a string for use in a `RegExp` by making hyphens and spaces optional.
*
* @private
* @param {string} string The string to qualify.
* @returns {string} The qualified string.
*/
function qualify(string) {
return String(string).replace(/([ -])(?!$)/g, '$1?');
}
/**
* A bare-bones `Array#reduce` like utility function.
*
* @private
* @param {Array} array The array to iterate over.
* @param {Function} callback The function called per iteration.
* @returns {*} The accumulated result.
*/
function reduce(array, callback) {
var accumulator = null;
each(array, function(value, index) {
accumulator = callback(accumulator, value, index, array);
});
return accumulator;
}
/**
* Removes leading and trailing whitespace from a string.
*
* @private
* @param {string} string The string to trim.
* @returns {string} The trimmed string.
*/
function trim(string) {
return String(string).replace(/^ +| +$/g, '');
}
/*--------------------------------------------------------------------------*/
/**
* Creates a new platform object.
*
* @memberOf platform
* @param {Object|string} [ua=navigator.userAgent] The user agent string or
* context object.
* @returns {Object} A platform object.
*/
function parse(ua) {
/** The environment context object. */
var context = root;
/** Used to flag when a custom context is provided. */
var isCustomContext = ua && typeof ua == 'object' && getClassOf(ua) != 'String';
// Juggle arguments.
if (isCustomContext) {
context = ua;
ua = null;
}
/** Browser navigator object. */
var nav = context.navigator || {};
/** Browser user agent string. */
var userAgent = nav.userAgent || '';
ua || (ua = userAgent);
/** Used to flag when `thisBinding` is the [ModuleScope]. */
var isModuleScope = isCustomContext || thisBinding == oldRoot;
/** Used to detect if browser is like Chrome. */
var likeChrome = isCustomContext
? !!nav.likeChrome
: /\bChrome\b/.test(ua) && !/internal|\n/i.test(toString.toString());
/** Internal `[[Class]]` value shortcuts. */
var objectClass = 'Object',
airRuntimeClass = isCustomContext ? objectClass : 'ScriptBridgingProxyObject',
enviroClass = isCustomContext ? objectClass : 'Environment',
javaClass = (isCustomContext && context.java) ? 'JavaPackage' : getClassOf(context.java),
phantomClass = isCustomContext ? objectClass : 'RuntimeObject';
/** Detect Java environments. */
var java = /\bJava/.test(javaClass) && context.java;
/** Detect Rhino. */
var rhino = java && getClassOf(context.environment) == enviroClass;
/** A character to represent alpha. */
var alpha = java ? 'a' : '\u03b1';
/** A character to represent beta. */
var beta = java ? 'b' : '\u03b2';
/** Browser document object. */
var doc = context.document || {};
/**
* Detect Opera browser (Presto-based).
* http://www.howtocreate.co.uk/operaStuff/operaObject.html
* http://dev.opera.com/articles/view/opera-mini-web-content-authoring-guidelines/#operamini
*/
var opera = context.operamini || context.opera;
/** Opera `[[Class]]`. */
var operaClass = reOpera.test(operaClass = (isCustomContext && opera) ? opera['[[Class]]'] : getClassOf(opera))
? operaClass
: (opera = null);
/*------------------------------------------------------------------------*/
/** Temporary variable used over the script's lifetime. */
var data;
/** The CPU architecture. */
var arch = ua;
/** Platform description array. */
var description = [];
/** Platform alpha/beta indicator. */
var prerelease = null;
/** A flag to indicate that environment features should be used to resolve the platform. */
var useFeatures = ua == userAgent;
/** The browser/environment version. */
var version = useFeatures && opera && typeof opera.version == 'function' && opera.version();
/** A flag to indicate if the OS ends with "/ Version" */
var isSpecialCasedOS;
/* Detectable layout engines (order is important). */
var layout = getLayout([
{ 'label': 'EdgeHTML', 'pattern': '(?:Edge|EdgA|EdgiOS)' },
'Trident',
{ 'label': 'WebKit', 'pattern': 'AppleWebKit' },
'iCab',
'Presto',
'NetFront',
'Tasman',
'KHTML',
'Gecko'
]);
/* Detectable browser names (order is important). */
var name = getName([
'Adobe AIR',
'Arora',
'Avant Browser',
'Breach',
'Camino',
'Electron',
'Epiphany',
'Fennec',
'Flock',
'Galeon',
'GreenBrowser',
'iCab',
'Iceweasel',
'K-Meleon',
'Konqueror',
'Lunascape',
'Maxthon',
{ 'label': 'Microsoft Edge', 'pattern': '(?:Edge|Edg|EdgA|EdgiOS)' },
'Midori',
'Nook Browser',
'PaleMoon',
'PhantomJS',
'Raven',
'Rekonq',
'RockMelt',
{ 'label': 'Samsung Internet', 'pattern': 'SamsungBrowser' },
'SeaMonkey',
{ 'label': 'Silk', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
'Sleipnir',
'SlimBrowser',
{ 'label': 'SRWare Iron', 'pattern': 'Iron' },
'Sunrise',
'Swiftfox',
'Waterfox',
'WebPositive',
'Opera Mini',
{ 'label': 'Opera Mini', 'pattern': 'OPiOS' },
'Opera',
{ 'label': 'Opera', 'pattern': 'OPR' },
'Chrome',
{ 'label': 'Chrome Mobile', 'pattern': '(?:CriOS|CrMo)' },
{ 'label': 'Firefox', 'pattern': '(?:Firefox|Minefield)' },
{ 'label': 'Firefox for iOS', 'pattern': 'FxiOS' },
{ 'label': 'IE', 'pattern': 'IEMobile' },
{ 'label': 'IE', 'pattern': 'MSIE' },
'Safari'
]);
/* Detectable products (order is important). */
var product = getProduct([
{ 'label': 'BlackBerry', 'pattern': 'BB10' },
'BlackBerry',
{ 'label': 'Galaxy S', 'pattern': 'GT-I9000' },
{ 'label': 'Galaxy S2', 'pattern': 'GT-I9100' },
{ 'label': 'Galaxy S3', 'pattern': 'GT-I9300' },
{ 'label': 'Galaxy S4', 'pattern': 'GT-I9500' },
{ 'label': 'Galaxy S5', 'pattern': 'SM-G900' },
{ 'label': 'Galaxy S6', 'pattern': 'SM-G920' },
{ 'label': 'Galaxy S6 Edge', 'pattern': 'SM-G925' },
{ 'label': 'Galaxy S7', 'pattern': 'SM-G930' },
{ 'label': 'Galaxy S7 Edge', 'pattern': 'SM-G935' },
'Google TV',
'Lumia',
'iPad',
'iPod',
'iPhone',
'Kindle',
{ 'label': 'Kindle Fire', 'pattern': '(?:Cloud9|Silk-Accelerated)' },
'Nexus',
'Nook',
'PlayBook',
'PlayStation Vita',
'PlayStation',
'TouchPad',
'Transformer',
{ 'label': 'Wii U', 'pattern': 'WiiU' },
'Wii',
'Xbox One',
{ 'label': 'Xbox 360', 'pattern': 'Xbox' },
'Xoom'
]);
/* Detectable manufacturers. */
var manufacturer = getManufacturer({
'Apple': { 'iPad': 1, 'iPhone': 1, 'iPod': 1 },
'Archos': {},
'Amazon': { 'Kindle': 1, 'Kindle Fire': 1 },
'Asus': { 'Transformer': 1 },
'Barnes & Noble': { 'Nook': 1 },
'BlackBerry': { 'PlayBook': 1 },
'Google': { 'Google TV': 1, 'Nexus': 1 },
'HP': { 'TouchPad': 1 },
'HTC': {},
'LG': {},
'Microsoft': { 'Xbox': 1, 'Xbox One': 1 },
'Motorola': { 'Xoom': 1 },
'Nintendo': { 'Wii U': 1, 'Wii': 1 },
'Nokia': { 'Lumia': 1 },
'Samsung': { 'Galaxy S': 1, 'Galaxy S2': 1, 'Galaxy S3': 1, 'Galaxy S4': 1 },
'Sony': { 'PlayStation': 1, 'PlayStation Vita': 1 }
});
/* Detectable operating systems (order is important). */
var os = getOS([
'Windows Phone',
'Android',
'CentOS',
{ 'label': 'Chrome OS', 'pattern': 'CrOS' },
'Debian',
'Fedora',
'FreeBSD',
'Gentoo',
'Haiku',
'Kubuntu',
'Linux Mint',
'OpenBSD',
'Red Hat',
'SuSE',
'Ubuntu',
'Xubuntu',
'Cygwin',
'Symbian OS',
'hpwOS',
'webOS ',
'webOS',
'Tablet OS',
'Tizen',
'Linux',
'Mac OS X',
'Macintosh',
'Mac',
'Windows 98;',
'Windows ',
'SunOS',
]);
/*------------------------------------------------------------------------*/
/**
* Picks the layout engine from an array of guesses.
*
* @private
* @param {Array} guesses An array of guesses.
* @returns {null|string} The detected layout engine.
*/
function getLayout(guesses) {
return reduce(guesses, function(result, guess) {
return result || RegExp('\\b' + (
guess.pattern || qualify(guess)
) + '\\b', 'i').exec(ua) && (guess.label || guess);
});
}
/**
* Picks the manufacturer from an array of guesses.
*
* @private
* @param {Array} guesses An object of guesses.
* @returns {null|string} The detected manufacturer.
*/
function getManufacturer(guesses) {
return reduce(guesses, function(result, value, key) {
// Lookup the manufacturer by product or scan the UA for the manufacturer.
return result || (
value[product] ||
value[/^[a-z]+(?: +[a-z]+\b)*/i.exec(product)] ||
RegExp('\\b' + qualify(key) + '(?:\\b|\\w*\\d)', 'i').exec(ua)
) && key;
});
}
/**
* Picks the browser name from an array of guesses.
*
* @private
* @param {Array} guesses An array of guesses.
* @returns {null|string} The detected browser name.
*/
function getName(guesses) {
return reduce(guesses, function(result, guess) {
return result || RegExp('\\b' + (
guess.pattern || qualify(guess)
) + '\\b', 'i').exec(ua) && (guess.label || guess);
});
}
/**
* Picks the OS name from an array of guesses.
*
* @private
* @param {Array} guesses An array of guesses.
* @returns {null|string} The detected OS name.
*/
function getOS(guesses) {
return reduce(guesses, function(result, guess) {
var pattern = guess.pattern || qualify(guess);
if (!result && (result =
RegExp('\\b' + pattern + '(?:/[\\d.]+|[ \\w.]*)', 'i').exec(ua)
)) {
result = cleanupOS(result, pattern, guess.label || guess);
}
return result;
});
}
/**
* Picks the product name from an array of guesses.
*
* @private
* @param {Array} guesses An array of guesses.
* @returns {null|string} The detected product name.
*/
function getProduct(guesses) {
return reduce(guesses, function(result, guess) {
var pattern = guess.pattern || qualify(guess);
if (!result && (result =
RegExp('\\b' + pattern + ' *\\d+[.\\w_]*', 'i').exec(ua) ||
RegExp('\\b' + pattern + ' *\\w+-[\\w]*', 'i').exec(ua) ||
RegExp('\\b' + pattern + '(?:; *(?:[a-z]+[_-])?[a-z]+\\d+|[^ ();-]*)', 'i').exec(ua)
)) {
// Split by forward slash and append product version if needed.
if ((result = String((guess.label && !RegExp(pattern, 'i').test(guess.label)) ? guess.label : result).split('/'))[1] && !/[\d.]+/.test(result[0])) {
result[0] += ' ' + result[1];
}
// Correct character case and cleanup string.
guess = guess.label || guess;
result = format(result[0]
.replace(RegExp(pattern, 'i'), guess)
.replace(RegExp('; *(?:' + guess + '[_-])?', 'i'), ' ')
.replace(RegExp('(' + guess + ')[-_.]?(\\w)', 'i'), '$1 $2'));
}
return result;
});
}
/**
* Resolves the version using an array of UA patterns.
*
* @private
* @param {Array} patterns An array of UA patterns.
* @returns {null|string} The detected version.
*/
function getVersion(patterns) {
return reduce(patterns, function(result, pattern) {
return result || (RegExp(pattern +
'(?:-[\\d.]+/|(?: for [\\w-]+)?[ /-])([\\d.]+[^ ();/_-]*)', 'i').exec(ua) || 0)[1] || null;
});
}
/**
* Returns `platform.description` when the platform object is coerced to a string.
*
* @name toString
* @memberOf platform
* @returns {string} Returns `platform.description` if available, else an empty string.
*/
function toStringPlatform() {
return this.description || '';
}
/*------------------------------------------------------------------------*/
// Convert layout to an array so we can add extra details.
layout && (layout = [layout]);
// Detect product names that contain their manufacturer's name.
if (manufacturer && !product) {
product = getProduct([manufacturer]);
}
// Clean up Google TV.
if ((data = /\bGoogle TV\b/.exec(product))) {
product = data[0];
}
// Detect simulators.
if (/\bSimulator\b/i.test(ua)) {
product = (product ? product + ' ' : '') + 'Simulator';
}
// Detect Opera Mini 8+ running in Turbo/Uncompressed mode on iOS.
if (name == 'Opera Mini' && /\bOPiOS\b/.test(ua)) {
description.push('running in Turbo/Uncompressed mode');
}
// Detect IE Mobile 11.
if (name == 'IE' && /\blike iPhone OS\b/.test(ua)) {
data = parse(ua.replace(/like iPhone OS/, ''));
manufacturer = data.manufacturer;
product = data.product;
}
// Detect iOS.
else if (/^iP/.test(product)) {
name || (name = 'Safari');
os = 'iOS' + ((data = / OS ([\d_]+)/i.exec(ua))
? ' ' + data[1].replace(/_/g, '.')
: '');
}
// Detect Kubuntu.
else if (name == 'Konqueror' && !/buntu/i.test(os)) {
os = 'Kubuntu';
}
// Detect Android browsers.
else if ((manufacturer && manufacturer != 'Google' &&
((/Chrome/.test(name) && !/\bMobile Safari\b/i.test(ua)) || /\bVita\b/.test(product))) ||
(/\bAndroid\b/.test(os) && /^Chrome/.test(name) && /\bVersion\//i.test(ua))) {
name = 'Android Browser';
os = /\bAndroid\b/.test(os) ? os : 'Android';
}
// Detect Silk desktop/accelerated modes.
else if (name == 'Silk') {
if (!/\bMobi/i.test(ua)) {
os = 'Android';
description.unshift('desktop mode');
}
if (/Accelerated *= *true/i.test(ua)) {
description.unshift('accelerated');
}
}
// Detect PaleMoon identifying as Firefox.
else if (name == 'PaleMoon' && (data = /\bFirefox\/([\d.]+)\b/.exec(ua))) {
description.push('identifying as Firefox ' + data[1]);
}
// Detect Firefox OS and products running Firefox.
else if (name == 'Firefox' && (data = /\b(Mobile|Tablet|TV)\b/i.exec(ua))) {
os || (os = 'Firefox OS');
product || (product = data[1]);
}
// Detect false positives for Firefox/Safari.
else if (!name || (data = !/\bMinefield\b/i.test(ua) && /\b(?:Firefox|Safari)\b/.exec(name))) {
// Escape the `/` for Firefox 1.
if (name && !product && /[\/,]|^[^(]+?\)/.test(ua.slice(ua.indexOf(data + '/') + 8))) {
// Clear name of false positives.
name = null;
}
// Reassign a generic name.
if ((data = product || manufacturer || os) &&
(product || manufacturer || /\b(?:Android|Symbian OS|Tablet OS|webOS)\b/.test(os))) {
name = /[a-z]+(?: Hat)?/i.exec(/\bAndroid\b/.test(os) ? os : data) + ' Browser';
}
}
// Add Chrome version to description for Electron.
else if (name == 'Electron' && (data = (/\bChrome\/([\d.]+)\b/.exec(ua) || 0)[1])) {
description.push('Chromium ' + data);
}
// Detect non-Opera (Presto-based) versions (order is important).
if (!version) {
version = getVersion([
'(?:Cloud9|CriOS|CrMo|Edge|Edg|EdgA|EdgiOS|FxiOS|IEMobile|Iron|Opera ?Mini|OPiOS|OPR|Raven|SamsungBrowser|Silk(?!/[\\d.]+$))',
'Version',
qualify(name),
'(?:Firefox|Minefield|NetFront)'
]);
}
// Detect stubborn layout engines.
if ((data =
layout == 'iCab' && parseFloat(version) > 3 && 'WebKit' ||
/\bOpera\b/.test(name) && (/\bOPR\b/.test(ua) ? 'Blink' : 'Presto') ||
/\b(?:Midori|Nook|Safari)\b/i.test(ua) && !/^(?:Trident|EdgeHTML)$/.test(layout) && 'WebKit' ||
!layout && /\bMSIE\b/i.test(ua) && (os == 'Mac OS' ? 'Tasman' : 'Trident') ||
layout == 'WebKit' && /\bPlayStation\b(?! Vita\b)/i.test(name) && 'NetFront'
)) {
layout = [data];
}
// Detect Windows Phone 7 desktop mode.
if (name == 'IE' && (data = (/; *(?:XBLWP|ZuneWP)(\d+)/i.exec(ua) || 0)[1])) {
name += ' Mobile';
os = 'Windows Phone ' + (/\+$/.test(data) ? data : data + '.x');
description.unshift('desktop mode');
}
// Detect Windows Phone 8.x desktop mode.
else if (/\bWPDesktop\b/i.test(ua)) {
name = 'IE Mobile';
os = 'Windows Phone 8.x';
description.unshift('desktop mode');
version || (version = (/\brv:([\d.]+)/.exec(ua) || 0)[1]);
}
// Detect IE 11 identifying as other browsers.
else if (name != 'IE' && layout == 'Trident' && (data = /\brv:([\d.]+)/.exec(ua))) {
if (name) {
description.push('identifying as ' + name + (version ? ' ' + version : ''));
}
name = 'IE';
version = data[1];
}
// Leverage environment features.
if (useFeatures) {
// Detect server-side environments.
// Rhino has a global function while others have a global object.
if (isHostType(context, 'global')) {
if (java) {
data = java.lang.System;
arch = data.getProperty('os.arch');
os = os || data.getProperty('os.name') + ' ' + data.getProperty('os.version');
}
if (rhino) {
try {
version = context.require('ringo/engine').version.join('.');
name = 'RingoJS';
} catch(e) {
if ((data = context.system) && data.global.system == context.system) {
name = 'Narwhal';
os || (os = data[0].os || null);
}
}
if (!name) {
name = 'Rhino';
}
}
else if (
typeof context.process == 'object' && !context.process.browser &&
(data = context.process)
) {
if (typeof data.versions == 'object') {
if (typeof data.versions.electron == 'string') {
description.push('Node ' + data.versions.node);
name = 'Electron';
version = data.versions.electron;
} else if (typeof data.versions.nw == 'string') {
description.push('Chromium ' + version, 'Node ' + data.versions.node);
name = 'NW.js';
version = data.versions.nw;
}
}
if (!name) {
name = 'Node.js';
arch = data.arch;
os = data.platform;
version = /[\d.]+/.exec(data.version);
version = version ? version[0] : null;
}
}
}
// Detect Adobe AIR.
else if (getClassOf((data = context.runtime)) == airRuntimeClass) {
name = 'Adobe AIR';
os = data.flash.system.Capabilities.os;
}
// Detect PhantomJS.
else if (getClassOf((data = context.phantom)) == phantomClass) {
name = 'PhantomJS';
version = (data = data.version || null) && (data.major + '.' + data.minor + '.' + data.patch);
}
// Detect IE compatibility modes.
else if (typeof doc.documentMode == 'number' && (data = /\bTrident\/(\d+)/i.exec(ua))) {
// We're in compatibility mode when the Trident version + 4 doesn't
// equal the document mode.
version = [version, doc.documentMode];
if ((data = +data[1] + 4) != version[1]) {
description.push('IE ' + version[1] + ' mode');
layout && (layout[1] = '');
version[1] = data;
}
version = name == 'IE' ? String(version[1].toFixed(1)) : version[0];
}
// Detect IE 11 masking as other browsers.
else if (typeof doc.documentMode == 'number' && /^(?:Chrome|Firefox)\b/.test(name)) {
description.push('masking as ' + name + ' ' + version);
name = 'IE';
version = '11.0';
layout = ['Trident'];
os = 'Windows';
}
os = os && format(os);
}
// Detect prerelease phases.
if (version && (data =
/(?:[ab]|dp|pre|[ab]\d+pre)(?:\d+\+?)?$/i.exec(version) ||
/(?:alpha|beta)(?: ?\d)?/i.exec(ua + ';' + (useFeatures && nav.appMinorVersion)) ||
/\bMinefield\b/i.test(ua) && 'a'
)) {
prerelease = /b/i.test(data) ? 'beta' : 'alpha';
version = version.replace(RegExp(data + '\\+?$'), '') +
(prerelease == 'beta' ? beta : alpha) + (/\d+\+?/.exec(data) || '');
}
// Detect Firefox Mobile.
if (name == 'Fennec' || name == 'Firefox' && /\b(?:Android|Firefox OS)\b/.test(os)) {
name = 'Firefox Mobile';
}
// Obscure Maxthon's unreliable version.
else if (name == 'Maxthon' && version) {
version = version.replace(/\.[\d.]+/, '.x');
}
// Detect Xbox 360 and Xbox One.
else if (/\bXbox\b/i.test(product)) {
if (product == 'Xbox 360') {
os = null;
}
if (product == 'Xbox 360' && /\bIEMobile\b/.test(ua)) {
description.unshift('mobile mode');
}
}
// Add mobile postfix.
else if ((/^(?:Chrome|IE|Opera)$/.test(name) || name && !product && !/Browser|Mobi/.test(name)) &&
(os == 'Windows CE' || /Mobi/i.test(ua))) {
name += ' Mobile';
}
// Detect IE platform preview.
else if (name == 'IE' && useFeatures) {
try {
if (context.external === null) {
description.unshift('platform preview');
}
} catch(e) {
description.unshift('embedded');
}
}
// Detect BlackBerry OS version.
// http://docs.blackberry.com/en/developers/deliverables/18169/HTTP_headers_sent_by_BB_Browser_1234911_11.jsp
else if ((/\bBlackBerry\b/.test(product) || /\bBB10\b/.test(ua)) && (data =
(RegExp(product.replace(/ +/g, ' *') + '/([.\\d]+)', 'i').exec(ua) || 0)[1] ||
version
)) {
data = [data, /BB10/.test(ua)];
os = (data[1] ? (product = null, manufacturer = 'BlackBerry') : 'Device Software') + ' ' + data[0];
version = null;
}
// Detect Opera identifying/masking itself as another browser.
// http://www.opera.com/support/kb/view/843/
else if (this != forOwn && product != 'Wii' && (
(useFeatures && opera) ||
(/Opera/.test(name) && /\b(?:MSIE|Firefox)\b/i.test(ua)) ||
(name == 'Firefox' && /\bOS X (?:\d+\.){2,}/.test(os)) ||
(name == 'IE' && (
(os && !/^Win/.test(os) && version > 5.5) ||
/\bWindows XP\b/.test(os) && version > 8 ||
version == 8 && !/\bTrident\b/.test(ua)
))
) && !reOpera.test((data = parse.call(forOwn, ua.replace(reOpera, '') + ';'))) && data.name) {
// When "identifying", the UA contains both Opera and the other browser's name.
data = 'ing as ' + data.name + ((data = data.version) ? ' ' + data : '');
if (reOpera.test(name)) {
if (/\bIE\b/.test(data) && os == 'Mac OS') {
os = null;
}
data = 'identify' + data;
}
// When "masking", the UA contains only the other browser's name.
else {
data = 'mask' + data;
if (operaClass) {
name = format(operaClass.replace(/([a-z])([A-Z])/g, '$1 $2'));
} else {
name = 'Opera';
}
if (/\bIE\b/.test(data)) {
os = null;
}
if (!useFeatures) {
version = null;
}
}
layout = ['Presto'];
description.push(data);
}
// Detect WebKit Nightly and approximate Chrome/Safari versions.
if ((data = (/\bAppleWebKit\/([\d.]+\+?)/i.exec(ua) || 0)[1])) {
// Correct build number for numeric comparison.
// (e.g. "532.5" becomes "532.05")
data = [parseFloat(data.replace(/\.(\d)$/, '.0$1')), data];
// Nightly builds are postfixed with a "+".
if (name == 'Safari' && data[1].slice(-1) == '+') {
name = 'WebKit Nightly';
prerelease = 'alpha';
version = data[1].slice(0, -1);
}
// Clear incorrect browser versions.
else if (version == data[1] ||
version == (data[2] = (/\bSafari\/([\d.]+\+?)/i.exec(ua) || 0)[1])) {
version = null;
}
// Use the full Chrome version when available.
data[1] = (/\bChrome\/([\d.]+)/i.exec(ua) || 0)[1];
// Detect Blink layout engine.
if (data[0] == 537.36 && data[2] == 537.36 && parseFloat(data[1]) >= 28 && layout == 'WebKit') {
layout = ['Blink'];
}
// Detect JavaScriptCore.
// http://stackoverflow.com/questions/6768474/how-can-i-detect-which-javascript-engine-v8-or-jsc-is-used-at-runtime-in-androi
if (!useFeatures || (!likeChrome && !data[1])) {
layout && (layout[1] = 'like Safari');
data = (data = data[0], data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : data < 534 ? '4+' : data < 535 ? 5 : data < 537 ? 6 : data < 538 ? 7 : data < 601 ? 8 : '8');
} else {
layout && (layout[1] = 'like Chrome');
data = data[1] || (data = data[0], data < 530 ? 1 : data < 532 ? 2 : data < 532.05 ? 3 : data < 533 ? 4 : data < 534.03 ? 5 : data < 534.07 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : data < 534.24 ? 10 : data < 534.30 ? 11 : data < 535.01 ? 12 : data < 535.02 ? '13+' : data < 535.07 ? 15 : data < 535.11 ? 16 : data < 535.19 ? 17 : data < 536.05 ? 18 : data < 536.10 ? 19 : data < 537.01 ? 20 : data < 537.11 ? '21+' : data < 537.13 ? 23 : data < 537.18 ? 24 : data < 537.24 ? 25 : data < 537.36 ? 26 : layout != 'Blink' ? '27' : '28');
}
// Add the postfix of ".x" or "+" for approximate versions.
layout && (layout[1] += ' ' + (data += typeof data == 'number' ? '.x' : /[.+]/.test(data) ? '' : '+'));
// Obscure version for some Safari 1-2 releases.
if (name == 'Safari' && (!version || parseInt(version) > 45)) {
version = data;
}
}
// Detect Opera desktop modes.
if (name == 'Opera' && (data = /\bzbov|zvav$/.exec(os))) {
name += ' ';
description.unshift('desktop mode');
if (data == 'zvav') {
name += 'Mini';
version = null;
} else {
name += 'Mobile';
}
os = os.replace(RegExp(' *' + data + '$'), '');
}
// Detect Chrome desktop mode.
else if (name == 'Safari' && /\bChrome\b/.exec(layout && layout[1])) {
description.unshift('desktop mode');
name = 'Chrome Mobile';
version = null;
if (/\bOS X\b/.test(os)) {
manufacturer = 'Apple';
os = 'iOS 4.3+';
} else {
os = null;
}
}
// Strip incorrect OS versions.
if (version && version.indexOf((data = /[\d.]+$/.exec(os))) == 0 &&
ua.indexOf('/' + data + '-') > -1) {
os = trim(os.replace(data, ''));
}
// Add layout engine.
if (layout && !/\b(?:Avant|Nook)\b/.test(name) && (
/Browser|Lunascape|Maxthon/.test(name) ||
name != 'Safari' && /^iOS/.test(os) && /\bSafari\b/.test(layout[1]) ||
/^(?:Adobe|Arora|Breach|Midori|Opera|Phantom|Rekonq|Rock|Samsung Internet|Sleipnir|Web)/.test(name) && layout[1])) {
// Don't add layout details to description if they are falsey.
(data = layout[layout.length - 1]) && description.push(data);
}
// Combine contextual information.
if (description.length) {
description = ['(' + description.join('; ') + ')'];
}
// Append manufacturer to description.
if (manufacturer && product && product.indexOf(manufacturer) < 0) {
description.push('on ' + manufacturer);
}
// Append product to description.
if (product) {
description.push((/^on /.test(description[description.length - 1]) ? '' : 'on ') + product);
}
// Parse the OS into an object.
if (os) {
data = / ([\d.+]+)$/.exec(os);
isSpecialCasedOS = data && os.charAt(os.length - data[0].length - 1) == '/';
os = {
'architecture': 32,
'family': (data && !isSpecialCasedOS) ? os.replace(data[0], '') : os,
'version': data ? data[1] : null,
'toString': function() {
var version = this.version;
return this.family + ((version && !isSpecialCasedOS) ? ' ' + version : '') + (this.architecture == 64 ? ' 64-bit' : '');
}
};
}
// Add browser/OS architecture.
if ((data = /\b(?:AMD|IA|Win|WOW|x86_|x)64\b/i.exec(arch)) && !/\bi686\b/i.test(arch)) {
if (os) {
os.architecture = 64;
os.family = os.family.replace(RegExp(' *' + data), '');
}
if (
name && (/\bWOW64\b/i.test(ua) ||
(useFeatures && /\w(?:86|32)$/.test(nav.cpuClass || nav.platform) && !/\bWin64; x64\b/i.test(ua)))
) {
description.unshift('32-bit');
}
}
// Chrome 39 and above on OS X is always 64-bit.
else if (
os && /^OS X/.test(os.family) &&
name == 'Chrome' && parseFloat(version) >= 39
) {
os.architecture = 64;
}
ua || (ua = null);
/*------------------------------------------------------------------------*/
/**
* The platform object.
*
* @name platform
* @type Object
*/
var platform = {};
/**
* The platform description.
*
* @memberOf platform
* @type string|null
*/
platform.description = ua;
/**
* The name of the browser's layout engine.
*
* The list of common layout engines include:
* "Blink", "EdgeHTML", "Gecko", "Trident" and "WebKit"
*
* @memberOf platform
* @type string|null
*/
platform.engine = layout && layout[0];
/**
* The name of the product's manufacturer.
*
* The list of manufacturers include:
* "Apple", "Archos", "Amazon", "Asus", "Barnes & Noble", "BlackBerry",
* "Google", "HP", "HTC", "LG", "Microsoft", "Motorola", "Nintendo",
* "Nokia", "Samsung" and "Sony"
*
* @memberOf platform
* @type string|null
*/
platform.manufacturer = manufacturer;
/**
* The name of the browser/environment.
*
* The list of common browser names include:
* "Chrome", "Electron", "Firefox", "Firefox for iOS", "IE",
* "Microsoft Edge", "PhantomJS", "Safari", "SeaMonkey", "Silk",
* "Opera Mini" and "Opera"
*
* Mobile versions of some browsers have "Mobile" appended to their name:
* eg. "Chrome Mobile", "Firefox Mobile", "IE Mobile" and "Opera Mobile"
*
* @memberOf platform
* @type string|null
*/
platform.name = name;
/**
* The alpha/beta release indicator.
*
* @memberOf platform
* @type string|null
*/
platform.prerelease = prerelease;
/**
* The name of the product hosting the browser.
*
* The list of common products include:
*
* "BlackBerry", "Galaxy S4", "Lumia", "iPad", "iPod", "iPhone", "Kindle",
* "Kindle Fire", "Nexus", "Nook", "PlayBook", "TouchPad" and "Transformer"
*
* @memberOf platform
* @type string|null
*/
platform.product = product;
/**
* The browser's user agent string.
*
* @memberOf platform
* @type string|null
*/
platform.ua = ua;
/**
* The browser/environment version.
*
* @memberOf platform
* @type string|null
*/
platform.version = name && version;
/**
* The name of the operating system.
*
* @memberOf platform
* @type Object
*/
platform.os = os || {
/**
* The CPU architecture the OS is built for.
*
* @memberOf platform.os
* @type number|null
*/
'architecture': null,
/**
* The family of the OS.
*
* Common values include:
* "Windows", "Windows Server 2008 R2 / 7", "Windows Server 2008 / Vista",
* "Windows XP", "OS X", "Ubuntu", "Debian", "Fedora", "Red Hat", "SuSE",
* "Android", "iOS" and "Windows Phone"
*
* @memberOf platform.os
* @type string|null
*/
'family': null,
/**
* The version of the OS.
*
* @memberOf platform.os
* @type string|null
*/
'version': null,
/**
* Returns the OS string.
*
* @memberOf platform.os
* @returns {string} The OS string.
*/
'toString': function() { return 'null'; }
};
platform.parse = parse;
platform.toString = toStringPlatform;
if (platform.version) {
description.unshift(version);
}
if (platform.name) {
description.unshift(name);
}
if (os && name && !(os == String(os).split(' ')[0] && (os == name.split(' ')[0] || product))) {
description.push(product ? '(' + os + ')' : 'on ' + os);
}
if (description.length) {
platform.description = description.join(' ');
}
// Are pop-up windows allowed for this site? (i. e. has the user a pop-up blocker?)
function popupsAllowed() {
var allowed = false;
if (!window.open) return;
var w = window.open("about:blank","","directories=no,height=1,width=1,menubar=no,resizable=no,scrollbars=no,status=no,titlebar=no,left=0,top=0,location=no");
if (w) {
allowed = true;
w.close();
}
return allowed;
}
function isTouch() {
try{ document.createEvent("TouchEvent"); return true; }
catch(e){ return ("ontouchstart" in window)?true:false; }
}
function isMobile() {
try {
if (typeof navigator == 'undefined') return false;
if (typeof sessionStorage != 'undefined' && sessionStorage.desktop) // desktop storage
return false;
else if (typeof localStorage != 'undefined' && localStorage.mobile) // mobile storage
return true;
var mobile = ['iphone','ipad','android','blackberry','nokia','opera mini','windows mobile','windows phone','iemobile'];
for (var i in mobile) if (navigator.userAgent.toLowerCase().indexOf(mobile[i].toLowerCase()) > 0) return true;
} catch (e) {}
return false;
}
global.jsVersion=1.0;
// Helper function to detect Javascript version
function _detectJsVersion() {
if (!document.write) return;
document.write('<script language="JavaScript1.0">');
document.write('jsVersion=1.0;');
document.write('<\/script>');
document.write('<script language="JavaScript1.1">');
document.write('jsVersion=1.1;');
document.write('<\/script>');
document.write('<script language="JavaScript1.2">');
document.write('jsVersion=1.2;');
document.write('<\/script>');
document.write('<script language="JavaScript1.3">');
document.write('jsVersion=1.3;');
document.write('<\/script>');
document.write('<script language="JavaScript1.4">');
document.write('jsVersion=1.4;');
document.write('<\/script>');
document.write('<script language="JavaScript1.5">');
document.write('jsVersion=1.5;');
document.write('<\/script>');
document.write('<script language="JavaScript1.6">');
document.write('jsVersion=1.6;');
document.write('<\/script>');
document.write('<script language="JavaScript1.7">');
document.write('jsVersion=1.7;');
document.write('<\/script>');
document.write('<script language="JavaScript1.8">');
document.write('jsVersion=1.8;');
document.write('<\/script>');
document.write('<script language="JavaScript2.0">');
document.write('jsVersion=2.0;');
document.write('<\/script>');
}
// What is the newest version of Javascript does the browser report as supported?
function detectJsVersion() {
_detectJsVersion();
}
detectJsVersion();
platform.jsVersion=function () { return jsVersion };
// platform.popups=popupsAllowed();
if (typeof navigator != 'undefined')
platform.hasWebRTC = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia)?1:undefined;
platform.touch = isTouch();
platform.mobile = isMobile();
platform.geoloc = (typeof navigator != 'undefined' &&
navigator.geolocation &&
typeof navigator.geolocation.getCurrentPosition == 'function')?true:false;
return platform;
}
/*--------------------------------------------------------------------------*/
// Export platform.
var platform = parse();
} else {
var platform = {}
}
module.exports=platform;
};
BundleModuleCode['jam/analyzer']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 bLAB
** $CREATED: 4-5-16 by sbosse.
** $RCS: $Id: analyzer.js,v 1.5 2020/02/03 09:45:01 sbosse Exp sbosse $
** $VERSION: 1.7.1
**
** $INFO:
**
** JAM AgentJS Analyzer. A branch tree monster!
** Uses esprima parser AST structure.
**
** TODO: Many checks are missing or are incomplete!
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var Aios = none;
var current = none;
var util = Require('util');
var options = {
version:'1.7.1',
}
var out = function (msg) { Io.out('[AJS] '+msg)};
/** All functions with 'this' property pass the agent 'this' object to their bodies or callback functions!
* The 'this' property array contains the argument index indicating functions or arrays of functions inheriting the
* agent 'this' object.
*/
var corefuncs = {
act: {obj:{add:{argn:2},delete:{argn:1},update:{argn:2}}}, // TODO obj handling!
add: {argn:2},
angle: {argn:[1,2]},
alt : {argn:[2,3], obj:{try:{argn:[2,3,4]}}},
collect: {argn:2},
concat: {argn:2},
connectTo: {argn:1},
copyto: {argn:2},
delta: {argn:2},
distance: {argn:[1,2]},
empty: {argn:1},
equal: {argn:2},
Export: {argn:2},
exists: {argn:1},
filter: {argn:2, this:[1]},
head: {argn:1},
iter: {argn:2, this:[1]},
Import: {argn:1},
inp: {argn:[2,3], this:[1], obj:{try:{argn:[2,3,4]}}},
kill: {argn:[0,1]},
last: {argn:1},
length: {argn:1},
log: {argn:[1,2,3,4,5,6,7,8,9,10]},
mark: {argn:2},
map: {argn:2, this:[1]},
matrix: {argn:[2,3]},
max: {argn:[1,2]},
me: {argn:0},
min: {argn:[1,2]},
myClass: {argn:0},
myNode: {argn:0},
myParent: {argn:0},
myPosition: {argn:0},
Number: {argn:1},
object: {argn:1},
out: {argn:1},
opposite: {argn:1},
privilege: {argn:0},
random: {argn:[1,2,3]},
reduce: {argn:2, this:[1]},
reverse: {argn:1,this:[1]},
rd: {argn:[2,3], this:[1], obj:{try:{argn:[2,3,4]}}},
rm: {argn:[1,2]},
send: {argn:[2,3]},
sendto: {argn:[2,3]},
sort: {argn:2, this:[1]},
sleep: {argn:[0,1]},
store: {argn:2},
string: {argn:1},
sum: {argn:[1,2]},
tail: {argn:1},
time: {argn:0},
timer: {obj:{add:{argn:[2,3]},delete:{argn:1},update:{argn:2}}}, // TODO obj handling!
try_alt: {argn:[2,3,4], this:[2]},
try_inp: {argn:[2,3,4], this:[2]},
try_rd: {argn:[2,3,4], this:[2]},
ts: {argn:2},
zero: {argn:1},
B: {argn:[1,2], this:[0,1]},
I: {argn:[3,4], this:[1,2,3]},
L: {argn:[4,5], this:[1,2,3,4]},
Vector: {argn:[1,2,3]}
};
function check_args (arguments,corefun) {
var len=arguments?arguments.length:0,passed=false;
if (Comp.obj.isArray(corefun.argn)) Comp.array.iter(corefun.argn,function (n) {
if (n==len) passed=true;
});
else passed=(len==corefun.argn);
return passed;
}
var jamc = function (options) {
// Dummy constructor
};
var properties = {
indexOf:'string.indexOf',
push:'array.push',
shift:'array.shift'
}
var literals={
none:'Literal',
undefined:'Literal'
}
literals['_']='Literal';
var syntax = {
find: function (root,typ,name) {
if (root.type==typ && root.id && root.id.type == 'Identifier' && root.id.name==name) return root;
switch (root.type) {
case 'Program':
return Comp.array.findmap(root.body,function (el) { return syntax.find(el,typ,name)});
break;
case 'VariableDeclaration':
return Comp.array.findmap(root.declarations,function (el) { return syntax.find(el,typ,name)});
break;
}
return null;
},
location: function (elem,short) {
var str='';
if (elem.loc) {
if (elem.loc.start) str='line '+(elem.loc.start.line+syntax.offset)+', position '+elem.loc.start.column;
if (elem.loc.end) str +=' to line '+(elem.loc.end.line+syntax.offset)+', position '+elem.loc.end.column;
return str;
} else return "unknown location";
},
name: function (elem) {
switch (elem.type) {
case 'ThisExpression': return 'this';
case 'Identifier': return elem.name;
case 'MemberExpression':
return syntax.name(elem.object)+'.'+syntax.name(elem.property);
default: return elem.toString();
}
},
offset:0
}
jamc.prototype.syntax = syntax;
/** The Big Machine: Analyze and check an agent class constructor function.
*
* Checks performed:
* - references (top-level this, local this, local variables, no free variable access)
* - AIOS function calls, privilege level sensitive
* - basic structure (act, trans, on? , next)
*
*/
/*
* options and evaluation order:
*
* {left:true,this:true, syms: *[]}
* {right:true, this:true, target:elem, syms: *[]}
* {funbody:true, this:true, syms: *[]}
* {funbody:true, syms: *[]}
* {this:true}
* {reference:true, syms: *[]}
* {funcall:true, external:true, arguments:elem [], syms: *[]}
* {trans:true}
*
*
*
* type Esprima.syntax=object|*;
* type jamc.prototype.analyze = function(syntax:Esprima.syntax,options:{classname:string,level:number}) ->
* {
* activities:object,
* transitions:object,
* subclasses:object,
* symbols:object,
* errors: string []
* }
*/
jamc.prototype.analyze = function (syntax,_options) {
var self=this,
classname=_options.classname,
level=_options.level,
ep,elem,cp,declarator,ac,val,
// Pre-defined top-level this.XX symbols
syms={id:{type:'Literal'},ac:{type:'Literal'}},
nextVal,
transObj,
actObj,
subclassObj,
transitions={},
activities={},
subclasses,
aios,
options={},
errors=[],
verbose=_options.verbose||0,
err=function (msg) { errors.push(msg); (_options.err||this.err||Io.err)(msg)},
out=_options.out||this.out||Io.out,
warn=_options.warn||this.warn||Io.warn;
switch (level) {
case 0: aios=Aios.aios0; break;
case 1: aios=Aios.aios1; break;
case 2: aios=Aios.aios2; break;
case 3: aios=Aios.aios3; break;
}
function unwrap(elem) {
switch (elem.type) {
case 'BlockStatement':
if (elem.body.length==1) return elem.body[0];
else return elem;
break;
default:
return elem;
}
}
function isThisExpr(elem) {
switch (elem.type) {
case 'MemberExpression':
return isThisExpr(elem.object);
case 'ThisExpression':
return true;
}
return false;
}
function isEmpty(o) {
if (!o) return true;
for (var p in o) { if (o[p]!=undefined) return false;};
return true;
}
/**************************
** Iterate Member Expression
**************************/
function iterMemberExpression (elem,options) {
var part,corefun,obj;
switch (elem.type) {
case 'Identifier':
if (!aios[elem.name] && !corefuncs[elem.name])
err('['+classname+'] Call of undefined function: '+
elem.name+', in level '+level+' ,at '+self.syntax.location(elem));
else if (corefuncs[elem.name]) {
corefun=corefuncs[elem.name];
if (!check_args(options.arguments,corefun)) {
err('['+classname+']: Call of AIOS function '+elem.name+' with invalid number of arguments, '+
'(expecting '+(corefun.argn.toString())+' argument(s), got '+options.arguments.length+
'), in level '+level+
' ,at '+self.syntax.location(elem));
}
return aios[elem.name];
} else return aios[elem.name];
break;
case 'MemberExpression':
switch (elem.object.type) {
case 'ThisExpression':
if (!syms[elem.property.name] || syms[elem.property.name].context!='ThisExpression')
err("['+classname+'] Undefined 'this' reference: "+
elem.property.name+', at '+self.syntax.location(elem));
if(syms[elem.property.name].type=='ObjectExpression') {
var Osyms={};
Comp.array.iter(syms[elem.property.name].properties,function (p) {
Osyms[p.key.name]=p.type;
});
if (!isEmpty(Osyms))
return Osyms;
else
return none;
} else return none;
break;
case 'Identifier':
if (!aios[elem.object.name] &&
!corefuncs[elem.object.name] &&
!options.syms[elem.object.name]) {
// console.log(elem);
err('['+classname+'] Access of undefined object variable: '+
elem.object.name+', in level '+level+' ,at '+self.syntax.location(elem));
}
if (properties[elem.property.name]) return undefined;
if (elem.computed) return undefined; // TODO, check property!
if (corefuncs[elem.object.name]) {
obj=corefuncs[elem.object.name].obj||corefuncs[elem.object.name];
if (!obj[elem.property.name]) {
// console.log(corefuncs[elem.object.name])
err('['+classname+'] Access of unknown AIOS(corefuncs) object attribute: '+
elem.object.name+'.'+elem.property.name+', in level '+level+' ,at '+self.syntax.location(elem));
}
return obj[elem.property.name];
} else if (aios[elem.object.name]) {
// console.log(elem);
obj=aios[elem.object.name].obj||aios[elem.object.name];
if (!obj[elem.property.name])
err('['+classname+'] Access of unknown AIOS object attribute: '+
elem.object.name+'.'+elem.property.name+', in level '+level+' ,at '+self.syntax.location(elem));
return obj[elem.property.name];
}
else if (options.syms[elem.object.name]) {
// console.log(elem);
// User defined object, can't be resolved further
return none;
}
return;
break;
case 'MemberExpression':
part=iterMemberExpression(elem.object,options);
if (part && part.obj) part=part.obj;
if (!elem.computed && part && !part[elem.property.name] && !properties[elem.property.name]) {
err('['+classname+'] Access of unknown object attribute: '+
self.syntax.name(elem)+' ('+elem.property.name+'), in level '+
level+' ,at '+self.syntax.location(elem));
}
if (elem.computed) check(elem.property,{reference:true,syms:options.syms});
if (part && (typeof part[elem.property.name] == 'object') && !isEmpty(part[elem.property.name]))
return part[elem.property.name];
else
return none;
break;
}
break;
}
return;
}
/**********************************
** Check for a declaration and add it to the symbol table
**********************************/
function addDeclaration(elem,options) {
var ep,el;
switch (elem.type) {
case 'VariableDeclaration':
for (ep in elem.declarations) {
el=elem.declarations[ep];
if (!options.shadow[el.id.name]) options.shadow[el.id.name]=options.syms[el.id.name];
if (el.type=='VariableDeclarator') {
if (el.id.type=='Identifier') {
options.syms[el.id.name]=el;
}
}
}
break;
case 'FunctionDeclaration':
if (!options.shadow[elem.id.name]) options.shadow[elem.id.name]=options.syms[elem.id.name];
options.syms[elem.id.name]=elem;
break;
case 'ForStatement':
addDeclaration(elem.init,options);
break;
}
}
/*********************************
** Main checker function
*********************************/
function check(elem,options) {
var ep,el,name,thismaybe,shadow,locshadow;
/*
console.log(elem);
console.log(options);
*/
/*
** Top-level statements
*/
if (options.left && options.this) {
// LHS check of agent class top-level statements
switch (elem.type) {
case 'Identifier':
err('['+classname+'] Assignment may not contain free variables: var '+
elem.name+', at '+self.syntax.location(elem));
break;
case 'MemberExpression':
if (elem.object.type != 'ThisExpression')
err('['+classname+'] Assignment may not contain non-this MemberExpression on left side: '+
self.syntax.name(elem.object)+', at '+self.syntax.location(elem));
switch (elem.property.type) {
case 'Identifier':
if (syms[elem.property.name])
err('['+classname+'] Found duplicate property definition: '+
elem.property.name+' ('+syms[elem.property.name].type+'), at '+self.syntax.location(elem));
else {
syms[elem.property.name]=options.target;
syms[elem.property.name].context=elem.object.type;
}
switch (elem.property.name) {
case 'act': actObj = options.target; break;
case 'trans': transObj = options.target; break;
case 'subclass': subclassObj = options.target; break;
}
break;
}
break;
}
}
else if (options.right && options.this) {
// RHS check of agent class top-level statements
switch (elem.type) {
case 'Literal':
case 'Identifier':
switch (options.target.property.name) {
case 'next':
val = elem.value||elem.name;
if (!Comp.obj.isString(val))
err('['+classname+'] Invalid next property, expected string, got '+
val+', at '+self.syntax.location(elem));
nextVal = val;
break;
}
break;
case 'ObjectExpression':
switch (options.target.property.name) {
case 'trans':
for (ep in elem.properties) {
el=elem.properties[ep];
//console.log(el)
if (el.type=='Property') {
transitions[el.key.name]=el.value;
}
}
break;
case 'act':
for (ep in elem.properties) {
el=elem.properties[ep];
// console.log(el)
if (el.type=='Property') {
if (aios[el.key.name])
err('['+classname+'] Activity name '+el.key.name+
' shadows AIOS function or object, at '+self.syntax.location(elem));
activities[el.key.name]=el.value;
}
}
break;
case 'subclass':
subclasses={};
for (ep in elem.properties) {
el=elem.properties[ep];
// console.log(el)
if (el.type=='Property') {
subclasses[el.key.name]=el.value;
}
}
break;
}
break;
case 'FunctionExpression':
// Check and add function parameters
locshadow={};
for (ep in elem.params) {
param=elem.params[ep];
if (param.type!='Identifier')
err('['+classname+'] Invalid function parameter type'+param.type+', expected Identifier'+
', at '+self.syntax.location(elem));
locshadow[param.name]=options.syms[param.name];
options.syms[param.name]=param.type;
}
check(elem.body,{funbody:true,this:true,syms:options.syms});
// Restore symbol table
for (ep in locshadow) {
options.syms[ep]=locshadow[ep];
}
break;
}
}
/*
** Function body statements that can access the agent object by 'this'
*/
else if (options.funbody && options.this) {
// Activity or transition top- or second level function bodies - 'this' references always the agent object!
elem=unwrap(elem);
switch (elem.type) {
case 'BlockStatement':
// Local symbols
if (options.shadow) shadow=options.shadow;
options.shadow={};
// First get all function and variable definitions in current scope
if (!options.syms) options.syms={};
Comp.array.iter(elem.body,function (el) {
addDeclaration(el,options);
});
// Now check the body statements
Comp.array.iter(elem.body,function (el) {check(el,options)});
if (options.syms) for (ep in options.shadow) {
options.syms[ep]=options.shadow[ep];
}
options.shadow=shadow;
break;
case 'ExpressionStatement':
switch (elem.expression.type) {
case 'AssignmentExpression':
switch (elem.expression.left.type) {
case 'MemberExpression':
if (isThisExpr(elem.expression.left.object))
check(elem.expression.left,{this:true});
break;
case 'Identifier':
check(elem.expression.left,{reference:true,syms:options.syms});
break;
}
check(elem.expression.right,{reference:true,syms:options.syms});
break;
case 'CallExpression':
thismaybe=[]; // for 'this' propagation to arguments
if (elem.expression.callee.object && isThisExpr(elem.expression.callee.object)) {
check(elem.expression.callee,{this:true,funcall:true,arguments:elem.expression.arguments});
} else {
if (corefuncs[elem.expression.callee.name] && corefuncs[elem.expression.callee.name].this)
{
thismaybe=corefuncs[elem.expression.callee.name].this;
}
if (options.syms[elem.expression.callee.name]) {
if (options.syms[elem.expression.callee.name].type != 'FunctionDeclaration')
err('['+classname+'] Not a function:'+elem.expression.callee.name+
', at '+self.syntax.location(elem));
// TODO
} else
/* AIOS function call */
check(elem.expression.callee,{funcall:true,external:true,syms:options.syms,
arguments:elem.expression.arguments});
}
// Check arguments
Comp.array.iter(elem.expression.arguments,function (el,i) {
var ep,param,shadow;
if (!Comp.array.member(thismaybe,i)) {
check(el,{reference:true,syms:options.syms});
} else {
// It's a AIOS function call with a function argument.
// Check function body with 'this' referencing the agent object.
switch (el.type) {
case 'ArrayExpression':
// Block of functions ...
Comp.array.iter(el.elements,function (el_block,block_i) {
if (el_block.type != 'FunctionExpression')
err('['+classname+'] Invalid argument '+(i+1)+' of AIOS core function '+
elem.expression.callee.name+': Expeceted FunctionExpression array, but got '+
el_block.type+ ' element (array index '+(block_i+1)+')'+
', at '+self.syntax.location(elem));
check(el_block.body,{funbody:true,this:true,syms:options.syms});
});
break;
case 'FunctionExpression':
// Check and add function parameters
shadow={};
for (ep in el.params) {
param=el.params[ep];
if (param.type!='Identifier')
err('['+classname+'] Invalid function parameter type'+param.type+', expected Identifier'+
', at '+self.syntax.location(elem));
if (options.syms[param.name]) shadow[param.name]=options.syms[param.name];
options.syms[param.name]=param.type;
}
check(el.body,{funbody:true,this:true,syms:options.syms});
// Restore symbol table
for (ep in shadow) {
options.syms[ep]=shadow[ep];
}
break;
case 'ArrowFunctionExpression':
// Check and add function parameters
shadow={};
for (ep in el.params) {
param=el.params[ep];
if (param.type!='Identifier')
err('['+classname+'] Invalid function parameter type'+param.type+', expected Identifier'+
', at '+self.syntax.location(elem));
if (options.syms[param.name]) shadow[param.name]=options.syms[param.name];
options.syms[param.name]=param.type;
}
check(el.body,{funbody:true,this:true,syms:options.syms});
// Restore symbol table
for (ep in shadow) {
options.syms[ep]=shadow[ep];
}
break;
case 'CallExpression':
// TODO, check arguments ..
break;
case 'Identifier':
// Nothing to do?
break;
default:
err('['+classname+'] Invalid argument '+(i+1)+' of AIOS core function '+
elem.expression.callee.name+': Expeceted FunctionExpression, ArrowFunctionExpression, ArrayExpression, or Identifier, but got '+
el.type+
', at '+self.syntax.location(elem));
}
}
});
break;
case 'UpdateExpression':
check(elem.expression.argument,{reference:true,syms:options.syms});
break;
}
break;
case 'VariableDeclaration':
// console.log(elem.declarations);
if (!options.shadow) options.shadow={};
for (ep in elem.declarations) {
el=elem.declarations[ep];
if (!options.shadow[el.id.name]) options.shadow[el.id.name]=options.syms[el.id.name];
if (el.type=='VariableDeclarator') {
if (el.id.type=='Identifier') {
options.syms[el.id.name]=el;
}
}
}
break;
case 'IfStatement':
check(elem.consequent,options);
if (elem.alternate) check(elem.alternate,options);
check(elem.test,{reference:true,syms:options.syms});
break;
case 'ForStatement':
//console.log(elem)
check(elem.body,options);
check(elem.init,{reference:true,syms:options.syms});
check(elem.test,{reference:true,syms:options.syms});
check(elem.update,{reference:true,syms:options.syms});
break;
case 'WhileStatement':
//console.log(elem)
check(elem.body,options);
check(elem.test,{reference:true,syms:options.syms});
break;
case 'ReturnStatement':
if (elem.argument)
check(elem.argument,{reference:true,syms:options.syms});
break;
case 'FunctionDeclaration':
if (!options.shadow[elem.id.name]) options.shadow[elem.id.name]=options.syms[elem.id.name];
options.syms[elem.id.name]=elem;
/* agent object not accessible in function body! */
// Check and add function parameters
locshadow={};
for (ep in elem.params) {
param=elem.params[ep];
if (param.type!='Identifier')
err('['+classname+'] Invalid function parameter type'+param.type+', expected Identifier'+
', at '+self.syntax.location(elem));
locshadow[param.name]=options.syms[param.name];
options.syms[param.name]=param.type;
}
check(elem.body,{funbody:true,syms:options.syms});
// Restore symbol table
for (ep in locshadow) {
options.syms[ep]=locshadow[ep];
}
break;
}
}
/*
** Funcion body that cannot access the agent object (local functions)
*/
else if (options.funbody) {
// TODO
elem=unwrap(elem);
switch (elem.type) {
case 'BlockStatement':
// Local symbols
if (options.shadow) shadow=options.shadow;
options.shadow={};
// First get all function and variable definitions in current scope
if (!options.syms) options.syms={};
Comp.array.iter(elem.body,function (el) {
addDeclaration(el,options);
});
Comp.array.iter(elem.body,function (el) {check(el,options)});
if (options.syms) for (ep in options.shadow) {
options.syms[ep]=options.shadow[ep];
}
options.shadow=shadow;
break;
case 'ExpressionStatement':
switch (elem.expression.type) {
case 'AssignmentExpression':
switch (elem.expression.left.type) {
case 'MemberExpression':
if (elem.expression.left.object && isThisExpr(elem.expression.left.object))
check(elem.expression.left,{syms:options.syms});
break;
case 'Identifier':
check(elem.expression.left,{reference:true,syms:options.syms});
break;
}
check(elem.expression.right,{reference:true,syms:options.syms});
break;
case 'CallExpression':
thismaybe=[]; // for 'this' propagation to arguments
if (elem.expression.callee.object && isThisExpr(elem.expression.callee.object)) {
check(elem.expression.callee,{this:true,funcall:true,arguments:elem.expression.arguments});
} else {
if (corefuncs[elem.expression.callee.name] && corefuncs[elem.expression.callee.name].this)
{
thismaybe=corefuncs[elem.expression.callee.name].this;
}
if (options.syms[elem.expression.callee.name]) {
if (options.syms[elem.expression.callee.name].type != 'FunctionDeclaration')
err('['+classname+'] Not a function:'+elem.expression.callee.name+
', at '+self.syntax.location(elem));
// TODO
} else
/* AIOS function call */
check(elem.expression.callee,{funcall:true,external:true,syms:options.syms,
arguments:elem.expression.arguments});
}
// Check arguments
Comp.array.iter(elem.expression.arguments,function (el,i) {
var ep,param,shadow;
if (!Comp.array.member(thismaybe,i)) {
check(el,{reference:true,syms:options.syms});
} else {
// It's a AIOS function call with a function argument.
// Check function body with 'this' referencing the agent object.
switch (el.type) {
case 'ArrayExpression':
// Block of functions ...
Comp.array.iter(el.elements,function (el_block,block_i) {
if (el_block.type != 'FunctionExpression')
err('['+classname+'] Invalid argument '+(i+1)+' of AIOS core function '+
elem.expression.callee.name+': Expeceted FunctionExpression array, but got '+
el_block.type+ ' element (array index '+(block_i+1)+')'+
', at '+self.syntax.location(elem));
check(el_block.body,{funbody:true,this:true,syms:options.syms});
});
break;
case 'FunctionExpression':
// Check and add function parameters
shadow={};
for (ep in el.params) {
param=el.params[ep];
if (param.type!='Identifier')
err('['+classname+'] Invalid function parameter type'+param.type+', expected Identifier'+
', at '+self.syntax.location(elem));
if (options.syms[param.name]) shadow[param.name]=options.syms[param.name];
options.syms[param.name]=param.type;
}
check(el.body,{funbody:true,this:true,syms:options.syms});
// Restore symbol table
for (ep in shadow) {
options.syms[ep]=shadow[ep];
}
break;
case 'ArrowFunctionExpression':
// Check and add function parameters
shadow={};
for (ep in el.params) {
param=el.params[ep];
if (param.type!='Identifier')
err('['+classname+'] Invalid function parameter type'+param.type+', expected Identifier'+
', at '+self.syntax.location(elem));
if (options.syms[param.name]) shadow[param.name]=options.syms[param.name];
options.syms[param.name]=param.type;
}
check(el.body,{funbody:true,this:true,syms:options.syms});
// Restore symbol table
for (ep in shadow) {
options.syms[ep]=shadow[ep];
}
break;
case 'CallExpression':
// TODO, check arguments ..
break;
case 'Identifier':
// Nothing to do?
break;
default:
err('['+classname+'] Invalid argument '+(i+1)+' of AIOS core function '+
elem.expression.callee.name+': Expeceted FunctionExpression, ArrowFunctionExpression, ArrayExpression, or Identifier, but got '+
el.type+
', at '+self.syntax.location(elem));
}
}
});
break;
case 'UpdateExpression':
check(elem.expression.argument,{reference:true,syms:options.syms});
break;
}
break;
case 'VariableDeclaration':
for (ep in elem.declarations) {
el=elem.declarations[ep];
if (!options.shadow[el.id.name]) options.shadow[el.id.name]=options.syms[el.id.name];
if (el.type=='VariableDeclarator') {
if (el.id.type=='Identifier') {
options.syms[el.id.name]=el;
}
}
}
break;
case 'IfStatement':
check(elem.consequent,options);
if (elem.alternate) check(elem.alternate,options);
check(elem.test,{reference:true,syms:options.syms});
break;
case 'ForStatement':
//console.log(elem)
check(elem.body,options);
check(elem.init,{reference:true,syms:options.syms});
check(elem.test,{reference:true,syms:options.syms});
check(elem.update,{reference:true,syms:options.syms});
break;
case 'WhileStatement':
//console.log(elem)
check(elem.body,options);
check(elem.test,{reference:true,syms:options.syms});
break;
case 'ReturnStatement':
if (elem.argument)
check(elem.argument,{reference:true,syms:options.syms});
break;
case 'FunctionDeclaration':
if (!options.shadow[elem.id.name]) options.shadow[elem.id.name]=options.syms[elem.id.name];
options.syms[elem.id.name]=elem;
/* agent object not accessible in function body! */
// Check and add function parameters
locshadow={};
for (ep in elem.params) {
param=elem.params[ep];
if (param.type!='Identifier')
err('['+classname+'] Invalid function parameter type '+param.type+', expected Identifier'+
', at '+self.syntax.location(elem));
locshadow[param.name]=options.syms[param.name];
options.syms[param.name]=param.type;
}
check(elem.body,{funbody:true,syms:options.syms});
// Restore symbol table
for (ep in locshadow) {
options.syms[ep]=locshadow[ep];
}
break;
}
}
/*
** Check agent object 'this' reference
*/
else if (options.this) {
// Check symbol reference for ThisExpression only
switch (elem.object.type) {
case 'MemberExpression':
check(elem.object,{this:true});
break;
case 'ThisExpression':
if (!syms[elem.property.name])
err('['+classname+"] Undefined 'this' reference: "+
elem.property.name+', at '+self.syntax.location(elem));
if(options.funcall && syms[elem.property.name].type != 'FunctionExpression')
err('['+classname+"] Not a function: this."+
elem.property.name+', at '+self.syntax.location(elem));
}
}
/*
** Check generic references
*/
else if (options.reference) {
// Check symbol reference for local symbols only
switch (elem.type) {
case 'Identifier':
if (!options.syms[elem.name] && !literals[elem.name] && !aios[elem.name] && !activities[elem.name])
err('['+classname+'] Undefined variable reference: '+
elem.name+', at '+self.syntax.location(elem));
break;
case 'BinaryExpression':
check(elem.left,options);
check(elem.right,options);
break;
case 'AssignmentExpression':
switch (elem.left.type) {
case 'MemberExpression':
if (elem.left.object && isThisExpr(elem.left.object))
check(elem.left,{this:true});
break;
case 'Identifier':
check(elem.left,{reference:true,syms:options.syms});
break;
}
check(elem.right,options);
break;
case 'UpdateExpression':
check(elem.argument,options);
break;
case 'MemberExpression':
switch (elem.object.type) {
case 'ThisExpression':
check(elem,{this:true,syms:options.syms});
break;
case 'Identifier':
check(elem.object,{reference:true,syms:options.syms});
if (elem.computed) switch (elem.property.type) {
case 'Identifier':
check(elem.property,{reference:true,syms:options.syms});
break;
}
break;
case 'MemberExpression':
iterMemberExpression(elem,options);
//if (isThisExpr(elem.object))
// check(elem.object,{this:true,syms:options.syms});
}
break;
case 'ArrayExpression':
Comp.array.iter(elem.elements, function (el2,i) {
if (el2) check(el2,{reference:true,syms:options.syms});
});
break;
case 'CallExpression':
if (elem.callee.object && isThisExpr(elem.callee.object)) {
check(elem.callee,{this:true,funobj:true,arguments:elem.arguments});
} else {
if (options.syms[elem.callee.name]) {
if (options.syms[elem.callee.name].type != 'FunctionDeclaration')
err('['+classname+'] Not a function:'+elem.callee.name+
', at '+self.syntax.location(elem));
/* Internal function call, nothing to do */
} else
check(elem.callee,{funcall:true,external:true,syms:options.syms,
arguments:elem.arguments});
}
Comp.array.iter(elem.arguments,function (el) {
check(el,{reference:true,syms:options.syms,arguments:elem.arguments})
});
break;
}
}
/*
** AIOS function calls and objects
*/
else if (options.funcall && options.external) {
// Check external AIOS function references
switch (elem.type) {
case 'Identifier':
case 'MemberExpression':
iterMemberExpression(elem,options);
break;
}
}
/*
** Check transition function body statements
*/
else if (options.trans) {
switch (elem.type) {
case 'BlockStatement':
Comp.array.iter(elem.body,function (el) {check(el,options)});
break;
case 'IfStatement':
check(elem.consequent,options);
if (elem.alternate) check(elem.alternate,options);
break;
case 'ReturnStatement':
options.ret++;
if (elem.argument)
check(elem.argument,options);
else
if (verbose) warn('['+classname+'] Returns undefined in transition '+
options.trans+', at '+self.syntax.location(elem)+'.');
break;
case 'Literal':
if (!activities[elem.value])
err('['+classname+'] Returns unknown activity reference '+
elem.value+' in transition '+options.trans+', at '+self.syntax.location(elem)+'.');
break;
case 'Identifier':
if (!activities[elem.name])
err('['+classname+'] Returns unknown activity reference '+
elem.name+' in transition '+options.trans+', at '+self.syntax.location(elem)+'.');
break;
}
}
} /* End of check */
/************************
** Analyzer
************************/
if (verbose) out('Analyzing agent class "'+classname+'" ..');
if (syntax.type!='Program')
err('Syntax is not a program: '+syntax.type);
// Phase 1
loop1: for (ep in syntax.body) {
var elem=syntax.body[ep];
if (elem.type!='VariableDeclaration')
err('Body element is not a variable declaration: '+elem.type);
for(cp in elem.declarations) {
var declarator=elem.declarations[cp];
if (declarator.type!='VariableDeclarator') {
err('VariableDeclaration element is not a variable declarator: '+declarator.type);
}
if (declarator.id.name!='ac')
err('['+classname+'] Entry not found, expected ac, got: '+declarator.id.name);
else { ac=declarator; break loop1;};
}
}
if (!ac)
err('No agent class template found.');
if (!ac.init || ac.init.type != 'FunctionExpression')
err('['+classname+'] Entry is invalid, expected function, got: '+ac.init.type);
if (ac.init.type != 'FunctionExpression')
err('['+classname+'] Entry is invalid, expected function, got: '+ac.init.type);
if (ac.init.body.type != 'BlockStatement')
err('['+classname+'] Entry is invalid, expected function body, got: '+ac.init.body.type);
// Phase 2 Agent Class Pre-check / Top-level / Top symbol table creation
loop2: for (ep in ac.init.body.body) {
var elem=ac.init.body.body[ep];
switch (elem.type) {
case 'VariableDeclaration':
err('['+classname+'] May not contain free variable declarations: '+
Comp.printf.list(Comp.array.map(elem.declarations,function (decl) {
if (decl.type!='VariableDeclarator') return '?';
else return 'var '+self.syntax.name(decl.id)
}))+', at '+self.syntax.location(elem));
break;
case 'ExpressionStatement':
switch (elem.expression.type) {
case 'AssignmentExpression':
check(elem.expression.left,{left:true,this:true,target:elem.expression.right});
check(elem.expression.right,{right:true,this:true,target:elem.expression.left,syms:syms});
break;
case 'MemberExpression':
if (elem.expression.object && elem.expression.object.type=='ThisExpression')
check(elem.expression,{left:true,this:true,target:{type:'undefined'}});
break;
}
break;
default:
err('['+classname+'] Invalid top-level '+elem.type+
', at '+self.syntax.location(elem));
break;
}
}
if (!syms['act'] || syms['act'].type != 'ObjectExpression')
err('['+classname+'] Found no or no valid activity section, expecting this.act={..}.');
if (!syms['trans'] || syms['trans'].type != 'ObjectExpression')
err('['+classname+'] Found no or no valid transition section, expecting this.trans={..}.');
if (syms['on'] && syms['on'].type != 'ObjectExpression')
err('['+classname+'] Found invalid handler section, expecting this.on={..}.');
if (!syms['on'] && verbose)
warn('['+classname+'] Found no handler section, expecting this.on={..}.');
if (!nextVal)
err('['+classname+'] Found no next attribute, expecting this.next="<nextact>".');
if (!activities[nextVal])
err('['+classname+'] Found invalid next attribute pointing to undefined activity '+nextVal+'.');
// Phase 3 Function, Activity, and Transition properties check
loop3A: for (ep in activities) {
var elem=activities[ep];
if (!transitions[ep] && verbose) warn('['+classname+'] No transition entry found for activity '+ep);
switch (elem.type) {
case 'FunctionExpression':
options={funbody:true,this:true,syms:{}};
check(elem.body,options);
elem.syms=syms;
break;
case 'ArrowFunctionExpression':
options={funbody:true,this:true,syms:{}};
check(elem.body,options);
elem.syms=syms;
break;
default:
err('['+classname+'] Found invalid activity entry, expecting FunctionExpression or ArrowFunctionExpression, got '+
elem.type+', at '+self.syntax.location(elem));
}
}
loop3B: for (ep in transitions) {
var elem=transitions[ep],opt;
if (!activities[ep])
err('['+classname+'] Transition entry found referencing unknown activity: '+
ep+', at '+self.syntax.location(elem));
switch (elem.type) {
case 'Identifier':
if (!activities[elem.name])
err('['+classname+'] Unknown transition found: '+
elem.name+', at '+self.syntax.location(elem));
break;
case 'Literal':
if (!activities[elem.value])
err('['+classname+'] Unknown transition found: '+
elem.value+', at '+self.syntax.location(elem));
break;
case 'FunctionExpression':
opt={trans:ep,ret:0};
check(elem.body,opt);
if (opt.ret==0 && verbose)
warn('['+classname+'] Missing return (undefined) in transition '+
opt.trans+', at '+self.syntax.location(elem)+'.');
break;
case 'ArrowFunctionExpression':
opt={trans:ep,ret:0};
check(elem.body,opt);
if (opt.ret==0 && verbose)
warn('['+classname+'] Missing return (undefined) in transition '+
opt.trans+', at '+self.syntax.location(elem)+'.');
break;
default:
err('['+classname+'] Found invalid transition entry, expecting FunctionExpression or ArrowFunctionExpression, Identifier, or String Literal, got '+
elem.type+', at '+self.syntax.location(elem));
}
}
if (verbose) out(classname+' passed check.');
if (verbose) {
out(classname+' has the following top-level object properties:');
for (ep in syms) {
var sym=syms[ep];
if (!sym) continue;
out(' '+ep+' : '+sym.type);
}
out(classname+' has the following activities:');
for (ep in activities) {
var elem=activities[ep];
out(' '+ep);
}
out(classname+' next activity: '+nextVal);
out(classname+' has the following transition entries:');
for (ep in transitions) {
var elem=transitions[ep];
out(' '+ep);
}
if (subclasses) {
out(classname+' has the following subclass entries:');
for (ep in subclasses) {
var elem=subclasses[ep];
out(' '+ep);
}
}
}
if (verbose>1) {
out(classname+' has the following top-level symbols:');
for (ep in syms) {
if (!syms[ep]) continue;
out(' '+ep+':'+(verbose>2?Io.inspect(syms[ep]):syms[ep].type));
}
}
return {
activities:activities,
transitions:transitions,
subclasses:subclasses,
symbols:syms,
errors: errors
}
}
module.exports = {
corefuncs:corefuncs,
/* Extend corefuncs */
extend: function (funcs) {
var p;
for(p in funcs) {
corefuncs[p]=funcs[p];
}
},
jamc:jamc,
options:options,
current:function (module) { current=module.current; Aios=module; }
};
};
BundleModuleCode['shell/cluster']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2020 bLAB
** $CREATED: 1-9-19 by sbosse.
** $VERSION: 1.3.3
**
** $INFO:
**
** Automatic JAM Shell Cluster Manager
**
** var workers = new Cluster(options);
** workers.start()
**
** typeof @options = {
** cluster?:clusterdesc [],
** verbose?:number,
** connect?:boolean,
** rows?:number,
** cols?:number,
** port0?:number is private IP port base for grid links,
** port1?:number is public IP port base,
** portn?:number is public IP delta,
** proto?:string [] is public IP protocoll list,
** poll?: function (gridprocess),
** secure?:string is AMP security port,
** todo?: string is default command for worker,
** }
**
** typeof clusterdesc = {
** id:string|number,
** todo;string,
** ports?:number|{port:number,proto:string}|dir [],
** connect?:{from:dir,to:dir} [],
** poll?:function|number
** }
** type gridprocess = {
** }
**
** TODO:
** - Grid reconnect (pro.cluster.connect) after worker restart
** - Check internal links (satisfy above)
**
** $ENDOFINFO
**
*/
var Comp = Require('com/compat');
var CP = Require('child_process')
var osutils = Require('os/osutils');
var program = process.argv[1];
var Aios = none;
var PS = {
START : 'PS.START', // starting
STOP : 'PS.STOP', // stopping
RUN : 'PS.RUN', // running
RESTART : 'PS.RESTART', // restarting
DEAD : 'PS.DEAD', // killed - non existing anymore
EXIT : 'PS.EXIT', // on exit
DEAF : 'PS.DEAF', // not responding
}
/** Create the CLUSTER (Grid of physical nodes)
*/
function Cluster(options,shell) {
var i,j,n,m,self=this,poll;
function pos2index(col,row) {
return row*self.grid.cols+col;
}
function pos2ip(col,row,dir) {
var off=0;
switch (dir) {
case DIR.NORTH: off=0; break;
case DIR.WEST: off=1; break;
case DIR.EAST: off=2; break;
case DIR.SOUTH: off=3; break;
}
return self.options.port0+row*self.grid.cols*4+col*4+off;
}
function addr(port) {
return 'localhost:'+port;
}
function ports(col,row) {
// Create public ports if any
var port;
if (options.port1 && options.proto) {
port=options.port1+pos2index(col,row)*(options.portn||options.proto.length)-1;
return options.proto.map(function (proto) {
port++;
return DIR.IP(proto+'://'+addr(port)+
(options.secure?'?secure='+options.secure:''));
})
} else return [];
}
this.cluster = options.cluster;
this.options = options;
this.logging = options.logging; // messages from workers passed to outside!
if ((options.poll==true||typeof options.poll=='number') &&
options.port1 && options.proto &&
options.proto.contains('http')) {
// HTTP worker polling function using amp.scan
// -- requires http / no https with localhost DNS permitted!
this.log('Creating AMP HTTP scanner on port '+(options.port0||(options.port1-1)));
this.probe = shell.port(DIR.IP(options.port0||(options.port1-1)),{
proto:'http',
});
poll = function poll(pro) {
var port=(options.port1+options.proto.indexOf('http'))+pro.index*(options.portn||options.proto.length)
self.probe.amp.scan(
{address:'localhost',port:port,proto:'http'},
null,
function (reply) {
pro.poll={
time : shell.time(),
state : reply!=undefined,
info : reply&&reply.info,
port : (reply&&reply.port?shell.Port.toString(reply.port):null)
};
})
}
this.polltimer=setInterval(self.poll.bind(self),options.poll);
} else if (typeof options.poll=='function') poll=options.poll;
if (!this.cluster && this.options.rows && this.options.cols) {
this.cluster=[];
this.grid = {
rows: this.options.rows,
cols: this.options.cols
}
// Create cluster descriptor array
for(j=0;j<this.grid.rows;j++)
for(i=0;i<this.grid.cols;i++)
this.cluster.push({
id:'node('+i+','+j+')',
poll:poll,
todo:this.options.todo||'',
ports:ports(i,j)
})
} else if (this.cluster) {
n=m=int(Math.log2(this.cluster.length));
while (n*m<this.cluster.length) n++;
this.grid = {
rows: n,
cols: m
}
} else return;
if (this.options.verbose) this.log('Grid: '+this.grid.rows+','+this.grid.cols);
this.cluster.forEach(function (node,i) {
var row=int(i/self.grid.cols),
col=i-row*self.grid.cols,
ports,connects,ip;
node.position=[col,row];
if (!self.options.connect || !self.options.port0) return;
// Create private P2P ports and connect them
ip=pos2ip(col,row);
if (!node.ports) node.ports=[];
if (!node.connect) node.connect=[];
// TODO: compact; iterative composition
if (row==0) {
if (col == 0) ports = [
DIR.East (ip+2),
DIR.South (ip+3)
]; else if (col<(self.grid.cols-1)) ports = [
DIR.West (ip+1),
DIR.East (ip+2),
DIR.South (ip+3)
]; else ports = [
DIR.West (ip+1),
DIR.South (ip+3)
]
} else if (row<(self.grid.rows-1)) {
if (col == 0) ports = [
DIR.North (ip),
DIR.East (ip+2),
DIR.South (ip+3)
]; else if (col<(self.grid.cols-1)) ports = [
DIR.North (ip),
DIR.West (ip+1),
DIR.East (ip+2),
DIR.South (ip+3)
]; else ports = [
DIR.North (ip),
DIR.West (ip+1),
DIR.South (ip+3)
]
} else {
if (col == 0) ports = [
DIR.North (ip),
DIR.East (ip+2),
]; else if (col<(self.grid.cols-1)) ports = [
DIR.North (ip),
DIR.West (ip+1),
DIR.East (ip+2),
]; else ports = [
DIR.North (ip),
DIR.West (ip+1),
]
}
self.log('Creating cluster interconnect AMP UDP ports for node '+i+': '+
ports.map(function (p) { return DIR.print(p) }).join(' '));
node.ports=node.ports.concat(ports);
if (row==0) {
if (col==0) {
connects = [
DIR.East (addr(pos2ip(col+1,row,DIR.WEST))),
DIR.South (addr(pos2ip(col,row+1,DIR.NORTH)))
]
} else if (col<(self.grid.cols-1)) connects = [
DIR.East (addr(pos2ip(col+1,row,DIR.WEST))),
DIR.South (addr(pos2ip(col,row+1,DIR.NORTH)))
]; else connects = [
DIR.South (addr(pos2ip(col,row+1,DIR.NORTH)))
]
} else if (row<(self.grid.rows-1)) {
if (col==0) {
connects = [
DIR.East (addr(pos2ip(col+1,row,DIR.WEST))),
DIR.South (addr(pos2ip(col,row+1,DIR.NORTH)))
]
} else if (col<(self.grid.cols-1)) connects = [
DIR.East (addr(pos2ip(col+1,row,DIR.WEST))),
DIR.South (addr(pos2ip(col,row+1,DIR.NORTH)))
]; else connects = [
DIR.South (addr(pos2ip(col,row+1,DIR.NORTH)))
]
} else {
if (col==0) {
connects = [
DIR.East (addr(pos2ip(col+1,row,DIR.WEST))),
]
} else if (col<(self.grid.cols-1)) ports = [
DIR.East(localhost+':'+pos2ip(col+1,row))
]; else connects = [
]
}
node.connect=node.connect.concat(connects);
});
}
Cluster.prototype.log = function (msg,pid) {
console.log('[CLU'+(pid||process.pid)+' '+Aios.clock()+'] '+msg);
}
Cluster.prototype.newVM = function(index,todo) {
var argv = [],desc=this.cluster[index],self=this;
if (this.options.verbose) argv.push('-v');
if (this.options.verbose>1) argv.push('-v');
if (todo && todo[todo.length-1]!=';') todo += ';';
if (desc.ports) todo += desc.ports.map(function (port,index) {
if (port.port != undefined || port.proto != undefined || port.address != undefined)
return ('var p'+index+'='+
'port(DIR.IP('+port.port+'),{proto:"'+port.proto+'"'+
(port.secure?',secure:"'+port.secure+'"':'')+
'})');
if (port.tag)
return ('var p'+index+'=port('+Aios.DIR.toString(port)+')');
}).join(';');
if (desc.connect) todo = todo +';'+desc.connect.map(function (port,index) {
return 'var t'+index+'=setTimeout(function () { connect('+Aios.DIR.toString(port)+')},500)';
}).join(';');
todo = [
'config({print:process.send.bind(process),printAgent:process.send.bind(process),printAsync:process.send.bind(process)})',
''
].join(';') + todo;
argv=argv.concat(['-e',todo]);
if (this.options.verbose>1) this.log(argv);
var pro = CP.fork(program,argv);
pro.on('exit',function (code,signal) {
if (self.options.verbose) self.log('Process #'+pro.pid+' got signal '+code);
pro.exited={code:code,signal:signal};
});
pro.on('message', function(msg) {
if (self.logging) self.logging(msg,index);
else self.log(msg,pro.pid);
});
if (this.options.verbose) this.log('Started worker #'+pro.pid+': '+todo);
else this.log('Started worker #'+pro.pid);
pro.argv=argv;
pro.todo=todo;
pro.state=PS.START;
pro.index=index;
pro.cluster=desc;
pro.load=0;
pro.mem=0;
return pro;
}
// Check all worker processes
Cluster.prototype.check = function (index) {
var self=this,stats=[];
this.childs.forEach(function (pro,_index) {
if (index != undefined && _index != index) return;
if (pro.state != PS.DEAD)
pro.state=pro.exited?PS.STOP:PS.RUN;
stats.push({pid:pro.pid,state:pro.state,poll:pro.poll,load:pro.load,mem:pro.mem})
osutils.getProcess(pro.pid,function (res) {
if (res) {
pro.load=Math.floor(res.cpu)/100;
pro.mem=Math.floor(res.mem);
}
});
})
return stats;
}
// Start polling - poll function must be provided by cluster descriptor
Cluster.prototype.poll = function (index) {
var self=this;
this.childs.forEach(function (pro,_index) {
if (index != undefined && _index != index) return;
if (pro.cluster.poll) pro.cluster.poll(pro);
})
}
// Restart all or one worker processs
Cluster.prototype.restart = function (index) {
var self=this;
this.childs=this.childs.map(function (pro,_index) {
if (index != undefined && _index != index) return pro;
if (self.options.verbose) self.log('Restarting worker #'+index);
if (pro.state == PS.RUN) pro.kill();
return self.newVM(_index,self.cluster[_index].todo);
})
}
Cluster.prototype.report = function (index) {
var desc=this.cluster;
function pn(proto) {
switch (proto) {
case 'http': return 'h';
case 'https': return 'hs';
case 'udp': return 'u';
case 'tcp': return 't';
}
}
function ports(index) {
if (desc[index].ports) {
var ports = desc[index].ports.filter(function (p) {
return p.tag==DIR.tag.IP || p.port;
});
return ports.map(function(port) {
if (port.proto) return pn(port.proto)+port.port+(port.secure?'S':'P');
if (port.ip) return port.ip.replace(/localhost|ttp|dp|cp|:|\//g,'')
.replace(/\?[^$]+$/,'')+
(port.ip.match(/\?secure=/)?'S':'P')
}).join(',');
} else return '-';
}
var table='';
// this.poll();
var stats=this.check();
table = '| Node | Ports | PID | Status | CPU% | Memory MB | Agents | Links |\n'+
'|:---|:---|:---|:---|:---|:---|:---|:---|\n';
table = table+stats.map(function (row,index) {
if (!row || row.state!=Cluster.PS.RUN || !row.poll ||
!row.poll.state || !row.poll.info)
return '| - | '+ports(index)+' | - | '+row.state+' | - | - | - | - |';
else
return '| '+row.poll.info.world+' | '+ports(index)+' | '+
row.pid+' | '+row.state + ' | '+row.load + ' | '+row.mem + ' | '+
row.poll.info.stats.agents+' | '+
row.poll.info.stats.links+' |';
}).join('\n')
return table;
}
// Start all or one worker processs
Cluster.prototype.start = function (index) {
this.childs = [];
for (var pi=0;pi<this.cluster.length;pi++) {
if (index != undefined && index!=pi) continue;
this.childs.push(this.newVM(pi,this.cluster[pi].todo));
}
}
// Stop all or one worker processs
Cluster.prototype.stop = function (index) {
var self=this;
this.childs.forEach(function (pro,_index) {
if (index != undefined && _index != index) return;
if (pro.state == PS.DEAD) return;
self.log('Stopping worker #'+_index);
pro.state=PS.DEAD;
pro.kill();
})
}
Cluster.PS=PS;
Cluster.current = function (module) { Aios=module };
module.exports = Cluster;
};
BundleModuleCode['os/osutils']=function (module,exports){
// https://github.com/oscmejia/os-utils
var _os = require('os');
var _proc = require('child_process')
var _fs = require('fs')
exports.platform = function(){
return process.platform;
}
exports.cpuCount = function(){
return _os.cpus().length;
}
exports.sysUptime = function(){
//seconds
return _os.uptime();
}
exports.processUptime = function(){
//seconds
return process.uptime();
}
// Memory
exports.freemem = function(){
return _os.freemem() / ( 1024 * 1024 );
}
exports.totalmem = function(){
return _os.totalmem() / ( 1024 * 1024 );
}
exports.freememPercentage = function(){
return _os.freemem() / _os.totalmem();
}
exports.freeCommand = function(callback){
// Only Linux
require('child_process').exec('free -m', function(error, stdout, stderr) {
var lines = stdout.split("\n");
var str_mem_info = lines[1].replace( /[\s\n\r]+/g,' ');
var mem_info = str_mem_info.split(' ')
total_mem = parseFloat(mem_info[1])
free_mem = parseFloat(mem_info[3])
buffers_mem = parseFloat(mem_info[5])
cached_mem = parseFloat(mem_info[6])
used_mem = total_mem - (free_mem + buffers_mem + cached_mem)
callback(used_mem -2);
});
}
// Hard Disk Drive
exports.harddrive = function(callback){
require('child_process').exec('df -k', function(error, stdout, stderr) {
var total = 0;
var used = 0;
var free = 0;
var lines = stdout.split("\n");
var str_disk_info = lines[1].replace( /[\s\n\r]+/g,' ');
var disk_info = str_disk_info.split(' ');
total = Math.ceil((disk_info[1] * 1024)/ Math.pow(1024,2));
used = Math.ceil(disk_info[2] * 1024 / Math.pow(1024,2)) ;
free = Math.ceil(disk_info[3] * 1024 / Math.pow(1024,2)) ;
callback(total, free, used);
});
}
// Return process running current
exports.getProcesses = function(nProcess, callback){
// if nprocess is undefined then is function
if(typeof nProcess === 'function'){
callback =nProcess;
nProcess = 0
}
if (nProcess > 0)
command = 'ps -eo pid,pcpu,pmem,time,args | sort -k 2 -r | head -n'+(nProcess + 1)
else
command = 'ps -eo pid,pcpu,pmem,time,args | sort -k 2 -r | head -n'+10
_proc.exec(command, function(error, stdout, stderr) {
var that = this
var lines = stdout.split("\n");
lines.shift()
lines.pop()
var result=[];
lines.forEach(function(_item,_i){
var _str = _item.replace( /[\s\n\r]+/g,' ');
_str = _str.split(' ')
result.push( {cpu:Number(_str[2]),
mem:Number(_str[3]),
time:_str[4],
pro:_str[5],
args:_str.slice(6,_str.length)});
});
callback(result);
});
}
// One process
exports.getProcess = function(pid, callback){
if(typeof pid === 'function'){
callback = pid;
pid = process.pid
}
command = 'ps -eo pid,pcpu,rss,time,args'
_proc.exec(command, function(error, stdout, stderr) {
var that = this
var lines = stdout.split("\n");
lines.shift()
lines.pop()
var result;
lines.forEach(function(_item,_i){
var _str = _item.trim().replace( /[\s\n\r]+/g,' ');
_str = _str.split(' ')
if (Number(_str[0])==pid)
result = {cpu:Number(_str[1]),
mem:Math.floor(Number(_str[2])/1024),
time:_str[3],
pro:_str[4],
args:_str.slice(5,_str.length)};
});
callback(result);
});
}
/*
* Returns All the load average usage for 1, 5 or 15 minutes.
*/
exports.allLoadavg = function(){
var loads = _os.loadavg();
return loads[0].toFixed(4)+','+loads[1].toFixed(4)+','+loads[2].toFixed(4);
}
/*
* Returns the load average usage for 1, 5 or 15 minutes.
*/
exports.loadavg = function(_time){
if(_time === undefined || (_time !== 5 && _time !== 15) ) _time = 1;
var loads = _os.loadavg();
var v = 0;
if(_time == 1) v = loads[0];
if(_time == 5) v = loads[1];
if(_time == 15) v = loads[2];
return v;
}
exports.cpuFree = function(callback){
getCPUUsage(callback, true);
}
exports.cpuUsage = function(callback){
getCPUUsage(callback, false);
}
if (process.platform=='linux')
exports.load = function(pid,callback,time){
var t0,t1;
if (typeof pid == 'function') {
time=callback;
callback=pid;
pid=process.pid;
}
getUsage(pid,function (t) {
t0=t;
if (!time) callback(t);
})
if (time) setTimeout(function () {
getUsage(pid,function (t) {
t1=t;
callback((t1-t0)/time*1000);
})
},time);
}
function getCPUUsage(callback, free){
var stats1 = getCPUInfo();
var startIdle = stats1.idle;
var startTotal = stats1.total;
setTimeout(function() {
var stats2 = getCPUInfo();
var endIdle = stats2.idle;
var endTotal = stats2.total;
var idle = endIdle - startIdle;
var total = endTotal - startTotal;
var perc = idle / total;
if(free === true)
callback( perc );
else
callback( (1 - perc) );
}, 1000 );
}
function getCPUInfo(callback){
var cpus = _os.cpus();
var user = 0;
var nice = 0;
var sys = 0;
var idle = 0;
var irq = 0;
var total = 0;
for(var cpu in cpus){
if (!cpus.hasOwnProperty(cpu)) continue;
user += cpus[cpu].times.user;
nice += cpus[cpu].times.nice;
sys += cpus[cpu].times.sys;
irq += cpus[cpu].times.irq;
idle += cpus[cpu].times.idle;
}
var total = user + nice + sys + idle + irq;
return {
'idle': idle,
'total': total
};
}
function getUsage(pid,cb){
// Linux only
print(pid)
_fs.readFile("/proc/" + pid + "/stat", function(err, data){
var elems = data.toString().split(' ');
var utime = parseInt(elems[13]);
var stime = parseInt(elems[14]);
cb(utime + stime);
});
}
};
BundleModuleCode['rpc/rpc']=function (module,exports){
var udprpc = Require('rpc/udp_rpc');
var version = "1.1.3"
function rpc(proto,port,arrayOfNamedFunctions,options) {
options=options||{}
switch (proto.toUpperCase()) {
case 'UDP':
return new udprpc(options.dgramType||'udp4',port,
arrayOfNamedFunctions,options);
}
}
var clientCache=[];
// server
function getreq (url, requestCallback, options) {
var proto = url.match(/^([a-zA-Z]+):/),
port = url.match(/([0-9]+)$/);
proto=(proto&&proto[1])||'udp';
port=(port&&port[1]);
if (!port) throw Error('getreq: invalid port');
var myport = rpc(proto,port,[
function request(source, data, callback) {
callback(requestCallback(data,source))
}
],options)
return myport;
}
// client
function trans (url,data,replyCallback,options) {
var proto = url.match(/^([a-zA-Z]+):/),
destination = url.replace(/^([a-zA-Z]+):\/\//,''),
port = url.match(/([0-9]+)$/);
proto=(proto&&proto[1])||'udp';
port=(port&&port[1]);
if (!port) throw Error('getreq: invalid port');
if (clientCache[url]) {
if (clientCache[url].state!='listening') {
clientCache[url].queue.push([destination,data,replyCallback]);
} else
clientCache[url].request(destination,data,replyCallback||function(){});
clientCache[url].timestamp=Date.now();
} else {
var myport = clientCache[url] = rpc(proto,0,[
function request(source, data, callback) {}
],options);
clientCache[url].timestamp=Date.now();
clientCache[url].queue=[];
clientCache[url].queue.push([destination,data,replyCallback]);
myport.on('init',function () {
while (clientCache[url].queue.length) {
var next = clientCache[url].queue.shift();
myport.request(next[0],next[1],next[2]||function(){});
}
});
}
}
module.exports = {
clientCache : clientCache,
rpc : rpc,
getreq : getreq,
trans : trans,
version : version
}
};
BundleModuleCode['rpc/udp_rpc']=function (module,exports){
// https://github.com/dfellis/node-udp-rpc
'use strict';
var dgram = require('dgram');
var crypto = Require('os/crypto.rand'); // var crypto = require('crypto');
var util = require('util');
var events = require('events');
var async = Require('rpc/async');
var version = "1.1.3";
// The header flag for udp-rpc packet reconstruction
var flags = {
// Reserved: 1, 2, 4, 8, 16, 32, 64
lastPacket: 128
};
// Helper function to generate Xor bytes
function bufXor(buf) {
var out = 0;
for (var i = 0; i < buf.length; i++) {
out = out ^ buf.readUInt8(i, true);
}
return out;
}
// Helper function for generating a unique byte for the flag/id byte
function uniqId(usedList) {
var id = 0;
do {
id = (Math.random() * (Math.pow(2, 8) - 1)) & 127; // Get a random byte and trim the top bit off
} while (typeof usedList[id] != 'undefined')
return id;
}
// Called when the client wants to call a remote rpc method
function execRpc(method, address) {
if (!method || !address) {
throw new Error("Did not receive a valid RPC request!");
}
// Construct the payload to send to the remote server
var rpcParams = Array.prototype.slice.call(arguments, 2);
var callback = rpcParams.length > 1 ? rpcParams.pop() : undefined;
var addrArray = address.split(":");
var messageId = this.genId();
// var payload = new Buffer(([method].concat(messageId, rpcParams)).join(','));
var payload = new Buffer(JSON.stringify([method,
messageId,
].concat(rpcParams)));
this.emit('execRpc', method, address, rpcParams, callback);
var self = this;
// we have to register callback handler in advance; maybe reply arrives earlier than
// sendMessage callback is executed !?
this.messages[messageId] = {
callTs: new Date(),
callback: callback,
method: method,
ip: addrArray[0],
port: addrArray[1],
rpcParams: rpcParams // This is for debugging purposes only
};
sendMessage.call(this, address, payload, function allSent(err) {
self.emit('sentRpc', method, address, rpcParams, callback, err);
if (err instanceof Error) {
// delete callback handler
delete self.messages[messageId];
callback(err)
} else {
// nothing to do!?
}
});
}
function sendMessage(address, payload, callback) {
var addrArray = address.split(":");
// Construct the 6 byte header elements of the udp-rpc protocol for message verification/splitting
if (!this.packets[address]) {
this.packets[address] = {};
}
var currFlags = uniqId(this.packets[address]);
var totXor = bufXor(payload);
// Construct an array of payload fragments, each no larger than 494 bytes
var payloads = [];
if (payload.length > 494) {
var i = 0;
for (i = 0; i < payload.length; i += 494) {
var newPayload = new Buffer(494);
payload.copy(newPayload, 0, i, i + 493);
payloads.push(newPayload);
}
if (i < payload.length) {
var newPayload = new Buffer(payload.length - i);
payload.copy(newPayload, 0, i, payload.length - 1);
payloads.push(newPayload);
}
} else {
payloads.push(payload);
}
var self = this;
// For each payload fragment, generate the payload header, construct the output message, and send it
async.forEach(payloads, function sendPayload(payload, callback) {
var message = new Buffer(payload.length + 6);
var packetNumber = payloads.indexOf(payload);
currFlags &= 127;
message.writeUInt32BE(packetNumber, 1, true);
var curXor = bufXor(payload);
if (packetNumber == payloads.length - 1) {
currFlags |= flags.lastPacket;
message.writeUInt8(totXor, 5, true);
} else {
message.writeUInt8(curXor, 5, true);
}
message.writeUInt8(currFlags, 0, true);
payload.copy(message, 6);
self.stats.send++;
self.stats.sendData += message.length;
self.dgram.send(message, 0, message.length, addrArray[1], addrArray[0], function(err) {
if (err instanceof Error) {
return callback(err);
}
return callback();
});
// When all are sent, or an error occurred, execute the callback
}, callback);
}
// Called when a remote message is received. This could be an rpc request or response
function receiveRpc(message, info) {
this.emit('receivedPacket', message, info);
// Extract the header and body of the message
var header = {
flagid: message.readUInt8(0, true),
packetNumber: message.readUInt32BE(1, true),
xor: message.readUInt8(5, true)
};
var body = message.slice(6);
this.stats.recv++;
this.stats.recvData += message.length;
var source = info.address + ":" + info.port;
if (!this.packets[source]) {
this.packets[source] = {};
}
if (!this.packets[source][header.flagid]) {
this.packets[source][header.flagid] = [];
}
this.packets[source][header.flagid].push({
header: header,
body: body
});
attemptProcessing.call(this, source, header.flagid);
}
function attemptProcessing(source, id) {
// Get the packet array to work on
var packetArray = this.packets[source][id];
// Tag the set of first and last packets in the packet array
var integerSum = 0;
var totalMessageLen = 0;
var lastPacket = false;
for (var i = 0; i < packetArray.length; i++) {
integerSum += packetArray[i].header.packetNumber + 1;
totalMessageLen += packetArray[i].body.length;
if (!!(packetArray[i].header.flagid & flags.lastPacket)) {
lastPacket = true;
}
}
// If there is no last packet, processing impossible
// Also, if the sum of packet numbers isn't n(n+1)/2, there must be a missing packet
if (!lastPacket || integerSum != (packetArray.length * (packetArray.length + 1) / 2)) {
return;
}
// Sort the array of packets based on packet number, since we can process this data
packetArray = packetArray.sort(function(a, b) {
return a.header.packetNumber - b.header.packetNumber;
});
// Build the full message buffer from the sorted packets
var fullMessage = new Buffer(totalMessageLen);
for (var i = 0, j = 0; i < packetArray.length; j += packetArray[i].body.length, i++) {
packetArray[i].body.copy(fullMessage, 0, j);
}
// Remove the saved packets from the packets object and pass the message to the processMessage
delete this.packets[source][id];
processMessage.call(this, fullMessage, source);
}
function processMessage(message, source) {
var sourceArr = source.split(':');
// var messageArr = message.toString('utf8').split(',');
try {
var messageArr = JSON.parse(message.toString('utf8'));
} catch (e) { console.log(e) }
// console.log('processMessage',messageArr[0])
// Determine type of message: RPC response, request
if (!this.oneway && typeof this.messages[messageArr[0]] == 'object' &&
sourceArr[1] == this.messages[messageArr[0]].port) {
// If a response, the message array consists of the request id and response results
// Find the appropriate callback
this.emit('receivedRpcResponse', message, source);
this.messages[messageArr[0]].callback.apply(this, messageArr.slice(1));
delete this.messages[messageArr[0]];
} else if (typeof this.methods[messageArr[0]] == 'function') {
// If a request, the message array consists of the request method, id, and parameters, in that order
this.emit('receivedRpcRequest', message, source);
var messageId = messageArr[1];
var params = messageArr.slice(2);
// Parameters sent to the RPC method start with the source string, in case they need to know who is calling (simple state tracking)
params.unshift(source);
var self = this;
// Automatically insert a callback to the params passed to the RPC method to handle the results
if (!this.oneway) params.push(function rpcCallback() {
// var payload = new Buffer(([messageId].concat(Array.prototype.slice.call(arguments, 0))).join(','));
var payload = new Buffer(JSON.stringify([messageId].concat((Array.prototype.slice.call(arguments, 0)))));
sendMessage.call(self, source, payload, function(err) {
if (err instanceof Error) {
throw err;
} // RPC server non-functional, this is fatal
});
});
// Execute the requested RPC method
this.methods[messageArr[0]].apply(this, params);
} else {
// If packet is not understood, ignore it
console.log('receivedUnknownMessage',messageArr)
this.emit('receivedUnknownMessage', message, source);
}
}
// UDP-RPC object to provide a nice interface to the protocol and properly initialize/destroy it.
// I can't find a way to execute a function on ``delete`` like you can with get/set, so destroy this
// object with the ``die`` method.
function udpRpc(ipType, srvPort, methods, oneway) {
var options = {};
events.EventEmitter.call(this);
if (typeof oneway == 'object') {
options = oneway;
oneway = options.oneway;
}
this.methods = {};
this.messages = {};
this.packets = {};
this.state = 'starting';
this.stats = {
recv : 0, // #msgs
send : 0,
recvData : 0, // #Bytes
sendData : 0,
}
for (var i in methods) {
Object.defineProperty(this, methods[i].name, {
enumerable: true,
configurable: false,
writable: false,
value: execRpc.bind(this, methods[i].name)
});
Object.defineProperty(this.methods, methods[i].name, {
enumerable: true,
configurable: false,
writable: false,
value: methods[i]
});
}
this.genId = function() {
var id = "";
do {
try {
id = crypto.randomBytes(3).toString('base64');
} catch (e) {
id = new Buffer(Math.random() * (Math.pow(2, 24) - 1)).toString('base64');
}
} while (typeof this.messages[id] != 'undefined')
return id;
};
this.srvPort = srvPort;
this.address = undefined;
var self = this;
process.nextTick(function() {
self.dgram = dgram.createSocket(ipType);
self.dgram.on('listening', function udpRpcStart() {
self.address = self.dgram.address();
self.state = 'listening';
if (options.verbose) console.log('udpRpc: listening on', self.address);
self.emit('init');
});
self.dgram.on('message', receiveRpc.bind(self));
self.dgram.bind(srvPort); // Currently hardwired; will look into alternatives
});
this.die = function die() {
this.dgram.close();
this.emit('death');
delete this;
};
this.oneway = oneway || false;
return this;
}
util.inherits(udpRpc, events.EventEmitter);
exports = module.exports = udpRpc;
};
BundleModuleCode['rpc/async']=function (module,exports){
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.async = global.async || {})));
}(this, (function (exports) { 'use strict';
function slice(arrayLike, start) {
start = start|0;
var newLen = Math.max(arrayLike.length - start, 0);
var newArr = Array(newLen);
for(var idx = 0; idx < newLen; idx++) {
newArr[idx] = arrayLike[start + idx];
}
return newArr;
}
/**
* Creates a continuation function with some arguments already applied.
*
* Useful as a shorthand when combined with other control flow functions. Any
* arguments passed to the returned function are added to the arguments
* originally passed to apply.
*
* @name apply
* @static
* @memberOf module:Utils
* @method
* @category Util
* @param {Function} fn - The function you want to eventually apply all
* arguments to. Invokes with (arguments...).
* @param {...*} arguments... - Any number of arguments to automatically apply
* when the continuation is called.
* @returns {Function} the partially-applied function
* @example
*
* // using apply
* async.parallel([
* async.apply(fs.writeFile, 'testfile1', 'test1'),
* async.apply(fs.writeFile, 'testfile2', 'test2')
* ]);
*
*
* // the same process without using apply
* async.parallel([
* function(callback) {
* fs.writeFile('testfile1', 'test1', callback);
* },
* function(callback) {
* fs.writeFile('testfile2', 'test2', callback);
* }
* ]);
*
* // It's possible to pass any number of additional arguments when calling the
* // continuation:
*
* node> var fn = async.apply(sys.puts, 'one');
* node> fn('two', 'three');
* one
* two
* three
*/
var apply = function(fn/*, ...args*/) {
var args = slice(arguments, 1);
return function(/*callArgs*/) {
var callArgs = slice(arguments);
return fn.apply(null, args.concat(callArgs));
};
};
var initialParams = function (fn) {
return function (/*...args, callback*/) {
var args = slice(arguments);
var callback = args.pop();
fn.call(this, args, callback);
};
};
/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* _.isObject({});
* // => true
*
* _.isObject([1, 2, 3]);
* // => true
*
* _.isObject(_.noop);
* // => true
*
* _.isObject(null);
* // => false
*/
function isObject(value) {
var type = typeof value;
return value != null && (type == 'object' || type == 'function');
}
var hasSetImmediate = typeof setImmediate === 'function' && setImmediate;
var hasNextTick = typeof process === 'object' && typeof process.nextTick === 'function';
function fallback(fn) {
setTimeout(fn, 0);
}
function wrap(defer) {
return function (fn/*, ...args*/) {
var args = slice(arguments, 1);
defer(function () {
fn.apply(null, args);
});
};
}
var _defer;
if (hasSetImmediate) {
_defer = setImmediate;
} else if (hasNextTick) {
_defer = process.nextTick;
} else {
_defer = fallback;
}
var setImmediate$1 = wrap(_defer);
/**
* Take a sync function and make it async, passing its return value to a
* callback. This is useful for plugging sync functions into a waterfall,
* series, or other async functions. Any arguments passed to the generated
* function will be passed to the wrapped function (except for the final
* callback argument). Errors thrown will be passed to the callback.
*
* If the function passed to `asyncify` returns a Promise, that promises's
* resolved/rejected state will be used to call the callback, rather than simply
* the synchronous return value.
*
* This also means you can asyncify ES2017 `async` functions.
*
* @name asyncify
* @static
* @memberOf module:Utils
* @method
* @alias wrapSync
* @category Util
* @param {Function} func - The synchronous function, or Promise-returning
* function to convert to an {@link AsyncFunction}.
* @returns {AsyncFunction} An asynchronous wrapper of the `func`. To be
* invoked with `(args..., callback)`.
* @example
*
* // passing a regular synchronous function
* async.waterfall([
* async.apply(fs.readFile, filename, "utf8"),
* async.asyncify(JSON.parse),
* function (data, next) {
* // data is the result of parsing the text.
* // If there was a parsing error, it would have been caught.
* }
* ], callback);
*
* // passing a function returning a promise
* async.waterfall([
* async.apply(fs.readFile, filename, "utf8"),
* async.asyncify(function (contents) {
* return db.model.create(contents);
* }),
* function (model, next) {
* // `model` is the instantiated model object.
* // If there was an error, this function would be skipped.
* }
* ], callback);
*
* // es2017 example, though `asyncify` is not needed if your JS environment
* // supports async functions out of the box
* var q = async.queue(async.asyncify(async function(file) {
* var intermediateStep = await processFile(file);
* return await somePromise(intermediateStep)
* }));
*
* q.push(files);
*/
function asyncify(func) {
return initialParams(function (args, callback) {
var result;
try {
result = func.apply(this, args);
} catch (e) {
return callback(e);
}
// if result is Promise object
if (isObject(result) && typeof result.then === 'function') {
result.then(function(value) {
invokeCallback(callback, null, value);
}, function(err) {
invokeCallback(callback, err.message ? err : new Error(err));
});
} else {
callback(null, result);
}
});
}
function invokeCallback(callback, error, value) {
try {
callback(error, value);
} catch (e) {
setImmediate$1(rethrow, e);
}
}
function rethrow(error) {
throw error;
}
var supportsSymbol = typeof Symbol === 'function';
function isAsync(fn) {
return supportsSymbol && fn[Symbol.toStringTag] === 'AsyncFunction';
}
function wrapAsync(asyncFn) {
return isAsync(asyncFn) ? asyncify(asyncFn) : asyncFn;
}
function applyEach$1(eachfn) {
return function(fns/*, ...args*/) {
var args = slice(arguments, 1);
var go = initialParams(function(args, callback) {
var that = this;
return eachfn(fns, function (fn, cb) {
wrapAsync(fn).apply(that, args.concat(cb));
}, callback);
});
if (args.length) {
return go.apply(this, args);
}
else {
return go;
}
};
}
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();
/** Built-in value references. */
var Symbol$1 = root.Symbol;
/** Used for built-in method references. */
var objectProto = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
/**
* Used to resolve the
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
* of values.
*/
var nativeObjectToString = objectProto.toString;
/** Built-in value references. */
var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined;
/**
* A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the raw `toStringTag`.
*/
function getRawTag(value) {
var isOwn = hasOwnProperty.call(value, symToStringTag$1),
tag = value[symToStringTag$1];
try {
value[symToStringTag$1] = undefined;
var unmasked = true;
} catch (e) {}
var result = nativeObjectToString.call(value);
if (unmasked) {
if (isOwn) {
value[symToStringTag$1] = tag;
} else {
delete value[symToStringTag$1];
}
}
return result;
}
/** Used for built-in method references. */
var objectProto$1 = Object.prototype;
/**
* Used to resolve the
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
* of values.
*/
var nativeObjectToString$1 = objectProto$1.toString;
/**
* Converts `value` to a string using `Object.prototype.toString`.
*
* @private
* @param {*} value The value to convert.
* @returns {string} Returns the converted string.
*/
function objectToString(value) {
return nativeObjectToString$1.call(value);
}
/** `Object#toString` result references. */
var nullTag = '[object Null]';
var undefinedTag = '[object Undefined]';
/** Built-in value references. */
var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined;
/**
* The base implementation of `getTag` without fallbacks for buggy environments.
*
* @private
* @param {*} value The value to query.
* @returns {string} Returns the `toStringTag`.
*/
function baseGetTag(value) {
if (value == null) {
return value === undefined ? undefinedTag : nullTag;
}
return (symToStringTag && symToStringTag in Object(value))
? getRawTag(value)
: objectToString(value);
}
/** `Object#toString` result references. */
var asyncTag = '[object AsyncFunction]';
var funcTag = '[object Function]';
var genTag = '[object GeneratorFunction]';
var proxyTag = '[object Proxy]';
/**
* Checks if `value` is classified as a `Function` object.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a function, else `false`.
* @example
*
* _.isFunction(_);
* // => true
*
* _.isFunction(/abc/);
* // => false
*/
function isFunction(value) {
if (!isObject(value)) {
return false;
}
// The use of `Object#toString` avoids issues with the `typeof` operator
// in Safari 9 which returns 'object' for typed arrays and other constructors.
var tag = baseGetTag(value);
return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}
/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;
/**
* Checks if `value` is a valid array-like length.
*
* **Note:** This method is loosely based on
* [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
* @example
*
* _.isLength(3);
* // => true
*
* _.isLength(Number.MIN_VALUE);
* // => false
*
* _.isLength(Infinity);
* // => false
*
* _.isLength('3');
* // => false
*/
function isLength(value) {
return typeof value == 'number' &&
value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
}
/**
* Checks if `value` is array-like. A value is considered array-like if it's
* not a function and has a `value.length` that's an integer greater than or
* equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is array-like, else `false`.
* @example
*
* _.isArrayLike([1, 2, 3]);
* // => true
*
* _.isArrayLike(document.body.children);
* // => true
*
* _.isArrayLike('abc');
* // => true
*
* _.isArrayLike(_.noop);
* // => false
*/
function isArrayLike(value) {
return value != null && isLength(value.length) && !isFunction(value);
}
// A temporary value used to identify if the loop should be broken.
// See #1064, #1293
var breakLoop = {};
/**
* This method returns `undefined`.
*
* @static
* @memberOf _
* @since 2.3.0
* @category Util
* @example
*
* _.times(2, _.noop);
* // => [undefined, undefined]
*/
function noop() {
// No operation performed.
}
function once(fn) {
return function () {
if (fn === null) return;
var callFn = fn;
fn = null;
callFn.apply(this, arguments);
};
}
var iteratorSymbol = typeof Symbol === 'function' && Symbol.iterator;
var getIterator = function (coll) {
return iteratorSymbol && coll[iteratorSymbol] && coll[iteratorSymbol]();
};
/**
* The base implementation of `_.times` without support for iteratee shorthands
* or max array length checks.
*
* @private
* @param {number} n The number of times to invoke `iteratee`.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Array} Returns the array of results.
*/
function baseTimes(n, iteratee) {
var index = -1,
result = Array(n);
while (++index < n) {
result[index] = iteratee(index);
}
return result;
}
/**
* Checks if `value` is object-like. A value is object-like if it's not `null`
* and has a `typeof` result of "object".
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
* @example
*
* _.isObjectLike({});
* // => true
*
* _.isObjectLike([1, 2, 3]);
* // => true
*
* _.isObjectLike(_.noop);
* // => false
*
* _.isObjectLike(null);
* // => false
*/
function isObjectLike(value) {
return value != null && typeof value == 'object';
}
/** `Object#toString` result references. */
var argsTag = '[object Arguments]';
/**
* The base implementation of `_.isArguments`.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an `arguments` object,
*/
function baseIsArguments(value) {
return isObjectLike(value) && baseGetTag(value) == argsTag;
}
/** Used for built-in method references. */
var objectProto$3 = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty$2 = objectProto$3.hasOwnProperty;
/** Built-in value references. */
var propertyIsEnumerable = objectProto$3.propertyIsEnumerable;
/**
* Checks if `value` is likely an `arguments` object.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an `arguments` object,
* else `false`.
* @example
*
* _.isArguments(function() { return arguments; }());
* // => true
*
* _.isArguments([1, 2, 3]);
* // => false
*/
var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
return isObjectLike(value) && hasOwnProperty$2.call(value, 'callee') &&
!propertyIsEnumerable.call(value, 'callee');
};
/**
* Checks if `value` is classified as an `Array` object.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an array, else `false`.
* @example
*
* _.isArray([1, 2, 3]);
* // => true
*
* _.isArray(document.body.children);
* // => false
*
* _.isArray('abc');
* // => false
*
* _.isArray(_.noop);
* // => false
*/
var isArray = Array.isArray;
/**
* This method returns `false`.
*
* @static
* @memberOf _
* @since 4.13.0
* @category Util
* @returns {boolean} Returns `false`.
* @example
*
* _.times(2, _.stubFalse);
* // => [false, false]
*/
function stubFalse() {
return false;
}
/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;
/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined;
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;
/**
* Checks if `value` is a buffer.
*
* @static
* @memberOf _
* @since 4.3.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
* @example
*
* _.isBuffer(new Buffer(2));
* // => true
*
* _.isBuffer(new Uint8Array(2));
* // => false
*/
var isBuffer = nativeIsBuffer || stubFalse;
/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER$1 = 9007199254740991;
/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;
/**
* Checks if `value` is a valid array-like index.
*
* @private
* @param {*} value The value to check.
* @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
* @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
*/
function isIndex(value, length) {
var type = typeof value;
length = length == null ? MAX_SAFE_INTEGER$1 : length;
return !!length &&
(type == 'number' ||
(type != 'symbol' && reIsUint.test(value))) &&
(value > -1 && value % 1 == 0 && value < length);
}
/** `Object#toString` result references. */
var argsTag$1 = '[object Arguments]';
var arrayTag = '[object Array]';
var boolTag = '[object Boolean]';
var dateTag = '[object Date]';
var errorTag = '[object Error]';
var funcTag$1 = '[object Function]';
var mapTag = '[object Map]';
var numberTag = '[object Number]';
var objectTag = '[object Object]';
var regexpTag = '[object RegExp]';
var setTag = '[object Set]';
var stringTag = '[object String]';
var weakMapTag = '[object WeakMap]';
var arrayBufferTag = '[object ArrayBuffer]';
var dataViewTag = '[object DataView]';
var float32Tag = '[object Float32Array]';
var float64Tag = '[object Float64Array]';
var int8Tag = '[object Int8Array]';
var int16Tag = '[object Int16Array]';
var int32Tag = '[object Int32Array]';
var uint8Tag = '[object Uint8Array]';
var uint8ClampedTag = '[object Uint8ClampedArray]';
var uint16Tag = '[object Uint16Array]';
var uint32Tag = '[object Uint32Array]';
/** Used to identify `toStringTag` values of typed arrays. */
var typedArrayTags = {};
typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
typedArrayTags[uint32Tag] = true;
typedArrayTags[argsTag$1] = typedArrayTags[arrayTag] =
typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
typedArrayTags[errorTag] = typedArrayTags[funcTag$1] =
typedArrayTags[mapTag] = typedArrayTags[numberTag] =
typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
typedArrayTags[setTag] = typedArrayTags[stringTag] =
typedArrayTags[weakMapTag] = false;
/**
* The base implementation of `_.isTypedArray` without Node.js optimizations.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
*/
function baseIsTypedArray(value) {
return isObjectLike(value) &&
isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
}
/**
* The base implementation of `_.unary` without support for storing metadata.
*
* @private
* @param {Function} func The function to cap arguments for.
* @returns {Function} Returns the new capped function.
*/
function baseUnary(func) {
return function(value) {
return func(value);
};
}
/** Detect free variable `exports`. */
var freeExports$1 = typeof exports == 'object' && exports && !exports.nodeType && exports;
/** Detect free variable `module`. */
var freeModule$1 = freeExports$1 && typeof module == 'object' && module && !module.nodeType && module;
/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports$1 = freeModule$1 && freeModule$1.exports === freeExports$1;
/** Detect free variable `process` from Node.js. */
var freeProcess = moduleExports$1 && freeGlobal.process;
/** Used to access faster Node.js helpers. */
var nodeUtil = (function() {
try {
// Use `util.types` for Node.js 10+.
var types = freeModule$1 && freeModule$1.require && freeModule$1.require('util').types;
if (types) {
return types;
}
// Legacy `process.binding('util')` for Node.js < 10.
return freeProcess && freeProcess.binding && freeProcess.binding('util');
} catch (e) {}
}());
/* Node.js helper references. */
var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
/**
* Checks if `value` is classified as a typed array.
*
* @static
* @memberOf _
* @since 3.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
* @example
*
* _.isTypedArray(new Uint8Array);
* // => true
*
* _.isTypedArray([]);
* // => false
*/
var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
/** Used for built-in method references. */
var objectProto$2 = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty$1 = objectProto$2.hasOwnProperty;
/**
* Creates an array of the enumerable property names of the array-like `value`.
*
* @private
* @param {*} value The value to query.
* @param {boolean} inherited Specify returning inherited property names.
* @returns {Array} Returns the array of property names.
*/
function arrayLikeKeys(value, inherited) {
var isArr = isArray(value),
isArg = !isArr && isArguments(value),
isBuff = !isArr && !isArg && isBuffer(value),
isType = !isArr && !isArg && !isBuff && isTypedArray(value),
skipIndexes = isArr || isArg || isBuff || isType,
result = skipIndexes ? baseTimes(value.length, String) : [],
length = result.length;
for (var key in value) {
if ((inherited || hasOwnProperty$1.call(value, key)) &&
!(skipIndexes && (
// Safari 9 has enumerable `arguments.length` in strict mode.
key == 'length' ||
// Node.js 0.10 has enumerable non-index properties on buffers.
(isBuff && (key == 'offset' || key == 'parent')) ||
// PhantomJS 2 has enumerable non-index properties on typed arrays.
(isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
// Skip index properties.
isIndex(key, length)
))) {
result.push(key);
}
}
return result;
}
/** Used for built-in method references. */
var objectProto$5 = Object.prototype;
/**
* Checks if `value` is likely a prototype object.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
*/
function isPrototype(value) {
var Ctor = value && value.constructor,
proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto$5;
return value === proto;
}
/**
* Creates a unary function that invokes `func` with its argument transformed.
*
* @private
* @param {Function} func The function to wrap.
* @param {Function} transform The argument transform.
* @returns {Function} Returns the new function.
*/
function overArg(func, transform) {
return function(arg) {
return func(transform(arg));
};
}
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeKeys = overArg(Object.keys, Object);
/** Used for built-in method references. */
var objectProto$4 = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty$3 = objectProto$4.hasOwnProperty;
/**
* The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
*
* @private
* @param {Object} object The object to query.
* @returns {Array} Returns the array of property names.
*/
function baseKeys(object) {
if (!isPrototype(object)) {
return nativeKeys(object);
}
var result = [];
for (var key in Object(object)) {
if (hasOwnProperty$3.call(object, key) && key != 'constructor') {
result.push(key);
}
}
return result;
}
/**
* Creates an array of the own enumerable property names of `object`.
*
* **Note:** Non-object values are coerced to objects. See the
* [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
* for more details.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Object
* @param {Object} object The object to query.
* @returns {Array} Returns the array of property names.
* @example
*
* function Foo() {
* this.a = 1;
* this.b = 2;
* }
*
* Foo.prototype.c = 3;
*
* _.keys(new Foo);
* // => ['a', 'b'] (iteration order is not guaranteed)
*
* _.keys('hi');
* // => ['0', '1']
*/
function keys(object) {
return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}
function createArrayIterator(coll) {
var i = -1;
var len = coll.length;
return function next() {
return ++i < len ? {value: coll[i], key: i} : null;
}
}
function createES2015Iterator(iterator) {
var i = -1;
return function next() {
var item = iterator.next();
if (item.done)
return null;
i++;
return {value: item.value, key: i};
}
}
function createObjectIterator(obj) {
var okeys = keys(obj);
var i = -1;
var len = okeys.length;
return function next() {
var key = okeys[++i];
return i < len ? {value: obj[key], key: key} : null;
};
}
function iterator(coll) {
if (isArrayLike(coll)) {
return createArrayIterator(coll);
}
var iterator = getIterator(coll);
return iterator ? createES2015Iterator(iterator) : createObjectIterator(coll);
}
function onlyOnce(fn) {
return function() {
if (fn === null) throw new Error("Callback was already called.");
var callFn = fn;
fn = null;
callFn.apply(this, arguments);
};
}
function _eachOfLimit(limit) {
return function (obj, iteratee, callback) {
callback = once(callback || noop);
if (limit <= 0 || !obj) {
return callback(null);
}
var nextElem = iterator(obj);
var done = false;
var running = 0;
var looping = false;
function iterateeCallback(err, value) {
running -= 1;
if (err) {
done = true;
callback(err);
}
else if (value === breakLoop || (done && running <= 0)) {
done = true;
return callback(null);
}
else if (!looping) {
replenish();
}
}
function replenish () {
looping = true;
while (running < limit && !done) {
var elem = nextElem();
if (elem === null) {
done = true;
if (running <= 0) {
callback(null);
}
return;
}
running += 1;
iteratee(elem.value, elem.key, onlyOnce(iterateeCallback));
}
looping = false;
}
replenish();
};
}
/**
* The same as [`eachOf`]{@link module:Collections.eachOf} but runs a maximum of `limit` async operations at a
* time.
*
* @name eachOfLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.eachOf]{@link module:Collections.eachOf}
* @alias forEachOfLimit
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - An async function to apply to each
* item in `coll`. The `key` is the item's key, or index in the case of an
* array.
* Invoked with (item, key, callback).
* @param {Function} [callback] - A callback which is called when all
* `iteratee` functions have finished, or an error occurs. Invoked with (err).
*/
function eachOfLimit(coll, limit, iteratee, callback) {
_eachOfLimit(limit)(coll, wrapAsync(iteratee), callback);
}
function doLimit(fn, limit) {
return function (iterable, iteratee, callback) {
return fn(iterable, limit, iteratee, callback);
};
}
// eachOf implementation optimized for array-likes
function eachOfArrayLike(coll, iteratee, callback) {
callback = once(callback || noop);
var index = 0,
completed = 0,
length = coll.length;
if (length === 0) {
callback(null);
}
function iteratorCallback(err, value) {
if (err) {
callback(err);
} else if ((++completed === length) || value === breakLoop) {
callback(null);
}
}
for (; index < length; index++) {
iteratee(coll[index], index, onlyOnce(iteratorCallback));
}
}
// a generic version of eachOf which can handle array, object, and iterator cases.
var eachOfGeneric = doLimit(eachOfLimit, Infinity);
/**
* Like [`each`]{@link module:Collections.each}, except that it passes the key (or index) as the second argument
* to the iteratee.
*
* @name eachOf
* @static
* @memberOf module:Collections
* @method
* @alias forEachOf
* @category Collection
* @see [async.each]{@link module:Collections.each}
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - A function to apply to each
* item in `coll`.
* The `key` is the item's key, or index in the case of an array.
* Invoked with (item, key, callback).
* @param {Function} [callback] - A callback which is called when all
* `iteratee` functions have finished, or an error occurs. Invoked with (err).
* @example
*
* var obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"};
* var configs = {};
*
* async.forEachOf(obj, function (value, key, callback) {
* fs.readFile(__dirname + value, "utf8", function (err, data) {
* if (err) return callback(err);
* try {
* configs[key] = JSON.parse(data);
* } catch (e) {
* return callback(e);
* }
* callback();
* });
* }, function (err) {
* if (err) console.error(err.message);
* // configs is now a map of JSON data
* doSomethingWith(configs);
* });
*/
var eachOf = function(coll, iteratee, callback) {
var eachOfImplementation = isArrayLike(coll) ? eachOfArrayLike : eachOfGeneric;
eachOfImplementation(coll, wrapAsync(iteratee), callback);
};
function doParallel(fn) {
return function (obj, iteratee, callback) {
return fn(eachOf, obj, wrapAsync(iteratee), callback);
};
}
function _asyncMap(eachfn, arr, iteratee, callback) {
callback = callback || noop;
arr = arr || [];
var results = [];
var counter = 0;
var _iteratee = wrapAsync(iteratee);
eachfn(arr, function (value, _, callback) {
var index = counter++;
_iteratee(value, function (err, v) {
results[index] = v;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
/**
* Produces a new collection of values by mapping each value in `coll` through
* the `iteratee` function. The `iteratee` is called with an item from `coll`
* and a callback for when it has finished processing. Each of these callback
* takes 2 arguments: an `error`, and the transformed item from `coll`. If
* `iteratee` passes an error to its callback, the main `callback` (for the
* `map` function) is immediately called with the error.
*
* Note, that since this function applies the `iteratee` to each item in
* parallel, there is no guarantee that the `iteratee` functions will complete
* in order. However, the results array will be in the same order as the
* original `coll`.
*
* If `map` is passed an Object, the results will be an Array. The results
* will roughly be in the order of the original Objects' keys (but this can
* vary across JavaScript engines).
*
* @name map
* @static
* @memberOf module:Collections
* @method
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async function to apply to each item in
* `coll`.
* The iteratee should complete with the transformed item.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. Results is an Array of the
* transformed items from the `coll`. Invoked with (err, results).
* @example
*
* async.map(['file1','file2','file3'], fs.stat, function(err, results) {
* // results is now an array of stats for each file
* });
*/
var map = doParallel(_asyncMap);
/**
* Applies the provided arguments to each function in the array, calling
* `callback` after all functions have completed. If you only provide the first
* argument, `fns`, then it will return a function which lets you pass in the
* arguments as if it were a single function call. If more arguments are
* provided, `callback` is required while `args` is still optional.
*
* @name applyEach
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {Array|Iterable|Object} fns - A collection of {@link AsyncFunction}s
* to all call with the same arguments
* @param {...*} [args] - any number of separate arguments to pass to the
* function.
* @param {Function} [callback] - the final argument should be the callback,
* called when all functions have completed processing.
* @returns {Function} - If only the first argument, `fns`, is provided, it will
* return a function which lets you pass in the arguments as if it were a single
* function call. The signature is `(..args, callback)`. If invoked with any
* arguments, `callback` is required.
* @example
*
* async.applyEach([enableSearch, updateSchema], 'bucket', callback);
*
* // partial application example:
* async.each(
* buckets,
* async.applyEach([enableSearch, updateSchema]),
* callback
* );
*/
var applyEach = applyEach$1(map);
function doParallelLimit(fn) {
return function (obj, limit, iteratee, callback) {
return fn(_eachOfLimit(limit), obj, wrapAsync(iteratee), callback);
};
}
/**
* The same as [`map`]{@link module:Collections.map} but runs a maximum of `limit` async operations at a time.
*
* @name mapLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.map]{@link module:Collections.map}
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - An async function to apply to each item in
* `coll`.
* The iteratee should complete with the transformed item.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. Results is an array of the
* transformed items from the `coll`. Invoked with (err, results).
*/
var mapLimit = doParallelLimit(_asyncMap);
/**
* The same as [`map`]{@link module:Collections.map} but runs only a single async operation at a time.
*
* @name mapSeries
* @static
* @memberOf module:Collections
* @method
* @see [async.map]{@link module:Collections.map}
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async function to apply to each item in
* `coll`.
* The iteratee should complete with the transformed item.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. Results is an array of the
* transformed items from the `coll`. Invoked with (err, results).
*/
var mapSeries = doLimit(mapLimit, 1);
/**
* The same as [`applyEach`]{@link module:ControlFlow.applyEach} but runs only a single async operation at a time.
*
* @name applyEachSeries
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.applyEach]{@link module:ControlFlow.applyEach}
* @category Control Flow
* @param {Array|Iterable|Object} fns - A collection of {@link AsyncFunction}s to all
* call with the same arguments
* @param {...*} [args] - any number of separate arguments to pass to the
* function.
* @param {Function} [callback] - the final argument should be the callback,
* called when all functions have completed processing.
* @returns {Function} - If only the first argument is provided, it will return
* a function which lets you pass in the arguments as if it were a single
* function call.
*/
var applyEachSeries = applyEach$1(mapSeries);
/**
* A specialized version of `_.forEach` for arrays without support for
* iteratee shorthands.
*
* @private
* @param {Array} [array] The array to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Array} Returns `array`.
*/
function arrayEach(array, iteratee) {
var index = -1,
length = array == null ? 0 : array.length;
while (++index < length) {
if (iteratee(array[index], index, array) === false) {
break;
}
}
return array;
}
/**
* Creates a base function for methods like `_.forIn` and `_.forOwn`.
*
* @private
* @param {boolean} [fromRight] Specify iterating from right to left.
* @returns {Function} Returns the new base function.
*/
function createBaseFor(fromRight) {
return function(object, iteratee, keysFunc) {
var index = -1,
iterable = Object(object),
props = keysFunc(object),
length = props.length;
while (length--) {
var key = props[fromRight ? length : ++index];
if (iteratee(iterable[key], key, iterable) === false) {
break;
}
}
return object;
};
}
/**
* The base implementation of `baseForOwn` which iterates over `object`
* properties returned by `keysFunc` and invokes `iteratee` for each property.
* Iteratee functions may exit iteration early by explicitly returning `false`.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @param {Function} keysFunc The function to get the keys of `object`.
* @returns {Object} Returns `object`.
*/
var baseFor = createBaseFor();
/**
* The base implementation of `_.forOwn` without support for iteratee shorthands.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Object} Returns `object`.
*/
function baseForOwn(object, iteratee) {
return object && baseFor(object, iteratee, keys);
}
/**
* The base implementation of `_.findIndex` and `_.findLastIndex` without
* support for iteratee shorthands.
*
* @private
* @param {Array} array The array to inspect.
* @param {Function} predicate The function invoked per iteration.
* @param {number} fromIndex The index to search from.
* @param {boolean} [fromRight] Specify iterating from right to left.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function baseFindIndex(array, predicate, fromIndex, fromRight) {
var length = array.length,
index = fromIndex + (fromRight ? 1 : -1);
while ((fromRight ? index-- : ++index < length)) {
if (predicate(array[index], index, array)) {
return index;
}
}
return -1;
}
/**
* The base implementation of `_.isNaN` without support for number objects.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
*/
function baseIsNaN(value) {
return value !== value;
}
/**
* A specialized version of `_.indexOf` which performs strict equality
* comparisons of values, i.e. `===`.
*
* @private
* @param {Array} array The array to inspect.
* @param {*} value The value to search for.
* @param {number} fromIndex The index to search from.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function strictIndexOf(array, value, fromIndex) {
var index = fromIndex - 1,
length = array.length;
while (++index < length) {
if (array[index] === value) {
return index;
}
}
return -1;
}
/**
* The base implementation of `_.indexOf` without `fromIndex` bounds checks.
*
* @private
* @param {Array} array The array to inspect.
* @param {*} value The value to search for.
* @param {number} fromIndex The index to search from.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function baseIndexOf(array, value, fromIndex) {
return value === value
? strictIndexOf(array, value, fromIndex)
: baseFindIndex(array, baseIsNaN, fromIndex);
}
/**
* Determines the best order for running the {@link AsyncFunction}s in `tasks`, based on
* their requirements. Each function can optionally depend on other functions
* being completed first, and each function is run as soon as its requirements
* are satisfied.
*
* If any of the {@link AsyncFunction}s pass an error to their callback, the `auto` sequence
* will stop. Further tasks will not execute (so any other functions depending
* on it will not run), and the main `callback` is immediately called with the
* error.
*
* {@link AsyncFunction}s also receive an object containing the results of functions which
* have completed so far as the first argument, if they have dependencies. If a
* task function has no dependencies, it will only be passed a callback.
*
* @name auto
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {Object} tasks - An object. Each of its properties is either a
* function or an array of requirements, with the {@link AsyncFunction} itself the last item
* in the array. The object's key of a property serves as the name of the task
* defined by that property, i.e. can be used when specifying requirements for
* other tasks. The function receives one or two arguments:
* * a `results` object, containing the results of the previously executed
* functions, only passed if the task has any dependencies,
* * a `callback(err, result)` function, which must be called when finished,
* passing an `error` (which can be `null`) and the result of the function's
* execution.
* @param {number} [concurrency=Infinity] - An optional `integer` for
* determining the maximum number of tasks that can be run in parallel. By
* default, as many as possible.
* @param {Function} [callback] - An optional callback which is called when all
* the tasks have been completed. It receives the `err` argument if any `tasks`
* pass an error to their callback. Results are always returned; however, if an
* error occurs, no further `tasks` will be performed, and the results object
* will only contain partial results. Invoked with (err, results).
* @returns undefined
* @example
*
* async.auto({
* // this function will just be passed a callback
* readData: async.apply(fs.readFile, 'data.txt', 'utf-8'),
* showData: ['readData', function(results, cb) {
* // results.readData is the file's contents
* // ...
* }]
* }, callback);
*
* async.auto({
* get_data: function(callback) {
* console.log('in get_data');
* // async code to get some data
* callback(null, 'data', 'converted to array');
* },
* make_folder: function(callback) {
* console.log('in make_folder');
* // async code to create a directory to store a file in
* // this is run at the same time as getting the data
* callback(null, 'folder');
* },
* write_file: ['get_data', 'make_folder', function(results, callback) {
* console.log('in write_file', JSON.stringify(results));
* // once there is some data and the directory exists,
* // write the data to a file in the directory
* callback(null, 'filename');
* }],
* email_link: ['write_file', function(results, callback) {
* console.log('in email_link', JSON.stringify(results));
* // once the file is written let's email a link to it...
* // results.write_file contains the filename returned by write_file.
* callback(null, {'file':results.write_file, 'email':'user@example.com'});
* }]
* }, function(err, results) {
* console.log('err = ', err);
* console.log('results = ', results);
* });
*/
var auto = function (tasks, concurrency, callback) {
if (typeof concurrency === 'function') {
// concurrency is optional, shift the args.
callback = concurrency;
concurrency = null;
}
callback = once(callback || noop);
var keys$$1 = keys(tasks);
var numTasks = keys$$1.length;
if (!numTasks) {
return callback(null);
}
if (!concurrency) {
concurrency = numTasks;
}
var results = {};
var runningTasks = 0;
var hasError = false;
var listeners = Object.create(null);
var readyTasks = [];
// for cycle detection:
var readyToCheck = []; // tasks that have been identified as reachable
// without the possibility of returning to an ancestor task
var uncheckedDependencies = {};
baseForOwn(tasks, function (task, key) {
if (!isArray(task)) {
// no dependencies
enqueueTask(key, [task]);
readyToCheck.push(key);
return;
}
var dependencies = task.slice(0, task.length - 1);
var remainingDependencies = dependencies.length;
if (remainingDependencies === 0) {
enqueueTask(key, task);
readyToCheck.push(key);
return;
}
uncheckedDependencies[key] = remainingDependencies;
arrayEach(dependencies, function (dependencyName) {
if (!tasks[dependencyName]) {
throw new Error('async.auto task `' + key +
'` has a non-existent dependency `' +
dependencyName + '` in ' +
dependencies.join(', '));
}
addListener(dependencyName, function () {
remainingDependencies--;
if (remainingDependencies === 0) {
enqueueTask(key, task);
}
});
});
});
checkForDeadlocks();
processQueue();
function enqueueTask(key, task) {
readyTasks.push(function () {
runTask(key, task);
});
}
function processQueue() {
if (readyTasks.length === 0 && runningTasks === 0) {
return callback(null, results);
}
while(readyTasks.length && runningTasks < concurrency) {
var run = readyTasks.shift();
run();
}
}
function addListener(taskName, fn) {
var taskListeners = listeners[taskName];
if (!taskListeners) {
taskListeners = listeners[taskName] = [];
}
taskListeners.push(fn);
}
function taskComplete(taskName) {
var taskListeners = listeners[taskName] || [];
arrayEach(taskListeners, function (fn) {
fn();
});
processQueue();
}
function runTask(key, task) {
if (hasError) return;
var taskCallback = onlyOnce(function(err, result) {
runningTasks--;
if (arguments.length > 2) {
result = slice(arguments, 1);
}
if (err) {
var safeResults = {};
baseForOwn(results, function(val, rkey) {
safeResults[rkey] = val;
});
safeResults[key] = result;
hasError = true;
listeners = Object.create(null);
callback(err, safeResults);
} else {
results[key] = result;
taskComplete(key);
}
});
runningTasks++;
var taskFn = wrapAsync(task[task.length - 1]);
if (task.length > 1) {
taskFn(results, taskCallback);
} else {
taskFn(taskCallback);
}
}
function checkForDeadlocks() {
// Kahn's algorithm
// https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm
// http://connalle.blogspot.com/2013/10/topological-sortingkahn-algorithm.html
var currentTask;
var counter = 0;
while (readyToCheck.length) {
currentTask = readyToCheck.pop();
counter++;
arrayEach(getDependents(currentTask), function (dependent) {
if (--uncheckedDependencies[dependent] === 0) {
readyToCheck.push(dependent);
}
});
}
if (counter !== numTasks) {
throw new Error(
'async.auto cannot execute tasks due to a recursive dependency'
);
}
}
function getDependents(taskName) {
var result = [];
baseForOwn(tasks, function (task, key) {
if (isArray(task) && baseIndexOf(task, taskName, 0) >= 0) {
result.push(key);
}
});
return result;
}
};
/**
* A specialized version of `_.map` for arrays without support for iteratee
* shorthands.
*
* @private
* @param {Array} [array] The array to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Array} Returns the new mapped array.
*/
function arrayMap(array, iteratee) {
var index = -1,
length = array == null ? 0 : array.length,
result = Array(length);
while (++index < length) {
result[index] = iteratee(array[index], index, array);
}
return result;
}
/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';
/**
* Checks if `value` is classified as a `Symbol` primitive or object.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
* @example
*
* _.isSymbol(Symbol.iterator);
* // => true
*
* _.isSymbol('abc');
* // => false
*/
function isSymbol(value) {
return typeof value == 'symbol' ||
(isObjectLike(value) && baseGetTag(value) == symbolTag);
}
/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;
/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol$1 ? Symbol$1.prototype : undefined;
var symbolToString = symbolProto ? symbolProto.toString : undefined;
/**
* The base implementation of `_.toString` which doesn't convert nullish
* values to empty strings.
*
* @private
* @param {*} value The value to process.
* @returns {string} Returns the string.
*/
function baseToString(value) {
// Exit early for strings to avoid a performance hit in some environments.
if (typeof value == 'string') {
return value;
}
if (isArray(value)) {
// Recursively convert values (susceptible to call stack limits).
return arrayMap(value, baseToString) + '';
}
if (isSymbol(value)) {
return symbolToString ? symbolToString.call(value) : '';
}
var result = (value + '');
return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}
/**
* The base implementation of `_.slice` without an iteratee call guard.
*
* @private
* @param {Array} array The array to slice.
* @param {number} [start=0] The start position.
* @param {number} [end=array.length] The end position.
* @returns {Array} Returns the slice of `array`.
*/
function baseSlice(array, start, end) {
var index = -1,
length = array.length;
if (start < 0) {
start = -start > length ? 0 : (length + start);
}
end = end > length ? length : end;
if (end < 0) {
end += length;
}
length = start > end ? 0 : ((end - start) >>> 0);
start >>>= 0;
var result = Array(length);
while (++index < length) {
result[index] = array[index + start];
}
return result;
}
/**
* Casts `array` to a slice if it's needed.
*
* @private
* @param {Array} array The array to inspect.
* @param {number} start The start position.
* @param {number} [end=array.length] The end position.
* @returns {Array} Returns the cast slice.
*/
function castSlice(array, start, end) {
var length = array.length;
end = end === undefined ? length : end;
return (!start && end >= length) ? array : baseSlice(array, start, end);
}
/**
* Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
* that is not found in the character symbols.
*
* @private
* @param {Array} strSymbols The string symbols to inspect.
* @param {Array} chrSymbols The character symbols to find.
* @returns {number} Returns the index of the last unmatched string symbol.
*/
function charsEndIndex(strSymbols, chrSymbols) {
var index = strSymbols.length;
while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
return index;
}
/**
* Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
* that is not found in the character symbols.
*
* @private
* @param {Array} strSymbols The string symbols to inspect.
* @param {Array} chrSymbols The character symbols to find.
* @returns {number} Returns the index of the first unmatched string symbol.
*/
function charsStartIndex(strSymbols, chrSymbols) {
var index = -1,
length = strSymbols.length;
while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
return index;
}
/**
* Converts an ASCII `string` to an array.
*
* @private
* @param {string} string The string to convert.
* @returns {Array} Returns the converted array.
*/
function asciiToArray(string) {
return string.split('');
}
/** Used to compose unicode character classes. */
var rsAstralRange = '\\ud800-\\udfff';
var rsComboMarksRange = '\\u0300-\\u036f';
var reComboHalfMarksRange = '\\ufe20-\\ufe2f';
var rsComboSymbolsRange = '\\u20d0-\\u20ff';
var rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange;
var rsVarRange = '\\ufe0e\\ufe0f';
/** Used to compose unicode capture groups. */
var rsZWJ = '\\u200d';
/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');
/**
* Checks if `string` contains Unicode symbols.
*
* @private
* @param {string} string The string to inspect.
* @returns {boolean} Returns `true` if a symbol is found, else `false`.
*/
function hasUnicode(string) {
return reHasUnicode.test(string);
}
/** Used to compose unicode character classes. */
var rsAstralRange$1 = '\\ud800-\\udfff';
var rsComboMarksRange$1 = '\\u0300-\\u036f';
var reComboHalfMarksRange$1 = '\\ufe20-\\ufe2f';
var rsComboSymbolsRange$1 = '\\u20d0-\\u20ff';
var rsComboRange$1 = rsComboMarksRange$1 + reComboHalfMarksRange$1 + rsComboSymbolsRange$1;
var rsVarRange$1 = '\\ufe0e\\ufe0f';
/** Used to compose unicode capture groups. */
var rsAstral = '[' + rsAstralRange$1 + ']';
var rsCombo = '[' + rsComboRange$1 + ']';
var rsFitz = '\\ud83c[\\udffb-\\udfff]';
var rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')';
var rsNonAstral = '[^' + rsAstralRange$1 + ']';
var rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}';
var rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]';
var rsZWJ$1 = '\\u200d';
/** Used to compose unicode regexes. */
var reOptMod = rsModifier + '?';
var rsOptVar = '[' + rsVarRange$1 + ']?';
var rsOptJoin = '(?:' + rsZWJ$1 + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*';
var rsSeq = rsOptVar + reOptMod + rsOptJoin;
var rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
/**
* Converts a Unicode `string` to an array.
*
* @private
* @param {string} string The string to convert.
* @returns {Array} Returns the converted array.
*/
function unicodeToArray(string) {
return string.match(reUnicode) || [];
}
/**
* Converts `string` to an array.
*
* @private
* @param {string} string The string to convert.
* @returns {Array} Returns the converted array.
*/
function stringToArray(string) {
return hasUnicode(string)
? unicodeToArray(string)
: asciiToArray(string);
}
/**
* Converts `value` to a string. An empty string is returned for `null`
* and `undefined` values. The sign of `-0` is preserved.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to convert.
* @returns {string} Returns the converted string.
* @example
*
* _.toString(null);
* // => ''
*
* _.toString(-0);
* // => '-0'
*
* _.toString([1, 2, 3]);
* // => '1,2,3'
*/
function toString(value) {
return value == null ? '' : baseToString(value);
}
/** Used to match leading and trailing whitespace. */
var reTrim = /^\s+|\s+$/g;
/**
* Removes leading and trailing whitespace or specified characters from `string`.
*
* @static
* @memberOf _
* @since 3.0.0
* @category String
* @param {string} [string=''] The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
* @returns {string} Returns the trimmed string.
* @example
*
* _.trim(' abc ');
* // => 'abc'
*
* _.trim('-_-abc-_-', '_-');
* // => 'abc'
*
* _.map([' foo ', ' bar '], _.trim);
* // => ['foo', 'bar']
*/
function trim(string, chars, guard) {
string = toString(string);
if (string && (guard || chars === undefined)) {
return string.replace(reTrim, '');
}
if (!string || !(chars = baseToString(chars))) {
return string;
}
var strSymbols = stringToArray(string),
chrSymbols = stringToArray(chars),
start = charsStartIndex(strSymbols, chrSymbols),
end = charsEndIndex(strSymbols, chrSymbols) + 1;
return castSlice(strSymbols, start, end).join('');
}
var FN_ARGS = /^(?:async\s+)?(function)?\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /(=.+)?(\s*)$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
function parseParams(func) {
func = func.toString().replace(STRIP_COMMENTS, '');
func = func.match(FN_ARGS)[2].replace(' ', '');
func = func ? func.split(FN_ARG_SPLIT) : [];
func = func.map(function (arg){
return trim(arg.replace(FN_ARG, ''));
});
return func;
}
/**
* A dependency-injected version of the [async.auto]{@link module:ControlFlow.auto} function. Dependent
* tasks are specified as parameters to the function, after the usual callback
* parameter, with the parameter names matching the names of the tasks it
* depends on. This can provide even more readable task graphs which can be
* easier to maintain.
*
* If a final callback is specified, the task results are similarly injected,
* specified as named parameters after the initial error parameter.
*
* The autoInject function is purely syntactic sugar and its semantics are
* otherwise equivalent to [async.auto]{@link module:ControlFlow.auto}.
*
* @name autoInject
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.auto]{@link module:ControlFlow.auto}
* @category Control Flow
* @param {Object} tasks - An object, each of whose properties is an {@link AsyncFunction} of
* the form 'func([dependencies...], callback). The object's key of a property
* serves as the name of the task defined by that property, i.e. can be used
* when specifying requirements for other tasks.
* * The `callback` parameter is a `callback(err, result)` which must be called
* when finished, passing an `error` (which can be `null`) and the result of
* the function's execution. The remaining parameters name other tasks on
* which the task is dependent, and the results from those tasks are the
* arguments of those parameters.
* @param {Function} [callback] - An optional callback which is called when all
* the tasks have been completed. It receives the `err` argument if any `tasks`
* pass an error to their callback, and a `results` object with any completed
* task results, similar to `auto`.
* @example
*
* // The example from `auto` can be rewritten as follows:
* async.autoInject({
* get_data: function(callback) {
* // async code to get some data
* callback(null, 'data', 'converted to array');
* },
* make_folder: function(callback) {
* // async code to create a directory to store a file in
* // this is run at the same time as getting the data
* callback(null, 'folder');
* },
* write_file: function(get_data, make_folder, callback) {
* // once there is some data and the directory exists,
* // write the data to a file in the directory
* callback(null, 'filename');
* },
* email_link: function(write_file, callback) {
* // once the file is written let's email a link to it...
* // write_file contains the filename returned by write_file.
* callback(null, {'file':write_file, 'email':'user@example.com'});
* }
* }, function(err, results) {
* console.log('err = ', err);
* console.log('email_link = ', results.email_link);
* });
*
* // If you are using a JS minifier that mangles parameter names, `autoInject`
* // will not work with plain functions, since the parameter names will be
* // collapsed to a single letter identifier. To work around this, you can
* // explicitly specify the names of the parameters your task function needs
* // in an array, similar to Angular.js dependency injection.
*
* // This still has an advantage over plain `auto`, since the results a task
* // depends on are still spread into arguments.
* async.autoInject({
* //...
* write_file: ['get_data', 'make_folder', function(get_data, make_folder, callback) {
* callback(null, 'filename');
* }],
* email_link: ['write_file', function(write_file, callback) {
* callback(null, {'file':write_file, 'email':'user@example.com'});
* }]
* //...
* }, function(err, results) {
* console.log('err = ', err);
* console.log('email_link = ', results.email_link);
* });
*/
function autoInject(tasks, callback) {
var newTasks = {};
baseForOwn(tasks, function (taskFn, key) {
var params;
var fnIsAsync = isAsync(taskFn);
var hasNoDeps =
(!fnIsAsync && taskFn.length === 1) ||
(fnIsAsync && taskFn.length === 0);
if (isArray(taskFn)) {
params = taskFn.slice(0, -1);
taskFn = taskFn[taskFn.length - 1];
newTasks[key] = params.concat(params.length > 0 ? newTask : taskFn);
} else if (hasNoDeps) {
// no dependencies, use the function as-is
newTasks[key] = taskFn;
} else {
params = parseParams(taskFn);
if (taskFn.length === 0 && !fnIsAsync && params.length === 0) {
throw new Error("autoInject task functions require explicit parameters.");
}
// remove callback param
if (!fnIsAsync) params.pop();
newTasks[key] = params.concat(newTask);
}
function newTask(results, taskCb) {
var newArgs = arrayMap(params, function (name) {
return results[name];
});
newArgs.push(taskCb);
wrapAsync(taskFn).apply(null, newArgs);
}
});
auto(newTasks, callback);
}
// Simple doubly linked list (https://en.wikipedia.org/wiki/Doubly_linked_list) implementation
// used for queues. This implementation assumes that the node provided by the user can be modified
// to adjust the next and last properties. We implement only the minimal functionality
// for queue support.
function DLL() {
this.head = this.tail = null;
this.length = 0;
}
function setInitial(dll, node) {
dll.length = 1;
dll.head = dll.tail = node;
}
DLL.prototype.removeLink = function(node) {
if (node.prev) node.prev.next = node.next;
else this.head = node.next;
if (node.next) node.next.prev = node.prev;
else this.tail = node.prev;
node.prev = node.next = null;
this.length -= 1;
return node;
};
DLL.prototype.empty = function () {
while(this.head) this.shift();
return this;
};
DLL.prototype.insertAfter = function(node, newNode) {
newNode.prev = node;
newNode.next = node.next;
if (node.next) node.next.prev = newNode;
else this.tail = newNode;
node.next = newNode;
this.length += 1;
};
DLL.prototype.insertBefore = function(node, newNode) {
newNode.prev = node.prev;
newNode.next = node;
if (node.prev) node.prev.next = newNode;
else this.head = newNode;
node.prev = newNode;
this.length += 1;
};
DLL.prototype.unshift = function(node) {
if (this.head) this.insertBefore(this.head, node);
else setInitial(this, node);
};
DLL.prototype.push = function(node) {
if (this.tail) this.insertAfter(this.tail, node);
else setInitial(this, node);
};
DLL.prototype.shift = function() {
return this.head && this.removeLink(this.head);
};
DLL.prototype.pop = function() {
return this.tail && this.removeLink(this.tail);
};
DLL.prototype.toArray = function () {
var arr = Array(this.length);
var curr = this.head;
for(var idx = 0; idx < this.length; idx++) {
arr[idx] = curr.data;
curr = curr.next;
}
return arr;
};
DLL.prototype.remove = function (testFn) {
var curr = this.head;
while(!!curr) {
var next = curr.next;
if (testFn(curr)) {
this.removeLink(curr);
}
curr = next;
}
return this;
};
function queue(worker, concurrency, payload) {
if (concurrency == null) {
concurrency = 1;
}
else if(concurrency === 0) {
throw new Error('Concurrency must not be zero');
}
var _worker = wrapAsync(worker);
var numRunning = 0;
var workersList = [];
var processingScheduled = false;
function _insert(data, insertAtFront, callback) {
if (callback != null && typeof callback !== 'function') {
throw new Error('task callback must be a function');
}
q.started = true;
if (!isArray(data)) {
data = [data];
}
if (data.length === 0 && q.idle()) {
// call drain immediately if there are no tasks
return setImmediate$1(function() {
q.drain();
});
}
for (var i = 0, l = data.length; i < l; i++) {
var item = {
data: data[i],
callback: callback || noop
};
if (insertAtFront) {
q._tasks.unshift(item);
} else {
q._tasks.push(item);
}
}
if (!processingScheduled) {
processingScheduled = true;
setImmediate$1(function() {
processingScheduled = false;
q.process();
});
}
}
function _next(tasks) {
return function(err){
numRunning -= 1;
for (var i = 0, l = tasks.length; i < l; i++) {
var task = tasks[i];
var index = baseIndexOf(workersList, task, 0);
if (index === 0) {
workersList.shift();
} else if (index > 0) {
workersList.splice(index, 1);
}
task.callback.apply(task, arguments);
if (err != null) {
q.error(err, task.data);
}
}
if (numRunning <= (q.concurrency - q.buffer) ) {
q.unsaturated();
}
if (q.idle()) {
q.drain();
}
q.process();
};
}
var isProcessing = false;
var q = {
_tasks: new DLL(),
concurrency: concurrency,
payload: payload,
saturated: noop,
unsaturated:noop,
buffer: concurrency / 4,
empty: noop,
drain: noop,
error: noop,
started: false,
paused: false,
push: function (data, callback) {
_insert(data, false, callback);
},
kill: function () {
q.drain = noop;
q._tasks.empty();
},
unshift: function (data, callback) {
_insert(data, true, callback);
},
remove: function (testFn) {
q._tasks.remove(testFn);
},
process: function () {
// Avoid trying to start too many processing operations. This can occur
// when callbacks resolve synchronously (#1267).
if (isProcessing) {
return;
}
isProcessing = true;
while(!q.paused && numRunning < q.concurrency && q._tasks.length){
var tasks = [], data = [];
var l = q._tasks.length;
if (q.payload) l = Math.min(l, q.payload);
for (var i = 0; i < l; i++) {
var node = q._tasks.shift();
tasks.push(node);
workersList.push(node);
data.push(node.data);
}
numRunning += 1;
if (q._tasks.length === 0) {
q.empty();
}
if (numRunning === q.concurrency) {
q.saturated();
}
var cb = onlyOnce(_next(tasks));
_worker(data, cb);
}
isProcessing = false;
},
length: function () {
return q._tasks.length;
},
running: function () {
return numRunning;
},
workersList: function () {
return workersList;
},
idle: function() {
return q._tasks.length + numRunning === 0;
},
pause: function () {
q.paused = true;
},
resume: function () {
if (q.paused === false) { return; }
q.paused = false;
setImmediate$1(q.process);
}
};
return q;
}
/**
* A cargo of tasks for the worker function to complete. Cargo inherits all of
* the same methods and event callbacks as [`queue`]{@link module:ControlFlow.queue}.
* @typedef {Object} CargoObject
* @memberOf module:ControlFlow
* @property {Function} length - A function returning the number of items
* waiting to be processed. Invoke like `cargo.length()`.
* @property {number} payload - An `integer` for determining how many tasks
* should be process per round. This property can be changed after a `cargo` is
* created to alter the payload on-the-fly.
* @property {Function} push - Adds `task` to the `queue`. The callback is
* called once the `worker` has finished processing the task. Instead of a
* single task, an array of `tasks` can be submitted. The respective callback is
* used for every task in the list. Invoke like `cargo.push(task, [callback])`.
* @property {Function} saturated - A callback that is called when the
* `queue.length()` hits the concurrency and further tasks will be queued.
* @property {Function} empty - A callback that is called when the last item
* from the `queue` is given to a `worker`.
* @property {Function} drain - A callback that is called when the last item
* from the `queue` has returned from the `worker`.
* @property {Function} idle - a function returning false if there are items
* waiting or being processed, or true if not. Invoke like `cargo.idle()`.
* @property {Function} pause - a function that pauses the processing of tasks
* until `resume()` is called. Invoke like `cargo.pause()`.
* @property {Function} resume - a function that resumes the processing of
* queued tasks when the queue is paused. Invoke like `cargo.resume()`.
* @property {Function} kill - a function that removes the `drain` callback and
* empties remaining tasks from the queue forcing it to go idle. Invoke like `cargo.kill()`.
*/
/**
* Creates a `cargo` object with the specified payload. Tasks added to the
* cargo will be processed altogether (up to the `payload` limit). If the
* `worker` is in progress, the task is queued until it becomes available. Once
* the `worker` has completed some tasks, each callback of those tasks is
* called. Check out [these](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) [animations](https://camo.githubusercontent.com/f4810e00e1c5f5f8addbe3e9f49064fd5d102699/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130312f38346339323036362d356632392d313165322d383134662d3964336430323431336266642e676966)
* for how `cargo` and `queue` work.
*
* While [`queue`]{@link module:ControlFlow.queue} passes only one task to one of a group of workers
* at a time, cargo passes an array of tasks to a single worker, repeating
* when the worker is finished.
*
* @name cargo
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.queue]{@link module:ControlFlow.queue}
* @category Control Flow
* @param {AsyncFunction} worker - An asynchronous function for processing an array
* of queued tasks. Invoked with `(tasks, callback)`.
* @param {number} [payload=Infinity] - An optional `integer` for determining
* how many tasks should be processed per round; if omitted, the default is
* unlimited.
* @returns {module:ControlFlow.CargoObject} A cargo object to manage the tasks. Callbacks can
* attached as certain properties to listen for specific events during the
* lifecycle of the cargo and inner queue.
* @example
*
* // create a cargo object with payload 2
* var cargo = async.cargo(function(tasks, callback) {
* for (var i=0; i<tasks.length; i++) {
* console.log('hello ' + tasks[i].name);
* }
* callback();
* }, 2);
*
* // add some items
* cargo.push({name: 'foo'}, function(err) {
* console.log('finished processing foo');
* });
* cargo.push({name: 'bar'}, function(err) {
* console.log('finished processing bar');
* });
* cargo.push({name: 'baz'}, function(err) {
* console.log('finished processing baz');
* });
*/
function cargo(worker, payload) {
return queue(worker, 1, payload);
}
/**
* The same as [`eachOf`]{@link module:Collections.eachOf} but runs only a single async operation at a time.
*
* @name eachOfSeries
* @static
* @memberOf module:Collections
* @method
* @see [async.eachOf]{@link module:Collections.eachOf}
* @alias forEachOfSeries
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async function to apply to each item in
* `coll`.
* Invoked with (item, key, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. Invoked with (err).
*/
var eachOfSeries = doLimit(eachOfLimit, 1);
/**
* Reduces `coll` into a single value using an async `iteratee` to return each
* successive step. `memo` is the initial state of the reduction. This function
* only operates in series.
*
* For performance reasons, it may make sense to split a call to this function
* into a parallel map, and then use the normal `Array.prototype.reduce` on the
* results. This function is for situations where each step in the reduction
* needs to be async; if you can get the data before reducing it, then it's
* probably a good idea to do so.
*
* @name reduce
* @static
* @memberOf module:Collections
* @method
* @alias inject
* @alias foldl
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {*} memo - The initial state of the reduction.
* @param {AsyncFunction} iteratee - A function applied to each item in the
* array to produce the next step in the reduction.
* The `iteratee` should complete with the next state of the reduction.
* If the iteratee complete with an error, the reduction is stopped and the
* main `callback` is immediately called with the error.
* Invoked with (memo, item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Result is the reduced value. Invoked with
* (err, result).
* @example
*
* async.reduce([1,2,3], 0, function(memo, item, callback) {
* // pointless async:
* process.nextTick(function() {
* callback(null, memo + item)
* });
* }, function(err, result) {
* // result is now equal to the last value of memo, which is 6
* });
*/
function reduce(coll, memo, iteratee, callback) {
callback = once(callback || noop);
var _iteratee = wrapAsync(iteratee);
eachOfSeries(coll, function(x, i, callback) {
_iteratee(memo, x, function(err, v) {
memo = v;
callback(err);
});
}, function(err) {
callback(err, memo);
});
}
/**
* Version of the compose function that is more natural to read. Each function
* consumes the return value of the previous function. It is the equivalent of
* [compose]{@link module:ControlFlow.compose} with the arguments reversed.
*
* Each function is executed with the `this` binding of the composed function.
*
* @name seq
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.compose]{@link module:ControlFlow.compose}
* @category Control Flow
* @param {...AsyncFunction} functions - the asynchronous functions to compose
* @returns {Function} a function that composes the `functions` in order
* @example
*
* // Requires lodash (or underscore), express3 and dresende's orm2.
* // Part of an app, that fetches cats of the logged user.
* // This example uses `seq` function to avoid overnesting and error
* // handling clutter.
* app.get('/cats', function(request, response) {
* var User = request.models.User;
* async.seq(
* _.bind(User.get, User), // 'User.get' has signature (id, callback(err, data))
* function(user, fn) {
* user.getCats(fn); // 'getCats' has signature (callback(err, data))
* }
* )(req.session.user_id, function (err, cats) {
* if (err) {
* console.error(err);
* response.json({ status: 'error', message: err.message });
* } else {
* response.json({ status: 'ok', message: 'Cats found', data: cats });
* }
* });
* });
*/
function seq(/*...functions*/) {
var _functions = arrayMap(arguments, wrapAsync);
return function(/*...args*/) {
var args = slice(arguments);
var that = this;
var cb = args[args.length - 1];
if (typeof cb == 'function') {
args.pop();
} else {
cb = noop;
}
reduce(_functions, args, function(newargs, fn, cb) {
fn.apply(that, newargs.concat(function(err/*, ...nextargs*/) {
var nextargs = slice(arguments, 1);
cb(err, nextargs);
}));
},
function(err, results) {
cb.apply(that, [err].concat(results));
});
};
}
/**
* Creates a function which is a composition of the passed asynchronous
* functions. Each function consumes the return value of the function that
* follows. Composing functions `f()`, `g()`, and `h()` would produce the result
* of `f(g(h()))`, only this version uses callbacks to obtain the return values.
*
* Each function is executed with the `this` binding of the composed function.
*
* @name compose
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {...AsyncFunction} functions - the asynchronous functions to compose
* @returns {Function} an asynchronous function that is the composed
* asynchronous `functions`
* @example
*
* function add1(n, callback) {
* setTimeout(function () {
* callback(null, n + 1);
* }, 10);
* }
*
* function mul3(n, callback) {
* setTimeout(function () {
* callback(null, n * 3);
* }, 10);
* }
*
* var add1mul3 = async.compose(mul3, add1);
* add1mul3(4, function (err, result) {
* // result now equals 15
* });
*/
var compose = function(/*...args*/) {
return seq.apply(null, slice(arguments).reverse());
};
var _concat = Array.prototype.concat;
/**
* The same as [`concat`]{@link module:Collections.concat} but runs a maximum of `limit` async operations at a time.
*
* @name concatLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.concat]{@link module:Collections.concat}
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - A function to apply to each item in `coll`,
* which should use an array as its result. Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished, or an error occurs. Results is an array
* containing the concatenated results of the `iteratee` function. Invoked with
* (err, results).
*/
var concatLimit = function(coll, limit, iteratee, callback) {
callback = callback || noop;
var _iteratee = wrapAsync(iteratee);
mapLimit(coll, limit, function(val, callback) {
_iteratee(val, function(err /*, ...args*/) {
if (err) return callback(err);
return callback(null, slice(arguments, 1));
});
}, function(err, mapResults) {
var result = [];
for (var i = 0; i < mapResults.length; i++) {
if (mapResults[i]) {
result = _concat.apply(result, mapResults[i]);
}
}
return callback(err, result);
});
};
/**
* Applies `iteratee` to each item in `coll`, concatenating the results. Returns
* the concatenated list. The `iteratee`s are called in parallel, and the
* results are concatenated as they return. There is no guarantee that the
* results array will be returned in the original order of `coll` passed to the
* `iteratee` function.
*
* @name concat
* @static
* @memberOf module:Collections
* @method
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - A function to apply to each item in `coll`,
* which should use an array as its result. Invoked with (item, callback).
* @param {Function} [callback(err)] - A callback which is called after all the
* `iteratee` functions have finished, or an error occurs. Results is an array
* containing the concatenated results of the `iteratee` function. Invoked with
* (err, results).
* @example
*
* async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files) {
* // files is now a list of filenames that exist in the 3 directories
* });
*/
var concat = doLimit(concatLimit, Infinity);
/**
* The same as [`concat`]{@link module:Collections.concat} but runs only a single async operation at a time.
*
* @name concatSeries
* @static
* @memberOf module:Collections
* @method
* @see [async.concat]{@link module:Collections.concat}
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - A function to apply to each item in `coll`.
* The iteratee should complete with an array an array of results.
* Invoked with (item, callback).
* @param {Function} [callback(err)] - A callback which is called after all the
* `iteratee` functions have finished, or an error occurs. Results is an array
* containing the concatenated results of the `iteratee` function. Invoked with
* (err, results).
*/
var concatSeries = doLimit(concatLimit, 1);
/**
* Returns a function that when called, calls-back with the values provided.
* Useful as the first function in a [`waterfall`]{@link module:ControlFlow.waterfall}, or for plugging values in to
* [`auto`]{@link module:ControlFlow.auto}.
*
* @name constant
* @static
* @memberOf module:Utils
* @method
* @category Util
* @param {...*} arguments... - Any number of arguments to automatically invoke
* callback with.
* @returns {AsyncFunction} Returns a function that when invoked, automatically
* invokes the callback with the previous given arguments.
* @example
*
* async.waterfall([
* async.constant(42),
* function (value, next) {
* // value === 42
* },
* //...
* ], callback);
*
* async.waterfall([
* async.constant(filename, "utf8"),
* fs.readFile,
* function (fileData, next) {
* //...
* }
* //...
* ], callback);
*
* async.auto({
* hostname: async.constant("https://server.net/"),
* port: findFreePort,
* launchServer: ["hostname", "port", function (options, cb) {
* startServer(options, cb);
* }],
* //...
* }, callback);
*/
var constant = function(/*...values*/) {
var values = slice(arguments);
var args = [null].concat(values);
return function (/*...ignoredArgs, callback*/) {
var callback = arguments[arguments.length - 1];
return callback.apply(this, args);
};
};
/**
* This method returns the first argument it receives.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Util
* @param {*} value Any value.
* @returns {*} Returns `value`.
* @example
*
* var object = { 'a': 1 };
*
* console.log(_.identity(object) === object);
* // => true
*/
function identity(value) {
return value;
}
function _createTester(check, getResult) {
return function(eachfn, arr, iteratee, cb) {
cb = cb || noop;
var testPassed = false;
var testResult;
eachfn(arr, function(value, _, callback) {
iteratee(value, function(err, result) {
if (err) {
callback(err);
} else if (check(result) && !testResult) {
testPassed = true;
testResult = getResult(true, value);
callback(null, breakLoop);
} else {
callback();
}
});
}, function(err) {
if (err) {
cb(err);
} else {
cb(null, testPassed ? testResult : getResult(false));
}
});
};
}
function _findGetResult(v, x) {
return x;
}
/**
* Returns the first value in `coll` that passes an async truth test. The
* `iteratee` is applied in parallel, meaning the first iteratee to return
* `true` will fire the detect `callback` with that result. That means the
* result might not be the first item in the original `coll` (in terms of order)
* that passes the test.
* If order within the original `coll` is important, then look at
* [`detectSeries`]{@link module:Collections.detectSeries}.
*
* @name detect
* @static
* @memberOf module:Collections
* @method
* @alias find
* @category Collections
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - A truth test to apply to each item in `coll`.
* The iteratee must complete with a boolean value as its result.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called as soon as any
* iteratee returns `true`, or after all the `iteratee` functions have finished.
* Result will be the first item in the array that passes the truth test
* (iteratee) or the value `undefined` if none passed. Invoked with
* (err, result).
* @example
*
* async.detect(['file1','file2','file3'], function(filePath, callback) {
* fs.access(filePath, function(err) {
* callback(null, !err)
* });
* }, function(err, result) {
* // result now equals the first file in the list that exists
* });
*/
var detect = doParallel(_createTester(identity, _findGetResult));
/**
* The same as [`detect`]{@link module:Collections.detect} but runs a maximum of `limit` async operations at a
* time.
*
* @name detectLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.detect]{@link module:Collections.detect}
* @alias findLimit
* @category Collections
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - A truth test to apply to each item in `coll`.
* The iteratee must complete with a boolean value as its result.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called as soon as any
* iteratee returns `true`, or after all the `iteratee` functions have finished.
* Result will be the first item in the array that passes the truth test
* (iteratee) or the value `undefined` if none passed. Invoked with
* (err, result).
*/
var detectLimit = doParallelLimit(_createTester(identity, _findGetResult));
/**
* The same as [`detect`]{@link module:Collections.detect} but runs only a single async operation at a time.
*
* @name detectSeries
* @static
* @memberOf module:Collections
* @method
* @see [async.detect]{@link module:Collections.detect}
* @alias findSeries
* @category Collections
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - A truth test to apply to each item in `coll`.
* The iteratee must complete with a boolean value as its result.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called as soon as any
* iteratee returns `true`, or after all the `iteratee` functions have finished.
* Result will be the first item in the array that passes the truth test
* (iteratee) or the value `undefined` if none passed. Invoked with
* (err, result).
*/
var detectSeries = doLimit(detectLimit, 1);
function consoleFunc(name) {
return function (fn/*, ...args*/) {
var args = slice(arguments, 1);
args.push(function (err/*, ...args*/) {
var args = slice(arguments, 1);
if (typeof console === 'object') {
if (err) {
if (console.error) {
console.error(err);
}
} else if (console[name]) {
arrayEach(args, function (x) {
console[name](x);
});
}
}
});
wrapAsync(fn).apply(null, args);
};
}
/**
* Logs the result of an [`async` function]{@link AsyncFunction} to the
* `console` using `console.dir` to display the properties of the resulting object.
* Only works in Node.js or in browsers that support `console.dir` and
* `console.error` (such as FF and Chrome).
* If multiple arguments are returned from the async function,
* `console.dir` is called on each argument in order.
*
* @name dir
* @static
* @memberOf module:Utils
* @method
* @category Util
* @param {AsyncFunction} function - The function you want to eventually apply
* all arguments to.
* @param {...*} arguments... - Any number of arguments to apply to the function.
* @example
*
* // in a module
* var hello = function(name, callback) {
* setTimeout(function() {
* callback(null, {hello: name});
* }, 1000);
* };
*
* // in the node repl
* node> async.dir(hello, 'world');
* {hello: 'world'}
*/
var dir = consoleFunc('dir');
/**
* The post-check version of [`during`]{@link module:ControlFlow.during}. To reflect the difference in
* the order of operations, the arguments `test` and `fn` are switched.
*
* Also a version of [`doWhilst`]{@link module:ControlFlow.doWhilst} with asynchronous `test` function.
* @name doDuring
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.during]{@link module:ControlFlow.during}
* @category Control Flow
* @param {AsyncFunction} fn - An async function which is called each time
* `test` passes. Invoked with (callback).
* @param {AsyncFunction} test - asynchronous truth test to perform before each
* execution of `fn`. Invoked with (...args, callback), where `...args` are the
* non-error args from the previous callback of `fn`.
* @param {Function} [callback] - A callback which is called after the test
* function has failed and repeated execution of `fn` has stopped. `callback`
* will be passed an error if one occurred, otherwise `null`.
*/
function doDuring(fn, test, callback) {
callback = onlyOnce(callback || noop);
var _fn = wrapAsync(fn);
var _test = wrapAsync(test);
function next(err/*, ...args*/) {
if (err) return callback(err);
var args = slice(arguments, 1);
args.push(check);
_test.apply(this, args);
}
function check(err, truth) {
if (err) return callback(err);
if (!truth) return callback(null);
_fn(next);
}
check(null, true);
}
/**
* The post-check version of [`whilst`]{@link module:ControlFlow.whilst}. To reflect the difference in
* the order of operations, the arguments `test` and `iteratee` are switched.
*
* `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
*
* @name doWhilst
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.whilst]{@link module:ControlFlow.whilst}
* @category Control Flow
* @param {AsyncFunction} iteratee - A function which is called each time `test`
* passes. Invoked with (callback).
* @param {Function} test - synchronous truth test to perform after each
* execution of `iteratee`. Invoked with any non-error callback results of
* `iteratee`.
* @param {Function} [callback] - A callback which is called after the test
* function has failed and repeated execution of `iteratee` has stopped.
* `callback` will be passed an error and any arguments passed to the final
* `iteratee`'s callback. Invoked with (err, [results]);
*/
function doWhilst(iteratee, test, callback) {
callback = onlyOnce(callback || noop);
var _iteratee = wrapAsync(iteratee);
var next = function(err/*, ...args*/) {
if (err) return callback(err);
var args = slice(arguments, 1);
if (test.apply(this, args)) return _iteratee(next);
callback.apply(null, [null].concat(args));
};
_iteratee(next);
}
/**
* Like ['doWhilst']{@link module:ControlFlow.doWhilst}, except the `test` is inverted. Note the
* argument ordering differs from `until`.
*
* @name doUntil
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.doWhilst]{@link module:ControlFlow.doWhilst}
* @category Control Flow
* @param {AsyncFunction} iteratee - An async function which is called each time
* `test` fails. Invoked with (callback).
* @param {Function} test - synchronous truth test to perform after each
* execution of `iteratee`. Invoked with any non-error callback results of
* `iteratee`.
* @param {Function} [callback] - A callback which is called after the test
* function has passed and repeated execution of `iteratee` has stopped. `callback`
* will be passed an error and any arguments passed to the final `iteratee`'s
* callback. Invoked with (err, [results]);
*/
function doUntil(iteratee, test, callback) {
doWhilst(iteratee, function() {
return !test.apply(this, arguments);
}, callback);
}
/**
* Like [`whilst`]{@link module:ControlFlow.whilst}, except the `test` is an asynchronous function that
* is passed a callback in the form of `function (err, truth)`. If error is
* passed to `test` or `fn`, the main callback is immediately called with the
* value of the error.
*
* @name during
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.whilst]{@link module:ControlFlow.whilst}
* @category Control Flow
* @param {AsyncFunction} test - asynchronous truth test to perform before each
* execution of `fn`. Invoked with (callback).
* @param {AsyncFunction} fn - An async function which is called each time
* `test` passes. Invoked with (callback).
* @param {Function} [callback] - A callback which is called after the test
* function has failed and repeated execution of `fn` has stopped. `callback`
* will be passed an error, if one occurred, otherwise `null`.
* @example
*
* var count = 0;
*
* async.during(
* function (callback) {
* return callback(null, count < 5);
* },
* function (callback) {
* count++;
* setTimeout(callback, 1000);
* },
* function (err) {
* // 5 seconds have passed
* }
* );
*/
function during(test, fn, callback) {
callback = onlyOnce(callback || noop);
var _fn = wrapAsync(fn);
var _test = wrapAsync(test);
function next(err) {
if (err) return callback(err);
_test(check);
}
function check(err, truth) {
if (err) return callback(err);
if (!truth) return callback(null);
_fn(next);
}
_test(check);
}
function _withoutIndex(iteratee) {
return function (value, index, callback) {
return iteratee(value, callback);
};
}
/**
* Applies the function `iteratee` to each item in `coll`, in parallel.
* The `iteratee` is called with an item from the list, and a callback for when
* it has finished. If the `iteratee` passes an error to its `callback`, the
* main `callback` (for the `each` function) is immediately called with the
* error.
*
* Note, that since this function applies `iteratee` to each item in parallel,
* there is no guarantee that the iteratee functions will complete in order.
*
* @name each
* @static
* @memberOf module:Collections
* @method
* @alias forEach
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async function to apply to
* each item in `coll`. Invoked with (item, callback).
* The array index is not passed to the iteratee.
* If you need the index, use `eachOf`.
* @param {Function} [callback] - A callback which is called when all
* `iteratee` functions have finished, or an error occurs. Invoked with (err).
* @example
*
* // assuming openFiles is an array of file names and saveFile is a function
* // to save the modified contents of that file:
*
* async.each(openFiles, saveFile, function(err){
* // if any of the saves produced an error, err would equal that error
* });
*
* // assuming openFiles is an array of file names
* async.each(openFiles, function(file, callback) {
*
* // Perform operation on file here.
* console.log('Processing file ' + file);
*
* if( file.length > 32 ) {
* console.log('This file name is too long');
* callback('File name too long');
* } else {
* // Do work to process file here
* console.log('File processed');
* callback();
* }
* }, function(err) {
* // if any of the file processing produced an error, err would equal that error
* if( err ) {
* // One of the iterations produced an error.
* // All processing will now stop.
* console.log('A file failed to process');
* } else {
* console.log('All files have been processed successfully');
* }
* });
*/
function eachLimit(coll, iteratee, callback) {
eachOf(coll, _withoutIndex(wrapAsync(iteratee)), callback);
}
/**
* The same as [`each`]{@link module:Collections.each} but runs a maximum of `limit` async operations at a time.
*
* @name eachLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.each]{@link module:Collections.each}
* @alias forEachLimit
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - An async function to apply to each item in
* `coll`.
* The array index is not passed to the iteratee.
* If you need the index, use `eachOfLimit`.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called when all
* `iteratee` functions have finished, or an error occurs. Invoked with (err).
*/
function eachLimit$1(coll, limit, iteratee, callback) {
_eachOfLimit(limit)(coll, _withoutIndex(wrapAsync(iteratee)), callback);
}
/**
* The same as [`each`]{@link module:Collections.each} but runs only a single async operation at a time.
*
* @name eachSeries
* @static
* @memberOf module:Collections
* @method
* @see [async.each]{@link module:Collections.each}
* @alias forEachSeries
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async function to apply to each
* item in `coll`.
* The array index is not passed to the iteratee.
* If you need the index, use `eachOfSeries`.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called when all
* `iteratee` functions have finished, or an error occurs. Invoked with (err).
*/
var eachSeries = doLimit(eachLimit$1, 1);
/**
* Wrap an async function and ensure it calls its callback on a later tick of
* the event loop. If the function already calls its callback on a next tick,
* no extra deferral is added. This is useful for preventing stack overflows
* (`RangeError: Maximum call stack size exceeded`) and generally keeping
* [Zalgo](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony)
* contained. ES2017 `async` functions are returned as-is -- they are immune
* to Zalgo's corrupting influences, as they always resolve on a later tick.
*
* @name ensureAsync
* @static
* @memberOf module:Utils
* @method
* @category Util
* @param {AsyncFunction} fn - an async function, one that expects a node-style
* callback as its last argument.
* @returns {AsyncFunction} Returns a wrapped function with the exact same call
* signature as the function passed in.
* @example
*
* function sometimesAsync(arg, callback) {
* if (cache[arg]) {
* return callback(null, cache[arg]); // this would be synchronous!!
* } else {
* doSomeIO(arg, callback); // this IO would be asynchronous
* }
* }
*
* // this has a risk of stack overflows if many results are cached in a row
* async.mapSeries(args, sometimesAsync, done);
*
* // this will defer sometimesAsync's callback if necessary,
* // preventing stack overflows
* async.mapSeries(args, async.ensureAsync(sometimesAsync), done);
*/
function ensureAsync(fn) {
if (isAsync(fn)) return fn;
return initialParams(function (args, callback) {
var sync = true;
args.push(function () {
var innerArgs = arguments;
if (sync) {
setImmediate$1(function () {
callback.apply(null, innerArgs);
});
} else {
callback.apply(null, innerArgs);
}
});
fn.apply(this, args);
sync = false;
});
}
function notId(v) {
return !v;
}
/**
* Returns `true` if every element in `coll` satisfies an async test. If any
* iteratee call returns `false`, the main `callback` is immediately called.
*
* @name every
* @static
* @memberOf module:Collections
* @method
* @alias all
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async truth test to apply to each item
* in the collection in parallel.
* The iteratee must complete with a boolean result value.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Result will be either `true` or `false`
* depending on the values of the async tests. Invoked with (err, result).
* @example
*
* async.every(['file1','file2','file3'], function(filePath, callback) {
* fs.access(filePath, function(err) {
* callback(null, !err)
* });
* }, function(err, result) {
* // if result is true then every file exists
* });
*/
var every = doParallel(_createTester(notId, notId));
/**
* The same as [`every`]{@link module:Collections.every} but runs a maximum of `limit` async operations at a time.
*
* @name everyLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.every]{@link module:Collections.every}
* @alias allLimit
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - An async truth test to apply to each item
* in the collection in parallel.
* The iteratee must complete with a boolean result value.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Result will be either `true` or `false`
* depending on the values of the async tests. Invoked with (err, result).
*/
var everyLimit = doParallelLimit(_createTester(notId, notId));
/**
* The same as [`every`]{@link module:Collections.every} but runs only a single async operation at a time.
*
* @name everySeries
* @static
* @memberOf module:Collections
* @method
* @see [async.every]{@link module:Collections.every}
* @alias allSeries
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async truth test to apply to each item
* in the collection in series.
* The iteratee must complete with a boolean result value.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Result will be either `true` or `false`
* depending on the values of the async tests. Invoked with (err, result).
*/
var everySeries = doLimit(everyLimit, 1);
/**
* The base implementation of `_.property` without support for deep paths.
*
* @private
* @param {string} key The key of the property to get.
* @returns {Function} Returns the new accessor function.
*/
function baseProperty(key) {
return function(object) {
return object == null ? undefined : object[key];
};
}
function filterArray(eachfn, arr, iteratee, callback) {
var truthValues = new Array(arr.length);
eachfn(arr, function (x, index, callback) {
iteratee(x, function (err, v) {
truthValues[index] = !!v;
callback(err);
});
}, function (err) {
if (err) return callback(err);
var results = [];
for (var i = 0; i < arr.length; i++) {
if (truthValues[i]) results.push(arr[i]);
}
callback(null, results);
});
}
function filterGeneric(eachfn, coll, iteratee, callback) {
var results = [];
eachfn(coll, function (x, index, callback) {
iteratee(x, function (err, v) {
if (err) {
callback(err);
} else {
if (v) {
results.push({index: index, value: x});
}
callback();
}
});
}, function (err) {
if (err) {
callback(err);
} else {
callback(null, arrayMap(results.sort(function (a, b) {
return a.index - b.index;
}), baseProperty('value')));
}
});
}
function _filter(eachfn, coll, iteratee, callback) {
var filter = isArrayLike(coll) ? filterArray : filterGeneric;
filter(eachfn, coll, wrapAsync(iteratee), callback || noop);
}
/**
* Returns a new array of all the values in `coll` which pass an async truth
* test. This operation is performed in parallel, but the results array will be
* in the same order as the original.
*
* @name filter
* @static
* @memberOf module:Collections
* @method
* @alias select
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {Function} iteratee - A truth test to apply to each item in `coll`.
* The `iteratee` is passed a `callback(err, truthValue)`, which must be called
* with a boolean argument once it has completed. Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Invoked with (err, results).
* @example
*
* async.filter(['file1','file2','file3'], function(filePath, callback) {
* fs.access(filePath, function(err) {
* callback(null, !err)
* });
* }, function(err, results) {
* // results now equals an array of the existing files
* });
*/
var filter = doParallel(_filter);
/**
* The same as [`filter`]{@link module:Collections.filter} but runs a maximum of `limit` async operations at a
* time.
*
* @name filterLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.filter]{@link module:Collections.filter}
* @alias selectLimit
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {Function} iteratee - A truth test to apply to each item in `coll`.
* The `iteratee` is passed a `callback(err, truthValue)`, which must be called
* with a boolean argument once it has completed. Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Invoked with (err, results).
*/
var filterLimit = doParallelLimit(_filter);
/**
* The same as [`filter`]{@link module:Collections.filter} but runs only a single async operation at a time.
*
* @name filterSeries
* @static
* @memberOf module:Collections
* @method
* @see [async.filter]{@link module:Collections.filter}
* @alias selectSeries
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {Function} iteratee - A truth test to apply to each item in `coll`.
* The `iteratee` is passed a `callback(err, truthValue)`, which must be called
* with a boolean argument once it has completed. Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Invoked with (err, results)
*/
var filterSeries = doLimit(filterLimit, 1);
/**
* Calls the asynchronous function `fn` with a callback parameter that allows it
* to call itself again, in series, indefinitely.
* If an error is passed to the callback then `errback` is called with the
* error, and execution stops, otherwise it will never be called.
*
* @name forever
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {AsyncFunction} fn - an async function to call repeatedly.
* Invoked with (next).
* @param {Function} [errback] - when `fn` passes an error to it's callback,
* this function will be called, and execution stops. Invoked with (err).
* @example
*
* async.forever(
* function(next) {
* // next is suitable for passing to things that need a callback(err [, whatever]);
* // it will result in this function being called again.
* },
* function(err) {
* // if next is called with a value in its first parameter, it will appear
* // in here as 'err', and execution will stop.
* }
* );
*/
function forever(fn, errback) {
var done = onlyOnce(errback || noop);
var task = wrapAsync(ensureAsync(fn));
function next(err) {
if (err) return done(err);
task(next);
}
next();
}
/**
* The same as [`groupBy`]{@link module:Collections.groupBy} but runs a maximum of `limit` async operations at a time.
*
* @name groupByLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.groupBy]{@link module:Collections.groupBy}
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - An async function to apply to each item in
* `coll`.
* The iteratee should complete with a `key` to group the value under.
* Invoked with (value, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. Result is an `Object` whoses
* properties are arrays of values which returned the corresponding key.
*/
var groupByLimit = function(coll, limit, iteratee, callback) {
callback = callback || noop;
var _iteratee = wrapAsync(iteratee);
mapLimit(coll, limit, function(val, callback) {
_iteratee(val, function(err, key) {
if (err) return callback(err);
return callback(null, {key: key, val: val});
});
}, function(err, mapResults) {
var result = {};
// from MDN, handle object having an `hasOwnProperty` prop
var hasOwnProperty = Object.prototype.hasOwnProperty;
for (var i = 0; i < mapResults.length; i++) {
if (mapResults[i]) {
var key = mapResults[i].key;
var val = mapResults[i].val;
if (hasOwnProperty.call(result, key)) {
result[key].push(val);
} else {
result[key] = [val];
}
}
}
return callback(err, result);
});
};
/**
* Returns a new object, where each value corresponds to an array of items, from
* `coll`, that returned the corresponding key. That is, the keys of the object
* correspond to the values passed to the `iteratee` callback.
*
* Note: Since this function applies the `iteratee` to each item in parallel,
* there is no guarantee that the `iteratee` functions will complete in order.
* However, the values for each key in the `result` will be in the same order as
* the original `coll`. For Objects, the values will roughly be in the order of
* the original Objects' keys (but this can vary across JavaScript engines).
*
* @name groupBy
* @static
* @memberOf module:Collections
* @method
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async function to apply to each item in
* `coll`.
* The iteratee should complete with a `key` to group the value under.
* Invoked with (value, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. Result is an `Object` whoses
* properties are arrays of values which returned the corresponding key.
* @example
*
* async.groupBy(['userId1', 'userId2', 'userId3'], function(userId, callback) {
* db.findById(userId, function(err, user) {
* if (err) return callback(err);
* return callback(null, user.age);
* });
* }, function(err, result) {
* // result is object containing the userIds grouped by age
* // e.g. { 30: ['userId1', 'userId3'], 42: ['userId2']};
* });
*/
var groupBy = doLimit(groupByLimit, Infinity);
/**
* The same as [`groupBy`]{@link module:Collections.groupBy} but runs only a single async operation at a time.
*
* @name groupBySeries
* @static
* @memberOf module:Collections
* @method
* @see [async.groupBy]{@link module:Collections.groupBy}
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - An async function to apply to each item in
* `coll`.
* The iteratee should complete with a `key` to group the value under.
* Invoked with (value, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. Result is an `Object` whoses
* properties are arrays of values which returned the corresponding key.
*/
var groupBySeries = doLimit(groupByLimit, 1);
/**
* Logs the result of an `async` function to the `console`. Only works in
* Node.js or in browsers that support `console.log` and `console.error` (such
* as FF and Chrome). If multiple arguments are returned from the async
* function, `console.log` is called on each argument in order.
*
* @name log
* @static
* @memberOf module:Utils
* @method
* @category Util
* @param {AsyncFunction} function - The function you want to eventually apply
* all arguments to.
* @param {...*} arguments... - Any number of arguments to apply to the function.
* @example
*
* // in a module
* var hello = function(name, callback) {
* setTimeout(function() {
* callback(null, 'hello ' + name);
* }, 1000);
* };
*
* // in the node repl
* node> async.log(hello, 'world');
* 'hello world'
*/
var log = consoleFunc('log');
/**
* The same as [`mapValues`]{@link module:Collections.mapValues} but runs a maximum of `limit` async operations at a
* time.
*
* @name mapValuesLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.mapValues]{@link module:Collections.mapValues}
* @category Collection
* @param {Object} obj - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - A function to apply to each value and key
* in `coll`.
* The iteratee should complete with the transformed value as its result.
* Invoked with (value, key, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. `result` is a new object consisting
* of each key from `obj`, with each transformed value on the right-hand side.
* Invoked with (err, result).
*/
function mapValuesLimit(obj, limit, iteratee, callback) {
callback = once(callback || noop);
var newObj = {};
var _iteratee = wrapAsync(iteratee);
eachOfLimit(obj, limit, function(val, key, next) {
_iteratee(val, key, function (err, result) {
if (err) return next(err);
newObj[key] = result;
next();
});
}, function (err) {
callback(err, newObj);
});
}
/**
* A relative of [`map`]{@link module:Collections.map}, designed for use with objects.
*
* Produces a new Object by mapping each value of `obj` through the `iteratee`
* function. The `iteratee` is called each `value` and `key` from `obj` and a
* callback for when it has finished processing. Each of these callbacks takes
* two arguments: an `error`, and the transformed item from `obj`. If `iteratee`
* passes an error to its callback, the main `callback` (for the `mapValues`
* function) is immediately called with the error.
*
* Note, the order of the keys in the result is not guaranteed. The keys will
* be roughly in the order they complete, (but this is very engine-specific)
*
* @name mapValues
* @static
* @memberOf module:Collections
* @method
* @category Collection
* @param {Object} obj - A collection to iterate over.
* @param {AsyncFunction} iteratee - A function to apply to each value and key
* in `coll`.
* The iteratee should complete with the transformed value as its result.
* Invoked with (value, key, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. `result` is a new object consisting
* of each key from `obj`, with each transformed value on the right-hand side.
* Invoked with (err, result).
* @example
*
* async.mapValues({
* f1: 'file1',
* f2: 'file2',
* f3: 'file3'
* }, function (file, key, callback) {
* fs.stat(file, callback);
* }, function(err, result) {
* // result is now a map of stats for each file, e.g.
* // {
* // f1: [stats for file1],
* // f2: [stats for file2],
* // f3: [stats for file3]
* // }
* });
*/
var mapValues = doLimit(mapValuesLimit, Infinity);
/**
* The same as [`mapValues`]{@link module:Collections.mapValues} but runs only a single async operation at a time.
*
* @name mapValuesSeries
* @static
* @memberOf module:Collections
* @method
* @see [async.mapValues]{@link module:Collections.mapValues}
* @category Collection
* @param {Object} obj - A collection to iterate over.
* @param {AsyncFunction} iteratee - A function to apply to each value and key
* in `coll`.
* The iteratee should complete with the transformed value as its result.
* Invoked with (value, key, callback).
* @param {Function} [callback] - A callback which is called when all `iteratee`
* functions have finished, or an error occurs. `result` is a new object consisting
* of each key from `obj`, with each transformed value on the right-hand side.
* Invoked with (err, result).
*/
var mapValuesSeries = doLimit(mapValuesLimit, 1);
function has(obj, key) {
return key in obj;
}
/**
* Caches the results of an async function. When creating a hash to store
* function results against, the callback is omitted from the hash and an
* optional hash function can be used.
*
* If no hash function is specified, the first argument is used as a hash key,
* which may work reasonably if it is a string or a data type that converts to a
* distinct string. Note that objects and arrays will not behave reasonably.
* Neither will cases where the other arguments are significant. In such cases,
* specify your own hash function.
*
* The cache of results is exposed as the `memo` property of the function
* returned by `memoize`.
*
* @name memoize
* @static
* @memberOf module:Utils
* @method
* @category Util
* @param {AsyncFunction} fn - The async function to proxy and cache results from.
* @param {Function} hasher - An optional function for generating a custom hash
* for storing results. It has all the arguments applied to it apart from the
* callback, and must be synchronous.
* @returns {AsyncFunction} a memoized version of `fn`
* @example
*
* var slow_fn = function(name, callback) {
* // do something
* callback(null, result);
* };
* var fn = async.memoize(slow_fn);
*
* // fn can now be used as if it were slow_fn
* fn('some name', function() {
* // callback
* });
*/
function memoize(fn, hasher) {
var memo = Object.create(null);
var queues = Object.create(null);
hasher = hasher || identity;
var _fn = wrapAsync(fn);
var memoized = initialParams(function memoized(args, callback) {
var key = hasher.apply(null, args);
if (has(memo, key)) {
setImmediate$1(function() {
callback.apply(null, memo[key]);
});
} else if (has(queues, key)) {
queues[key].push(callback);
} else {
queues[key] = [callback];
_fn.apply(null, args.concat(function(/*args*/) {
var args = slice(arguments);
memo[key] = args;
var q = queues[key];
delete queues[key];
for (var i = 0, l = q.length; i < l; i++) {
q[i].apply(null, args);
}
}));
}
});
memoized.memo = memo;
memoized.unmemoized = fn;
return memoized;
}
/**
* Calls `callback` on a later loop around the event loop. In Node.js this just
* calls `process.nextTick`. In the browser it will use `setImmediate` if
* available, otherwise `setTimeout(callback, 0)`, which means other higher
* priority events may precede the execution of `callback`.
*
* This is used internally for browser-compatibility purposes.
*
* @name nextTick
* @static
* @memberOf module:Utils
* @method
* @see [async.setImmediate]{@link module:Utils.setImmediate}
* @category Util
* @param {Function} callback - The function to call on a later loop around
* the event loop. Invoked with (args...).
* @param {...*} args... - any number of additional arguments to pass to the
* callback on the next tick.
* @example
*
* var call_order = [];
* async.nextTick(function() {
* call_order.push('two');
* // call_order now equals ['one','two']
* });
* call_order.push('one');
*
* async.setImmediate(function (a, b, c) {
* // a, b, and c equal 1, 2, and 3
* }, 1, 2, 3);
*/
var _defer$1;
if (hasNextTick) {
_defer$1 = process.nextTick;
} else if (hasSetImmediate) {
_defer$1 = setImmediate;
} else {
_defer$1 = fallback;
}
var nextTick = wrap(_defer$1);
function _parallel(eachfn, tasks, callback) {
callback = callback || noop;
var results = isArrayLike(tasks) ? [] : {};
eachfn(tasks, function (task, key, callback) {
wrapAsync(task)(function (err, result) {
if (arguments.length > 2) {
result = slice(arguments, 1);
}
results[key] = result;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
/**
* Run the `tasks` collection of functions in parallel, without waiting until
* the previous function has completed. If any of the functions pass an error to
* its callback, the main `callback` is immediately called with the value of the
* error. Once the `tasks` have completed, the results are passed to the final
* `callback` as an array.
*
* **Note:** `parallel` is about kicking-off I/O tasks in parallel, not about
* parallel execution of code. If your tasks do not use any timers or perform
* any I/O, they will actually be executed in series. Any synchronous setup
* sections for each task will happen one after the other. JavaScript remains
* single-threaded.
*
* **Hint:** Use [`reflect`]{@link module:Utils.reflect} to continue the
* execution of other tasks when a task fails.
*
* It is also possible to use an object instead of an array. Each property will
* be run as a function and the results will be passed to the final `callback`
* as an object instead of an array. This can be a more readable way of handling
* results from {@link async.parallel}.
*
* @name parallel
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {Array|Iterable|Object} tasks - A collection of
* [async functions]{@link AsyncFunction} to run.
* Each async function can complete with any number of optional `result` values.
* @param {Function} [callback] - An optional callback to run once all the
* functions have completed successfully. This function gets a results array
* (or object) containing all the result arguments passed to the task callbacks.
* Invoked with (err, results).
*
* @example
* async.parallel([
* function(callback) {
* setTimeout(function() {
* callback(null, 'one');
* }, 200);
* },
* function(callback) {
* setTimeout(function() {
* callback(null, 'two');
* }, 100);
* }
* ],
* // optional callback
* function(err, results) {
* // the results array will equal ['one','two'] even though
* // the second function had a shorter timeout.
* });
*
* // an example using an object instead of an array
* async.parallel({
* one: function(callback) {
* setTimeout(function() {
* callback(null, 1);
* }, 200);
* },
* two: function(callback) {
* setTimeout(function() {
* callback(null, 2);
* }, 100);
* }
* }, function(err, results) {
* // results is now equals to: {one: 1, two: 2}
* });
*/
function parallelLimit(tasks, callback) {
_parallel(eachOf, tasks, callback);
}
/**
* The same as [`parallel`]{@link module:ControlFlow.parallel} but runs a maximum of `limit` async operations at a
* time.
*
* @name parallelLimit
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.parallel]{@link module:ControlFlow.parallel}
* @category Control Flow
* @param {Array|Iterable|Object} tasks - A collection of
* [async functions]{@link AsyncFunction} to run.
* Each async function can complete with any number of optional `result` values.
* @param {number} limit - The maximum number of async operations at a time.
* @param {Function} [callback] - An optional callback to run once all the
* functions have completed successfully. This function gets a results array
* (or object) containing all the result arguments passed to the task callbacks.
* Invoked with (err, results).
*/
function parallelLimit$1(tasks, limit, callback) {
_parallel(_eachOfLimit(limit), tasks, callback);
}
/**
* A queue of tasks for the worker function to complete.
* @typedef {Object} QueueObject
* @memberOf module:ControlFlow
* @property {Function} length - a function returning the number of items
* waiting to be processed. Invoke with `queue.length()`.
* @property {boolean} started - a boolean indicating whether or not any
* items have been pushed and processed by the queue.
* @property {Function} running - a function returning the number of items
* currently being processed. Invoke with `queue.running()`.
* @property {Function} workersList - a function returning the array of items
* currently being processed. Invoke with `queue.workersList()`.
* @property {Function} idle - a function returning false if there are items
* waiting or being processed, or true if not. Invoke with `queue.idle()`.
* @property {number} concurrency - an integer for determining how many `worker`
* functions should be run in parallel. This property can be changed after a
* `queue` is created to alter the concurrency on-the-fly.
* @property {Function} push - add a new task to the `queue`. Calls `callback`
* once the `worker` has finished processing the task. Instead of a single task,
* a `tasks` array can be submitted. The respective callback is used for every
* task in the list. Invoke with `queue.push(task, [callback])`,
* @property {Function} unshift - add a new task to the front of the `queue`.
* Invoke with `queue.unshift(task, [callback])`.
* @property {Function} remove - remove items from the queue that match a test
* function. The test function will be passed an object with a `data` property,
* and a `priority` property, if this is a
* [priorityQueue]{@link module:ControlFlow.priorityQueue} object.
* Invoked with `queue.remove(testFn)`, where `testFn` is of the form
* `function ({data, priority}) {}` and returns a Boolean.
* @property {Function} saturated - a callback that is called when the number of
* running workers hits the `concurrency` limit, and further tasks will be
* queued.
* @property {Function} unsaturated - a callback that is called when the number
* of running workers is less than the `concurrency` & `buffer` limits, and
* further tasks will not be queued.
* @property {number} buffer - A minimum threshold buffer in order to say that
* the `queue` is `unsaturated`.
* @property {Function} empty - a callback that is called when the last item
* from the `queue` is given to a `worker`.
* @property {Function} drain - a callback that is called when the last item
* from the `queue` has returned from the `worker`.
* @property {Function} error - a callback that is called when a task errors.
* Has the signature `function(error, task)`.
* @property {boolean} paused - a boolean for determining whether the queue is
* in a paused state.
* @property {Function} pause - a function that pauses the processing of tasks
* until `resume()` is called. Invoke with `queue.pause()`.
* @property {Function} resume - a function that resumes the processing of
* queued tasks when the queue is paused. Invoke with `queue.resume()`.
* @property {Function} kill - a function that removes the `drain` callback and
* empties remaining tasks from the queue forcing it to go idle. No more tasks
* should be pushed to the queue after calling this function. Invoke with `queue.kill()`.
*/
/**
* Creates a `queue` object with the specified `concurrency`. Tasks added to the
* `queue` are processed in parallel (up to the `concurrency` limit). If all
* `worker`s are in progress, the task is queued until one becomes available.
* Once a `worker` completes a `task`, that `task`'s callback is called.
*
* @name queue
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {AsyncFunction} worker - An async function for processing a queued task.
* If you want to handle errors from an individual task, pass a callback to
* `q.push()`. Invoked with (task, callback).
* @param {number} [concurrency=1] - An `integer` for determining how many
* `worker` functions should be run in parallel. If omitted, the concurrency
* defaults to `1`. If the concurrency is `0`, an error is thrown.
* @returns {module:ControlFlow.QueueObject} A queue object to manage the tasks. Callbacks can
* attached as certain properties to listen for specific events during the
* lifecycle of the queue.
* @example
*
* // create a queue object with concurrency 2
* var q = async.queue(function(task, callback) {
* console.log('hello ' + task.name);
* callback();
* }, 2);
*
* // assign a callback
* q.drain = function() {
* console.log('all items have been processed');
* };
*
* // add some items to the queue
* q.push({name: 'foo'}, function(err) {
* console.log('finished processing foo');
* });
* q.push({name: 'bar'}, function (err) {
* console.log('finished processing bar');
* });
*
* // add some items to the queue (batch-wise)
* q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
* console.log('finished processing item');
* });
*
* // add some items to the front of the queue
* q.unshift({name: 'bar'}, function (err) {
* console.log('finished processing bar');
* });
*/
var queue$1 = function (worker, concurrency) {
var _worker = wrapAsync(worker);
return queue(function (items, cb) {
_worker(items[0], cb);
}, concurrency, 1);
};
/**
* The same as [async.queue]{@link module:ControlFlow.queue} only tasks are assigned a priority and
* completed in ascending priority order.
*
* @name priorityQueue
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.queue]{@link module:ControlFlow.queue}
* @category Control Flow
* @param {AsyncFunction} worker - An async function for processing a queued task.
* If you want to handle errors from an individual task, pass a callback to
* `q.push()`.
* Invoked with (task, callback).
* @param {number} concurrency - An `integer` for determining how many `worker`
* functions should be run in parallel. If omitted, the concurrency defaults to
* `1`. If the concurrency is `0`, an error is thrown.
* @returns {module:ControlFlow.QueueObject} A priorityQueue object to manage the tasks. There are two
* differences between `queue` and `priorityQueue` objects:
* * `push(task, priority, [callback])` - `priority` should be a number. If an
* array of `tasks` is given, all tasks will be assigned the same priority.
* * The `unshift` method was removed.
*/
var priorityQueue = function(worker, concurrency) {
// Start with a normal queue
var q = queue$1(worker, concurrency);
// Override push to accept second parameter representing priority
q.push = function(data, priority, callback) {
if (callback == null) callback = noop;
if (typeof callback !== 'function') {
throw new Error('task callback must be a function');
}
q.started = true;
if (!isArray(data)) {
data = [data];
}
if (data.length === 0) {
// call drain immediately if there are no tasks
return setImmediate$1(function() {
q.drain();
});
}
priority = priority || 0;
var nextNode = q._tasks.head;
while (nextNode && priority >= nextNode.priority) {
nextNode = nextNode.next;
}
for (var i = 0, l = data.length; i < l; i++) {
var item = {
data: data[i],
priority: priority,
callback: callback
};
if (nextNode) {
q._tasks.insertBefore(nextNode, item);
} else {
q._tasks.push(item);
}
}
setImmediate$1(q.process);
};
// Remove unshift function
delete q.unshift;
return q;
};
/**
* Runs the `tasks` array of functions in parallel, without waiting until the
* previous function has completed. Once any of the `tasks` complete or pass an
* error to its callback, the main `callback` is immediately called. It's
* equivalent to `Promise.race()`.
*
* @name race
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {Array} tasks - An array containing [async functions]{@link AsyncFunction}
* to run. Each function can complete with an optional `result` value.
* @param {Function} callback - A callback to run once any of the functions have
* completed. This function gets an error or result from the first function that
* completed. Invoked with (err, result).
* @returns undefined
* @example
*
* async.race([
* function(callback) {
* setTimeout(function() {
* callback(null, 'one');
* }, 200);
* },
* function(callback) {
* setTimeout(function() {
* callback(null, 'two');
* }, 100);
* }
* ],
* // main callback
* function(err, result) {
* // the result will be equal to 'two' as it finishes earlier
* });
*/
function race(tasks, callback) {
callback = once(callback || noop);
if (!isArray(tasks)) return callback(new TypeError('First argument to race must be an array of functions'));
if (!tasks.length) return callback();
for (var i = 0, l = tasks.length; i < l; i++) {
wrapAsync(tasks[i])(callback);
}
}
/**
* Same as [`reduce`]{@link module:Collections.reduce}, only operates on `array` in reverse order.
*
* @name reduceRight
* @static
* @memberOf module:Collections
* @method
* @see [async.reduce]{@link module:Collections.reduce}
* @alias foldr
* @category Collection
* @param {Array} array - A collection to iterate over.
* @param {*} memo - The initial state of the reduction.
* @param {AsyncFunction} iteratee - A function applied to each item in the
* array to produce the next step in the reduction.
* The `iteratee` should complete with the next state of the reduction.
* If the iteratee complete with an error, the reduction is stopped and the
* main `callback` is immediately called with the error.
* Invoked with (memo, item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Result is the reduced value. Invoked with
* (err, result).
*/
function reduceRight (array, memo, iteratee, callback) {
var reversed = slice(array).reverse();
reduce(reversed, memo, iteratee, callback);
}
/**
* Wraps the async function in another function that always completes with a
* result object, even when it errors.
*
* The result object has either the property `error` or `value`.
*
* @name reflect
* @static
* @memberOf module:Utils
* @method
* @category Util
* @param {AsyncFunction} fn - The async function you want to wrap
* @returns {Function} - A function that always passes null to it's callback as
* the error. The second argument to the callback will be an `object` with
* either an `error` or a `value` property.
* @example
*
* async.parallel([
* async.reflect(function(callback) {
* // do some stuff ...
* callback(null, 'one');
* }),
* async.reflect(function(callback) {
* // do some more stuff but error ...
* callback('bad stuff happened');
* }),
* async.reflect(function(callback) {
* // do some more stuff ...
* callback(null, 'two');
* })
* ],
* // optional callback
* function(err, results) {
* // values
* // results[0].value = 'one'
* // results[1].error = 'bad stuff happened'
* // results[2].value = 'two'
* });
*/
function reflect(fn) {
var _fn = wrapAsync(fn);
return initialParams(function reflectOn(args, reflectCallback) {
args.push(function callback(error, cbArg) {
if (error) {
reflectCallback(null, { error: error });
} else {
var value;
if (arguments.length <= 2) {
value = cbArg;
} else {
value = slice(arguments, 1);
}
reflectCallback(null, { value: value });
}
});
return _fn.apply(this, args);
});
}
/**
* A helper function that wraps an array or an object of functions with `reflect`.
*
* @name reflectAll
* @static
* @memberOf module:Utils
* @method
* @see [async.reflect]{@link module:Utils.reflect}
* @category Util
* @param {Array|Object|Iterable} tasks - The collection of
* [async functions]{@link AsyncFunction} to wrap in `async.reflect`.
* @returns {Array} Returns an array of async functions, each wrapped in
* `async.reflect`
* @example
*
* let tasks = [
* function(callback) {
* setTimeout(function() {
* callback(null, 'one');
* }, 200);
* },
* function(callback) {
* // do some more stuff but error ...
* callback(new Error('bad stuff happened'));
* },
* function(callback) {
* setTimeout(function() {
* callback(null, 'two');
* }, 100);
* }
* ];
*
* async.parallel(async.reflectAll(tasks),
* // optional callback
* function(err, results) {
* // values
* // results[0].value = 'one'
* // results[1].error = Error('bad stuff happened')
* // results[2].value = 'two'
* });
*
* // an example using an object instead of an array
* let tasks = {
* one: function(callback) {
* setTimeout(function() {
* callback(null, 'one');
* }, 200);
* },
* two: function(callback) {
* callback('two');
* },
* three: function(callback) {
* setTimeout(function() {
* callback(null, 'three');
* }, 100);
* }
* };
*
* async.parallel(async.reflectAll(tasks),
* // optional callback
* function(err, results) {
* // values
* // results.one.value = 'one'
* // results.two.error = 'two'
* // results.three.value = 'three'
* });
*/
function reflectAll(tasks) {
var results;
if (isArray(tasks)) {
results = arrayMap(tasks, reflect);
} else {
results = {};
baseForOwn(tasks, function(task, key) {
results[key] = reflect.call(this, task);
});
}
return results;
}
function reject$1(eachfn, arr, iteratee, callback) {
_filter(eachfn, arr, function(value, cb) {
iteratee(value, function(err, v) {
cb(err, !v);
});
}, callback);
}
/**
* The opposite of [`filter`]{@link module:Collections.filter}. Removes values that pass an `async` truth test.
*
* @name reject
* @static
* @memberOf module:Collections
* @method
* @see [async.filter]{@link module:Collections.filter}
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {Function} iteratee - An async truth test to apply to each item in
* `coll`.
* The should complete with a boolean value as its `result`.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Invoked with (err, results).
* @example
*
* async.reject(['file1','file2','file3'], function(filePath, callback) {
* fs.access(filePath, function(err) {
* callback(null, !err)
* });
* }, function(err, results) {
* // results now equals an array of missing files
* createFiles(results);
* });
*/
var reject = doParallel(reject$1);
/**
* The same as [`reject`]{@link module:Collections.reject} but runs a maximum of `limit` async operations at a
* time.
*
* @name rejectLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.reject]{@link module:Collections.reject}
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {Function} iteratee - An async truth test to apply to each item in
* `coll`.
* The should complete with a boolean value as its `result`.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Invoked with (err, results).
*/
var rejectLimit = doParallelLimit(reject$1);
/**
* The same as [`reject`]{@link module:Collections.reject} but runs only a single async operation at a time.
*
* @name rejectSeries
* @static
* @memberOf module:Collections
* @method
* @see [async.reject]{@link module:Collections.reject}
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {Function} iteratee - An async truth test to apply to each item in
* `coll`.
* The should complete with a boolean value as its `result`.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Invoked with (err, results).
*/
var rejectSeries = doLimit(rejectLimit, 1);
/**
* Creates a function that returns `value`.
*
* @static
* @memberOf _
* @since 2.4.0
* @category Util
* @param {*} value The value to return from the new function.
* @returns {Function} Returns the new constant function.
* @example
*
* var objects = _.times(2, _.constant({ 'a': 1 }));
*
* console.log(objects);
* // => [{ 'a': 1 }, { 'a': 1 }]
*
* console.log(objects[0] === objects[1]);
* // => true
*/
function constant$1(value) {
return function() {
return value;
};
}
/**
* Attempts to get a successful response from `task` no more than `times` times
* before returning an error. If the task is successful, the `callback` will be
* passed the result of the successful task. If all attempts fail, the callback
* will be passed the error and result (if any) of the final attempt.
*
* @name retry
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @see [async.retryable]{@link module:ControlFlow.retryable}
* @param {Object|number} [opts = {times: 5, interval: 0}| 5] - Can be either an
* object with `times` and `interval` or a number.
* * `times` - The number of attempts to make before giving up. The default
* is `5`.
* * `interval` - The time to wait between retries, in milliseconds. The
* default is `0`. The interval may also be specified as a function of the
* retry count (see example).
* * `errorFilter` - An optional synchronous function that is invoked on
* erroneous result. If it returns `true` the retry attempts will continue;
* if the function returns `false` the retry flow is aborted with the current
* attempt's error and result being returned to the final callback.
* Invoked with (err).
* * If `opts` is a number, the number specifies the number of times to retry,
* with the default interval of `0`.
* @param {AsyncFunction} task - An async function to retry.
* Invoked with (callback).
* @param {Function} [callback] - An optional callback which is called when the
* task has succeeded, or after the final failed attempt. It receives the `err`
* and `result` arguments of the last attempt at completing the `task`. Invoked
* with (err, results).
*
* @example
*
* // The `retry` function can be used as a stand-alone control flow by passing
* // a callback, as shown below:
*
* // try calling apiMethod 3 times
* async.retry(3, apiMethod, function(err, result) {
* // do something with the result
* });
*
* // try calling apiMethod 3 times, waiting 200 ms between each retry
* async.retry({times: 3, interval: 200}, apiMethod, function(err, result) {
* // do something with the result
* });
*
* // try calling apiMethod 10 times with exponential backoff
* // (i.e. intervals of 100, 200, 400, 800, 1600, ... milliseconds)
* async.retry({
* times: 10,
* interval: function(retryCount) {
* return 50 * Math.pow(2, retryCount);
* }
* }, apiMethod, function(err, result) {
* // do something with the result
* });
*
* // try calling apiMethod the default 5 times no delay between each retry
* async.retry(apiMethod, function(err, result) {
* // do something with the result
* });
*
* // try calling apiMethod only when error condition satisfies, all other
* // errors will abort the retry control flow and return to final callback
* async.retry({
* errorFilter: function(err) {
* return err.message === 'Temporary error'; // only retry on a specific error
* }
* }, apiMethod, function(err, result) {
* // do something with the result
* });
*
* // to retry individual methods that are not as reliable within other
* // control flow functions, use the `retryable` wrapper:
* async.auto({
* users: api.getUsers.bind(api),
* payments: async.retryable(3, api.getPayments.bind(api))
* }, function(err, results) {
* // do something with the results
* });
*
*/
function retry(opts, task, callback) {
var DEFAULT_TIMES = 5;
var DEFAULT_INTERVAL = 0;
var options = {
times: DEFAULT_TIMES,
intervalFunc: constant$1(DEFAULT_INTERVAL)
};
function parseTimes(acc, t) {
if (typeof t === 'object') {
acc.times = +t.times || DEFAULT_TIMES;
acc.intervalFunc = typeof t.interval === 'function' ?
t.interval :
constant$1(+t.interval || DEFAULT_INTERVAL);
acc.errorFilter = t.errorFilter;
} else if (typeof t === 'number' || typeof t === 'string') {
acc.times = +t || DEFAULT_TIMES;
} else {
throw new Error("Invalid arguments for async.retry");
}
}
if (arguments.length < 3 && typeof opts === 'function') {
callback = task || noop;
task = opts;
} else {
parseTimes(options, opts);
callback = callback || noop;
}
if (typeof task !== 'function') {
throw new Error("Invalid arguments for async.retry");
}
var _task = wrapAsync(task);
var attempt = 1;
function retryAttempt() {
_task(function(err) {
if (err && attempt++ < options.times &&
(typeof options.errorFilter != 'function' ||
options.errorFilter(err))) {
setTimeout(retryAttempt, options.intervalFunc(attempt));
} else {
callback.apply(null, arguments);
}
});
}
retryAttempt();
}
/**
* A close relative of [`retry`]{@link module:ControlFlow.retry}. This method
* wraps a task and makes it retryable, rather than immediately calling it
* with retries.
*
* @name retryable
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.retry]{@link module:ControlFlow.retry}
* @category Control Flow
* @param {Object|number} [opts = {times: 5, interval: 0}| 5] - optional
* options, exactly the same as from `retry`
* @param {AsyncFunction} task - the asynchronous function to wrap.
* This function will be passed any arguments passed to the returned wrapper.
* Invoked with (...args, callback).
* @returns {AsyncFunction} The wrapped function, which when invoked, will
* retry on an error, based on the parameters specified in `opts`.
* This function will accept the same parameters as `task`.
* @example
*
* async.auto({
* dep1: async.retryable(3, getFromFlakyService),
* process: ["dep1", async.retryable(3, function (results, cb) {
* maybeProcessData(results.dep1, cb);
* })]
* }, callback);
*/
var retryable = function (opts, task) {
if (!task) {
task = opts;
opts = null;
}
var _task = wrapAsync(task);
return initialParams(function (args, callback) {
function taskFn(cb) {
_task.apply(null, args.concat(cb));
}
if (opts) retry(opts, taskFn, callback);
else retry(taskFn, callback);
});
};
/**
* Run the functions in the `tasks` collection in series, each one running once
* the previous function has completed. If any functions in the series pass an
* error to its callback, no more functions are run, and `callback` is
* immediately called with the value of the error. Otherwise, `callback`
* receives an array of results when `tasks` have completed.
*
* It is also possible to use an object instead of an array. Each property will
* be run as a function, and the results will be passed to the final `callback`
* as an object instead of an array. This can be a more readable way of handling
* results from {@link async.series}.
*
* **Note** that while many implementations preserve the order of object
* properties, the [ECMAScript Language Specification](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6)
* explicitly states that
*
* > The mechanics and order of enumerating the properties is not specified.
*
* So if you rely on the order in which your series of functions are executed,
* and want this to work on all platforms, consider using an array.
*
* @name series
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {Array|Iterable|Object} tasks - A collection containing
* [async functions]{@link AsyncFunction} to run in series.
* Each function can complete with any number of optional `result` values.
* @param {Function} [callback] - An optional callback to run once all the
* functions have completed. This function gets a results array (or object)
* containing all the result arguments passed to the `task` callbacks. Invoked
* with (err, result).
* @example
* async.series([
* function(callback) {
* // do some stuff ...
* callback(null, 'one');
* },
* function(callback) {
* // do some more stuff ...
* callback(null, 'two');
* }
* ],
* // optional callback
* function(err, results) {
* // results is now equal to ['one', 'two']
* });
*
* async.series({
* one: function(callback) {
* setTimeout(function() {
* callback(null, 1);
* }, 200);
* },
* two: function(callback){
* setTimeout(function() {
* callback(null, 2);
* }, 100);
* }
* }, function(err, results) {
* // results is now equal to: {one: 1, two: 2}
* });
*/
function series(tasks, callback) {
_parallel(eachOfSeries, tasks, callback);
}
/**
* Returns `true` if at least one element in the `coll` satisfies an async test.
* If any iteratee call returns `true`, the main `callback` is immediately
* called.
*
* @name some
* @static
* @memberOf module:Collections
* @method
* @alias any
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async truth test to apply to each item
* in the collections in parallel.
* The iteratee should complete with a boolean `result` value.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called as soon as any
* iteratee returns `true`, or after all the iteratee functions have finished.
* Result will be either `true` or `false` depending on the values of the async
* tests. Invoked with (err, result).
* @example
*
* async.some(['file1','file2','file3'], function(filePath, callback) {
* fs.access(filePath, function(err) {
* callback(null, !err)
* });
* }, function(err, result) {
* // if result is true then at least one of the files exists
* });
*/
var some = doParallel(_createTester(Boolean, identity));
/**
* The same as [`some`]{@link module:Collections.some} but runs a maximum of `limit` async operations at a time.
*
* @name someLimit
* @static
* @memberOf module:Collections
* @method
* @see [async.some]{@link module:Collections.some}
* @alias anyLimit
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - An async truth test to apply to each item
* in the collections in parallel.
* The iteratee should complete with a boolean `result` value.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called as soon as any
* iteratee returns `true`, or after all the iteratee functions have finished.
* Result will be either `true` or `false` depending on the values of the async
* tests. Invoked with (err, result).
*/
var someLimit = doParallelLimit(_createTester(Boolean, identity));
/**
* The same as [`some`]{@link module:Collections.some} but runs only a single async operation at a time.
*
* @name someSeries
* @static
* @memberOf module:Collections
* @method
* @see [async.some]{@link module:Collections.some}
* @alias anySeries
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async truth test to apply to each item
* in the collections in series.
* The iteratee should complete with a boolean `result` value.
* Invoked with (item, callback).
* @param {Function} [callback] - A callback which is called as soon as any
* iteratee returns `true`, or after all the iteratee functions have finished.
* Result will be either `true` or `false` depending on the values of the async
* tests. Invoked with (err, result).
*/
var someSeries = doLimit(someLimit, 1);
/**
* Sorts a list by the results of running each `coll` value through an async
* `iteratee`.
*
* @name sortBy
* @static
* @memberOf module:Collections
* @method
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {AsyncFunction} iteratee - An async function to apply to each item in
* `coll`.
* The iteratee should complete with a value to use as the sort criteria as
* its `result`.
* Invoked with (item, callback).
* @param {Function} callback - A callback which is called after all the
* `iteratee` functions have finished, or an error occurs. Results is the items
* from the original `coll` sorted by the values returned by the `iteratee`
* calls. Invoked with (err, results).
* @example
*
* async.sortBy(['file1','file2','file3'], function(file, callback) {
* fs.stat(file, function(err, stats) {
* callback(err, stats.mtime);
* });
* }, function(err, results) {
* // results is now the original array of files sorted by
* // modified date
* });
*
* // By modifying the callback parameter the
* // sorting order can be influenced:
*
* // ascending order
* async.sortBy([1,9,3,5], function(x, callback) {
* callback(null, x);
* }, function(err,result) {
* // result callback
* });
*
* // descending order
* async.sortBy([1,9,3,5], function(x, callback) {
* callback(null, x*-1); //<- x*-1 instead of x, turns the order around
* }, function(err,result) {
* // result callback
* });
*/
function sortBy (coll, iteratee, callback) {
var _iteratee = wrapAsync(iteratee);
map(coll, function (x, callback) {
_iteratee(x, function (err, criteria) {
if (err) return callback(err);
callback(null, {value: x, criteria: criteria});
});
}, function (err, results) {
if (err) return callback(err);
callback(null, arrayMap(results.sort(comparator), baseProperty('value')));
});
function comparator(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}
}
/**
* Sets a time limit on an asynchronous function. If the function does not call
* its callback within the specified milliseconds, it will be called with a
* timeout error. The code property for the error object will be `'ETIMEDOUT'`.
*
* @name timeout
* @static
* @memberOf module:Utils
* @method
* @category Util
* @param {AsyncFunction} asyncFn - The async function to limit in time.
* @param {number} milliseconds - The specified time limit.
* @param {*} [info] - Any variable you want attached (`string`, `object`, etc)
* to timeout Error for more information..
* @returns {AsyncFunction} Returns a wrapped function that can be used with any
* of the control flow functions.
* Invoke this function with the same parameters as you would `asyncFunc`.
* @example
*
* function myFunction(foo, callback) {
* doAsyncTask(foo, function(err, data) {
* // handle errors
* if (err) return callback(err);
*
* // do some stuff ...
*
* // return processed data
* return callback(null, data);
* });
* }
*
* var wrapped = async.timeout(myFunction, 1000);
*
* // call `wrapped` as you would `myFunction`
* wrapped({ bar: 'bar' }, function(err, data) {
* // if `myFunction` takes < 1000 ms to execute, `err`
* // and `data` will have their expected values
*
* // else `err` will be an Error with the code 'ETIMEDOUT'
* });
*/
function timeout(asyncFn, milliseconds, info) {
var fn = wrapAsync(asyncFn);
return initialParams(function (args, callback) {
var timedOut = false;
var timer;
function timeoutCallback() {
var name = asyncFn.name || 'anonymous';
var error = new Error('Callback function "' + name + '" timed out.');
error.code = 'ETIMEDOUT';
if (info) {
error.info = info;
}
timedOut = true;
callback(error);
}
args.push(function () {
if (!timedOut) {
callback.apply(null, arguments);
clearTimeout(timer);
}
});
// setup timer and call original function
timer = setTimeout(timeoutCallback, milliseconds);
fn.apply(null, args);
});
}
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeCeil = Math.ceil;
var nativeMax = Math.max;
/**
* The base implementation of `_.range` and `_.rangeRight` which doesn't
* coerce arguments.
*
* @private
* @param {number} start The start of the range.
* @param {number} end The end of the range.
* @param {number} step The value to increment or decrement by.
* @param {boolean} [fromRight] Specify iterating from right to left.
* @returns {Array} Returns the range of numbers.
*/
function baseRange(start, end, step, fromRight) {
var index = -1,
length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
result = Array(length);
while (length--) {
result[fromRight ? length : ++index] = start;
start += step;
}
return result;
}
/**
* The same as [times]{@link module:ControlFlow.times} but runs a maximum of `limit` async operations at a
* time.
*
* @name timesLimit
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.times]{@link module:ControlFlow.times}
* @category Control Flow
* @param {number} count - The number of times to run the function.
* @param {number} limit - The maximum number of async operations at a time.
* @param {AsyncFunction} iteratee - The async function to call `n` times.
* Invoked with the iteration index and a callback: (n, next).
* @param {Function} callback - see [async.map]{@link module:Collections.map}.
*/
function timeLimit(count, limit, iteratee, callback) {
var _iteratee = wrapAsync(iteratee);
mapLimit(baseRange(0, count, 1), limit, _iteratee, callback);
}
/**
* Calls the `iteratee` function `n` times, and accumulates results in the same
* manner you would use with [map]{@link module:Collections.map}.
*
* @name times
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.map]{@link module:Collections.map}
* @category Control Flow
* @param {number} n - The number of times to run the function.
* @param {AsyncFunction} iteratee - The async function to call `n` times.
* Invoked with the iteration index and a callback: (n, next).
* @param {Function} callback - see {@link module:Collections.map}.
* @example
*
* // Pretend this is some complicated async factory
* var createUser = function(id, callback) {
* callback(null, {
* id: 'user' + id
* });
* };
*
* // generate 5 users
* async.times(5, function(n, next) {
* createUser(n, function(err, user) {
* next(err, user);
* });
* }, function(err, users) {
* // we should now have 5 users
* });
*/
var times = doLimit(timeLimit, Infinity);
/**
* The same as [times]{@link module:ControlFlow.times} but runs only a single async operation at a time.
*
* @name timesSeries
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.times]{@link module:ControlFlow.times}
* @category Control Flow
* @param {number} n - The number of times to run the function.
* @param {AsyncFunction} iteratee - The async function to call `n` times.
* Invoked with the iteration index and a callback: (n, next).
* @param {Function} callback - see {@link module:Collections.map}.
*/
var timesSeries = doLimit(timeLimit, 1);
/**
* A relative of `reduce`. Takes an Object or Array, and iterates over each
* element in series, each step potentially mutating an `accumulator` value.
* The type of the accumulator defaults to the type of collection passed in.
*
* @name transform
* @static
* @memberOf module:Collections
* @method
* @category Collection
* @param {Array|Iterable|Object} coll - A collection to iterate over.
* @param {*} [accumulator] - The initial state of the transform. If omitted,
* it will default to an empty Object or Array, depending on the type of `coll`
* @param {AsyncFunction} iteratee - A function applied to each item in the
* collection that potentially modifies the accumulator.
* Invoked with (accumulator, item, key, callback).
* @param {Function} [callback] - A callback which is called after all the
* `iteratee` functions have finished. Result is the transformed accumulator.
* Invoked with (err, result).
* @example
*
* async.transform([1,2,3], function(acc, item, index, callback) {
* // pointless async:
* process.nextTick(function() {
* acc.push(item * 2)
* callback(null)
* });
* }, function(err, result) {
* // result is now equal to [2, 4, 6]
* });
*
* @example
*
* async.transform({a: 1, b: 2, c: 3}, function (obj, val, key, callback) {
* setImmediate(function () {
* obj[key] = val * 2;
* callback();
* })
* }, function (err, result) {
* // result is equal to {a: 2, b: 4, c: 6}
* })
*/
function transform (coll, accumulator, iteratee, callback) {
if (arguments.length <= 3) {
callback = iteratee;
iteratee = accumulator;
accumulator = isArray(coll) ? [] : {};
}
callback = once(callback || noop);
var _iteratee = wrapAsync(iteratee);
eachOf(coll, function(v, k, cb) {
_iteratee(accumulator, v, k, cb);
}, function(err) {
callback(err, accumulator);
});
}
/**
* It runs each task in series but stops whenever any of the functions were
* successful. If one of the tasks were successful, the `callback` will be
* passed the result of the successful task. If all tasks fail, the callback
* will be passed the error and result (if any) of the final attempt.
*
* @name tryEach
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {Array|Iterable|Object} tasks - A collection containing functions to
* run, each function is passed a `callback(err, result)` it must call on
* completion with an error `err` (which can be `null`) and an optional `result`
* value.
* @param {Function} [callback] - An optional callback which is called when one
* of the tasks has succeeded, or all have failed. It receives the `err` and
* `result` arguments of the last attempt at completing the `task`. Invoked with
* (err, results).
* @example
* async.tryEach([
* function getDataFromFirstWebsite(callback) {
* // Try getting the data from the first website
* callback(err, data);
* },
* function getDataFromSecondWebsite(callback) {
* // First website failed,
* // Try getting the data from the backup website
* callback(err, data);
* }
* ],
* // optional callback
* function(err, results) {
* Now do something with the data.
* });
*
*/
function tryEach(tasks, callback) {
var error = null;
var result;
callback = callback || noop;
eachSeries(tasks, function(task, callback) {
wrapAsync(task)(function (err, res/*, ...args*/) {
if (arguments.length > 2) {
result = slice(arguments, 1);
} else {
result = res;
}
error = err;
callback(!err);
});
}, function () {
callback(error, result);
});
}
/**
* Undoes a [memoize]{@link module:Utils.memoize}d function, reverting it to the original,
* unmemoized form. Handy for testing.
*
* @name unmemoize
* @static
* @memberOf module:Utils
* @method
* @see [async.memoize]{@link module:Utils.memoize}
* @category Util
* @param {AsyncFunction} fn - the memoized function
* @returns {AsyncFunction} a function that calls the original unmemoized function
*/
function unmemoize(fn) {
return function () {
return (fn.unmemoized || fn).apply(null, arguments);
};
}
/**
* Repeatedly call `iteratee`, while `test` returns `true`. Calls `callback` when
* stopped, or an error occurs.
*
* @name whilst
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {Function} test - synchronous truth test to perform before each
* execution of `iteratee`. Invoked with ().
* @param {AsyncFunction} iteratee - An async function which is called each time
* `test` passes. Invoked with (callback).
* @param {Function} [callback] - A callback which is called after the test
* function has failed and repeated execution of `iteratee` has stopped. `callback`
* will be passed an error and any arguments passed to the final `iteratee`'s
* callback. Invoked with (err, [results]);
* @returns undefined
* @example
*
* var count = 0;
* async.whilst(
* function() { return count < 5; },
* function(callback) {
* count++;
* setTimeout(function() {
* callback(null, count);
* }, 1000);
* },
* function (err, n) {
* // 5 seconds have passed, n = 5
* }
* );
*/
function whilst(test, iteratee, callback) {
callback = onlyOnce(callback || noop);
var _iteratee = wrapAsync(iteratee);
if (!test()) return callback(null);
var next = function(err/*, ...args*/) {
if (err) return callback(err);
if (test()) return _iteratee(next);
var args = slice(arguments, 1);
callback.apply(null, [null].concat(args));
};
_iteratee(next);
}
/**
* Repeatedly call `iteratee` until `test` returns `true`. Calls `callback` when
* stopped, or an error occurs. `callback` will be passed an error and any
* arguments passed to the final `iteratee`'s callback.
*
* The inverse of [whilst]{@link module:ControlFlow.whilst}.
*
* @name until
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.whilst]{@link module:ControlFlow.whilst}
* @category Control Flow
* @param {Function} test - synchronous truth test to perform before each
* execution of `iteratee`. Invoked with ().
* @param {AsyncFunction} iteratee - An async function which is called each time
* `test` fails. Invoked with (callback).
* @param {Function} [callback] - A callback which is called after the test
* function has passed and repeated execution of `iteratee` has stopped. `callback`
* will be passed an error and any arguments passed to the final `iteratee`'s
* callback. Invoked with (err, [results]);
*/
function until(test, iteratee, callback) {
whilst(function() {
return !test.apply(this, arguments);
}, iteratee, callback);
}
/**
* Runs the `tasks` array of functions in series, each passing their results to
* the next in the array. However, if any of the `tasks` pass an error to their
* own callback, the next function is not executed, and the main `callback` is
* immediately called with the error.
*
* @name waterfall
* @static
* @memberOf module:ControlFlow
* @method
* @category Control Flow
* @param {Array} tasks - An array of [async functions]{@link AsyncFunction}
* to run.
* Each function should complete with any number of `result` values.
* The `result` values will be passed as arguments, in order, to the next task.
* @param {Function} [callback] - An optional callback to run once all the
* functions have completed. This will be passed the results of the last task's
* callback. Invoked with (err, [results]).
* @returns undefined
* @example
*
* async.waterfall([
* function(callback) {
* callback(null, 'one', 'two');
* },
* function(arg1, arg2, callback) {
* // arg1 now equals 'one' and arg2 now equals 'two'
* callback(null, 'three');
* },
* function(arg1, callback) {
* // arg1 now equals 'three'
* callback(null, 'done');
* }
* ], function (err, result) {
* // result now equals 'done'
* });
*
* // Or, with named functions:
* async.waterfall([
* myFirstFunction,
* mySecondFunction,
* myLastFunction,
* ], function (err, result) {
* // result now equals 'done'
* });
* function myFirstFunction(callback) {
* callback(null, 'one', 'two');
* }
* function mySecondFunction(arg1, arg2, callback) {
* // arg1 now equals 'one' and arg2 now equals 'two'
* callback(null, 'three');
* }
* function myLastFunction(arg1, callback) {
* // arg1 now equals 'three'
* callback(null, 'done');
* }
*/
var waterfall = function(tasks, callback) {
callback = once(callback || noop);
if (!isArray(tasks)) return callback(new Error('First argument to waterfall must be an array of functions'));
if (!tasks.length) return callback();
var taskIndex = 0;
function nextTask(args) {
var task = wrapAsync(tasks[taskIndex++]);
args.push(onlyOnce(next));
task.apply(null, args);
}
function next(err/*, ...args*/) {
if (err || taskIndex === tasks.length) {
return callback.apply(null, arguments);
}
nextTask(slice(arguments, 1));
}
nextTask([]);
};
/**
* An "async function" in the context of Async is an asynchronous function with
* a variable number of parameters, with the final parameter being a callback.
* (`function (arg1, arg2, ..., callback) {}`)
* The final callback is of the form `callback(err, results...)`, which must be
* called once the function is completed. The callback should be called with a
* Error as its first argument to signal that an error occurred.
* Otherwise, if no error occurred, it should be called with `null` as the first
* argument, and any additional `result` arguments that may apply, to signal
* successful completion.
* The callback must be called exactly once, ideally on a later tick of the
* JavaScript event loop.
*
* This type of function is also referred to as a "Node-style async function",
* or a "continuation passing-style function" (CPS). Most of the methods of this
* library are themselves CPS/Node-style async functions, or functions that
* return CPS/Node-style async functions.
*
* Wherever we accept a Node-style async function, we also directly accept an
* [ES2017 `async` function]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function}.
* In this case, the `async` function will not be passed a final callback
* argument, and any thrown error will be used as the `err` argument of the
* implicit callback, and the return value will be used as the `result` value.
* (i.e. a `rejected` of the returned Promise becomes the `err` callback
* argument, and a `resolved` value becomes the `result`.)
*
* Note, due to JavaScript limitations, we can only detect native `async`
* functions and not transpilied implementations.
* Your environment must have `async`/`await` support for this to work.
* (e.g. Node > v7.6, or a recent version of a modern browser).
* If you are using `async` functions through a transpiler (e.g. Babel), you
* must still wrap the function with [asyncify]{@link module:Utils.asyncify},
* because the `async function` will be compiled to an ordinary function that
* returns a promise.
*
* @typedef {Function} AsyncFunction
* @static
*/
/**
* Async is a utility module which provides straight-forward, powerful functions
* for working with asynchronous JavaScript. Although originally designed for
* use with [Node.js](http://nodejs.org) and installable via
* `npm install --save async`, it can also be used directly in the browser.
* @module async
* @see AsyncFunction
*/
/**
* A collection of `async` functions for manipulating collections, such as
* arrays and objects.
* @module Collections
*/
/**
* A collection of `async` functions for controlling the flow through a script.
* @module ControlFlow
*/
/**
* A collection of `async` utility functions.
* @module Utils
*/
var index = {
apply: apply,
applyEach: applyEach,
applyEachSeries: applyEachSeries,
asyncify: asyncify,
auto: auto,
autoInject: autoInject,
cargo: cargo,
compose: compose,
concat: concat,
concatLimit: concatLimit,
concatSeries: concatSeries,
constant: constant,
detect: detect,
detectLimit: detectLimit,
detectSeries: detectSeries,
dir: dir,
doDuring: doDuring,
doUntil: doUntil,
doWhilst: doWhilst,
during: during,
each: eachLimit,
eachLimit: eachLimit$1,
eachOf: eachOf,
eachOfLimit: eachOfLimit,
eachOfSeries: eachOfSeries,
eachSeries: eachSeries,
ensureAsync: ensureAsync,
every: every,
everyLimit: everyLimit,
everySeries: everySeries,
filter: filter,
filterLimit: filterLimit,
filterSeries: filterSeries,
forever: forever,
groupBy: groupBy,
groupByLimit: groupByLimit,
groupBySeries: groupBySeries,
log: log,
map: map,
mapLimit: mapLimit,
mapSeries: mapSeries,
mapValues: mapValues,
mapValuesLimit: mapValuesLimit,
mapValuesSeries: mapValuesSeries,
memoize: memoize,
nextTick: nextTick,
parallel: parallelLimit,
parallelLimit: parallelLimit$1,
priorityQueue: priorityQueue,
queue: queue$1,
race: race,
reduce: reduce,
reduceRight: reduceRight,
reflect: reflect,
reflectAll: reflectAll,
reject: reject,
rejectLimit: rejectLimit,
rejectSeries: rejectSeries,
retry: retry,
retryable: retryable,
seq: seq,
series: series,
setImmediate: setImmediate$1,
some: some,
someLimit: someLimit,
someSeries: someSeries,
sortBy: sortBy,
timeout: timeout,
times: times,
timesLimit: timeLimit,
timesSeries: timesSeries,
transform: transform,
tryEach: tryEach,
unmemoize: unmemoize,
until: until,
waterfall: waterfall,
whilst: whilst,
// aliases
all: every,
allLimit: everyLimit,
allSeries: everySeries,
any: some,
anyLimit: someLimit,
anySeries: someSeries,
find: detect,
findLimit: detectLimit,
findSeries: detectSeries,
forEach: eachLimit,
forEachSeries: eachSeries,
forEachLimit: eachLimit$1,
forEachOf: eachOf,
forEachOfSeries: eachOfSeries,
forEachOfLimit: eachOfLimit,
inject: reduce,
foldl: reduce,
foldr: reduceRight,
select: filter,
selectLimit: filterLimit,
selectSeries: filterSeries,
wrapSync: asyncify
};
exports['default'] = index;
exports.apply = apply;
exports.applyEach = applyEach;
exports.applyEachSeries = applyEachSeries;
exports.asyncify = asyncify;
exports.auto = auto;
exports.autoInject = autoInject;
exports.cargo = cargo;
exports.compose = compose;
exports.concat = concat;
exports.concatLimit = concatLimit;
exports.concatSeries = concatSeries;
exports.constant = constant;
exports.detect = detect;
exports.detectLimit = detectLimit;
exports.detectSeries = detectSeries;
exports.dir = dir;
exports.doDuring = doDuring;
exports.doUntil = doUntil;
exports.doWhilst = doWhilst;
exports.during = during;
exports.each = eachLimit;
exports.eachLimit = eachLimit$1;
exports.eachOf = eachOf;
exports.eachOfLimit = eachOfLimit;
exports.eachOfSeries = eachOfSeries;
exports.eachSeries = eachSeries;
exports.ensureAsync = ensureAsync;
exports.every = every;
exports.everyLimit = everyLimit;
exports.everySeries = everySeries;
exports.filter = filter;
exports.filterLimit = filterLimit;
exports.filterSeries = filterSeries;
exports.forever = forever;
exports.groupBy = groupBy;
exports.groupByLimit = groupByLimit;
exports.groupBySeries = groupBySeries;
exports.log = log;
exports.map = map;
exports.mapLimit = mapLimit;
exports.mapSeries = mapSeries;
exports.mapValues = mapValues;
exports.mapValuesLimit = mapValuesLimit;
exports.mapValuesSeries = mapValuesSeries;
exports.memoize = memoize;
exports.nextTick = nextTick;
exports.parallel = parallelLimit;
exports.parallelLimit = parallelLimit$1;
exports.priorityQueue = priorityQueue;
exports.queue = queue$1;
exports.race = race;
exports.reduce = reduce;
exports.reduceRight = reduceRight;
exports.reflect = reflect;
exports.reflectAll = reflectAll;
exports.reject = reject;
exports.rejectLimit = rejectLimit;
exports.rejectSeries = rejectSeries;
exports.retry = retry;
exports.retryable = retryable;
exports.seq = seq;
exports.series = series;
exports.setImmediate = setImmediate$1;
exports.some = some;
exports.someLimit = someLimit;
exports.someSeries = someSeries;
exports.sortBy = sortBy;
exports.timeout = timeout;
exports.times = times;
exports.timesLimit = timeLimit;
exports.timesSeries = timesSeries;
exports.transform = transform;
exports.tryEach = tryEach;
exports.unmemoize = unmemoize;
exports.until = until;
exports.waterfall = waterfall;
exports.whilst = whilst;
exports.all = every;
exports.allLimit = everyLimit;
exports.allSeries = everySeries;
exports.any = some;
exports.anyLimit = someLimit;
exports.anySeries = someSeries;
exports.find = detect;
exports.findLimit = detectLimit;
exports.findSeries = detectSeries;
exports.forEach = eachLimit;
exports.forEachSeries = eachSeries;
exports.forEachLimit = eachLimit$1;
exports.forEachOf = eachOf;
exports.forEachOfSeries = eachOfSeries;
exports.forEachOfLimit = eachOfLimit;
exports.inject = reduce;
exports.foldl = reduce;
exports.foldr = reduceRight;
exports.select = filter;
exports.selectLimit = filterLimit;
exports.selectSeries = filterSeries;
exports.wrapSync = asyncify;
Object.defineProperty(exports, '__esModule', { value: true });
})));
};
BundleModuleCode['nlp/nlp']=function (module,exports){
var nlp = Require('nlp/compromise');
nlp.extend(Require('nlp/compromise-adjectives'));
nlp.extend(Require('nlp/compromise-dates'));
nlp.extend(Require('nlp/compromise-numbers'));
nlp.extend(Require('nlp/compromise-sentences'));
var efrt = Require('nlp/efrt');
nlp.lexer = efrt.lexer;
nlp.pack = efrt.pack;
nlp.unpack = efrt.unpack;
nlp.version = '1.2.2';
module.exports=nlp;
};
BundleModuleCode['nlp/compromise']=function (module,exports){
/* compromise 13.5.0X004 MIT/blab */
/* https://github.com/spencermountain/compromise */
/* polyfills */
Object.addProperty(Array,'findIndex', function(callback) {
if (this === null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
} else if (typeof callback !== 'function') {
throw new TypeError('callback must be a function');
}
var list = Object(this);
// Makes sures is always has an positive integer as length.
var length = list.length >>> 0;
var thisArg = arguments[1];
for (var i = 0; i < length; i++) {
if ( callback.call(thisArg, list[i], i, list) ) {
return i;
}
}
return -1;
});
/* NLP module */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.nlp = factory());
}(this, (function () { 'use strict';
function similar_text (first, second, percent) {
// http://kevin.vanzonneveld.net
// + original by: Rafał Kukawski (http://blog.kukawski.pl)
// + bugfixed by: Chris McMacken
// + added percent parameter by: Markus Padourek (taken from http://www.kevinhq.com/2012/06/php-similartext-function-in-javascript_16.html)
// * example 1: similar_text('Hello World!', 'Hello phpjs!');
// * returns 1: 7
// * example 2: similar_text('Hello World!', null);
// * returns 2: 0
// * example 3: similar_text('Hello World!', null, 1);
// * returns 3: 58.33
if (first === null || second === null || typeof first === 'undefined' || typeof second === 'undefined') {
return 0;
}
first += '';
second += '';
var pos1 = 0,
pos2 = 0,
max = 0,
firstLength = first.length,
secondLength = second.length,
p, q, l, sum;
max = 0;
for (p = 0; p < firstLength; p++) {
for (q = 0; q < secondLength; q++) {
for (l = 0;
(p + l < firstLength) && (q + l < secondLength) && (first.charAt(p + l) === second.charAt(q + l)); l++);
if (l > max) {
max = l;
pos1 = p;
pos2 = q;
}
}
}
sum = max;
if (sum) {
if (pos1 && pos2) {
sum += similar_text(first.substr(0, pos2), second.substr(0, pos2));
}
if ((pos1 + max < firstLength) && (pos2 + max < secondLength)) {
sum += similar_text(first.substr(pos1 + max, firstLength - pos1 - max), second.substr(pos2 + max, secondLength - pos2 - max));
}
}
if (!percent) {
return sum;
} else {
return (sum * 200) / (firstLength + secondLength);
}
}
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArrayLimit(arr, i) {
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
//this is a not-well-thought-out way to reduce our dependence on `object===object` stuff
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.split(''); //generates a unique id for this term
function makeId(str) {
str = str || '_';
var text = str + '-';
for (var i = 0; i < 7; i++) {
text += chars[Math.floor(Math.random() * chars.length)];
}
return text;
}
var _id = makeId;
//a hugely-ignorant, and widely subjective transliteration of latin, cryllic, greek unicode characters to english ascii.
//approximate visual (not semantic or phonetic) relationship between unicode and ascii characters
//http://en.wikipedia.org/wiki/List_of_Unicode_characters
//https://docs.google.com/spreadsheet/ccc?key=0Ah46z755j7cVdFRDM1A2YVpwa1ZYWlpJM2pQZ003M0E
var compact = {
'!': '¡',
'?': '¿Ɂ',
'"': '“”"❝❞',
"'": '‘‛❛❜',
'-': '—–',
a: 'ªÀÁÂÃÄÅàáâãäåĀāĂ㥹ǍǎǞǟǠǡǺǻȀȁȂȃȦȧȺΆΑΔΛάαλАадѦѧӐӑӒӓƛɅæ',
b: 'ßþƀƁƂƃƄƅɃΒβϐϦБВЪЬвъьѢѣҌҍ',
c: '¢©ÇçĆćĈĉĊċČčƆƇƈȻȼͻͼͽϲϹϽϾСсєҀҁҪҫ',
d: 'ÐĎďĐđƉƊȡƋƌǷ',
e: 'ÈÉÊËèéêëĒēĔĕĖėĘęĚěƎƏƐǝȄȅȆȇȨȩɆɇΈΕΞΣέεξϱϵ϶ЀЁЕЭеѐёҼҽҾҿӖӗӘәӚӛӬӭ',
f: 'ƑƒϜϝӺӻҒғſ',
g: 'ĜĝĞğĠġĢģƓǤǥǦǧǴǵ',
h: 'ĤĥĦħƕǶȞȟΉΗЂЊЋНнђћҢңҤҥҺһӉӊ',
I: 'ÌÍÎÏ',
i: 'ìíîïĨĩĪīĬĭĮįİıƖƗȈȉȊȋΊΐΪίιϊІЇії',
j: 'ĴĵǰȷɈɉϳЈј',
k: 'ĶķĸƘƙǨǩΚκЌЖКжкќҚқҜҝҞҟҠҡ',
l: 'ĹĺĻļĽľĿŀŁłƚƪǀǏǐȴȽΙӀӏ',
m: 'ΜϺϻМмӍӎ',
n: 'ÑñŃńŅņŇňʼnŊŋƝƞǸǹȠȵΝΠήηϞЍИЙЛПийлпѝҊҋӅӆӢӣӤӥπ',
o: 'ÒÓÔÕÖØðòóôõöøŌōŎŏŐőƟƠơǑǒǪǫǬǭǾǿȌȍȎȏȪȫȬȭȮȯȰȱΌΘΟθοσόϕϘϙϬϭϴОФоѲѳӦӧӨөӪӫ',
p: 'ƤƿΡρϷϸϼРрҎҏÞ',
q: 'Ɋɋ',
r: 'ŔŕŖŗŘřƦȐȑȒȓɌɍЃГЯгяѓҐґ',
s: 'ŚśŜŝŞşŠšƧƨȘșȿЅѕ',
t: 'ŢţŤťŦŧƫƬƭƮȚțȶȾΓΤτϮТт',
u: 'µÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųƯưƱƲǓǔǕǖǗǘǙǚǛǜȔȕȖȗɄΰμυϋύ',
v: 'νѴѵѶѷ',
w: 'ŴŵƜωώϖϢϣШЩшщѡѿ',
x: '×ΧχϗϰХхҲҳӼӽӾӿ',
y: 'ÝýÿŶŷŸƳƴȲȳɎɏΎΥΫγψϒϓϔЎУучўѰѱҮүҰұӮӯӰӱӲӳ',
z: 'ŹźŻżŽžƩƵƶȤȥɀΖζ'
}; //decompress data into two hashes
var unicode = {};
Object.keys(compact).forEach(function (k) {
compact[k].split('').forEach(function (s) {
unicode[s] = k;
});
});
var killUnicode = function killUnicode(str) {
var chars = str.split('');
chars.forEach(function (s, i) {
if (unicode[s]) {
chars[i] = unicode[s];
}
});
return chars.join('');
};
var unicode_1 = killUnicode; // console.log(killUnicode('bjŏȒk—Ɏó'));
var periodAcronym = /([A-Z]\.)+[A-Z]?,?$/;
var oneLetterAcronym = /^[A-Z]\.,?$/;
var noPeriodAcronym = /[A-Z]{2,}('s|,)?$/;
var lowerCaseAcronym = /([a-z]\.){2,}[a-z]\.?$/;
var isAcronym = function isAcronym(str) {
//like N.D.A
if (periodAcronym.test(str) === true) {
return true;
} //like c.e.o
if (lowerCaseAcronym.test(str) === true) {
return true;
} //like 'F.'
if (oneLetterAcronym.test(str) === true) {
return true;
} //like NDA
if (noPeriodAcronym.test(str) === true) {
return true;
}
return false;
};
var isAcronym_1 = isAcronym;
var hasSlash = /[a-z\u00C0-\u00FF] ?\/ ?[a-z\u00C0-\u00FF]/;
/** some basic operations on a string to reduce noise */
var clean = function clean(str) {
str = str || '';
str = str.toLowerCase();
str = str.trim();
var original = str; //(very) rough ASCII transliteration - bjŏrk -> bjork
str = unicode_1(str); //rough handling of slashes - 'see/saw'
if (hasSlash.test(str) === true) {
str = str.replace(/\/.*/, '');
} //#tags, @mentions
str = str.replace(/^[#@]/, ''); //punctuation
str = str.replace(/[,;.!?]+$/, ''); // coerce single curly quotes
str = str.replace(/[\u0027\u0060\u00B4\u2018\u2019\u201A\u201B\u2032\u2035\u2039\u203A]+/g, "'"); // coerce double curly quotes
str = str.replace(/[\u0022\u00AB\u00BB\u201C\u201D\u201E\u201F\u2033\u2034\u2036\u2037\u2E42\u301D\u301E\u301F\uFF02]+/g, '"'); //coerce Unicode ellipses
str = str.replace(/\u2026/g, '...'); //en-dash
str = str.replace(/\u2013/g, '-'); //lookin'->looking (make it easier for conjugation)
str = str.replace(/([aeiou][ktrp])in$/, '$1ing'); //turn re-enactment to reenactment
if (/^(re|un)-?[^aeiou]./.test(str) === true) {
str = str.replace('-', '');
} //strip leading & trailing grammatical punctuation
if (/^[:;]/.test(str) === false) {
str = str.replace(/\.{3,}$/g, '');
str = str.replace(/[",\.!:;\?\)]+$/g, '');
str = str.replace(/^['"\(]+/g, '');
} //do this again..
str = str.trim(); //oh shucks,
if (str === '') {
str = original;
} //compact acronyms
if (isAcronym_1(str)) {
str = str.replace(/\./g, '');
} //nice-numbers
str = str.replace(/([0-9]),([0-9])/g, '$1$2');
return str;
};
var clean_1 = clean; // console.log(normalize('Dr. V Cooper'));
/** reduced is one step further than clean */
var reduced = function reduced(str) {
// remove apostrophes
str = str.replace(/[']s$/, '');
str = str.replace(/s[']$/, 's');
return str;
};
var reduce = reduced;
//all punctuation marks, from https://en.wikipedia.org/wiki/Punctuation
//we have slightly different rules for start/end - like #hashtags.
var startings = /^[ \n\t\.'\[\](){}⟨⟩:,،、‒–—―…!.‹›«»‐\-?;\/⁄·&*•^†‡°¡¿※№÷׺ª%‰+=‱¶′″‴§~|‖¦©℗®℠™¤₳฿\u0022|\uFF02|\u0027|\u201C|\u2018|\u201F|\u201B|\u201E|\u2E42|\u201A|\u00AB|\u2039|\u2035|\u2036|\u2037|\u301D|\u0060|\u301F]+/;
var endings = /[ \n\t\.'\[\](){}⟨⟩:,،、‒–—―…!.‹›«»‐\-?;\/⁄·&*@•^†‡°¡¿※#№÷׺ª‰+=‱¶′″‴§~|‖¦©℗®℠™¤₳฿\u0022|\uFF02|\u0027|\u201D|\u2019|\u201D|\u2019|\u201D|\u201D|\u2019|\u00BB|\u203A|\u2032|\u2033|\u2034|\u301E|\u00B4|\u301E]+$/; //money = ₵¢₡₢$₫₯֏₠€ƒ₣₲₴₭₺₾ℳ₥₦₧₱₰£៛₽₹₨₪৳₸₮₩¥
var hasSlash$1 = /\//;
var hasApostrophe = /[']/;
var hasAcronym = /^[a-z]\.([a-z]\.)+/i;
var minusNumber = /^[-+\.][0-9]/;
/** turn given text into a parsed-up object
* seperate the 'meat' of the word from the whitespace+punctuation
*/
var parseTerm = function parseTerm(str) {
var original = str;
var pre = '';
var post = '';
str = str.replace(startings, function (found) {
pre = found; // support '-40'
if ((pre === '-' || pre === '+' || pre === '.') && minusNumber.test(str)) {
pre = '';
return found;
}
return '';
});
str = str.replace(endings, function (found) {
post = found; // keep s-apostrophe - "flanders'" or "chillin'"
if (hasApostrophe.test(found) && /[sn][']$/.test(original) && hasApostrophe.test(pre) === false) {
post = post.replace(hasApostrophe, '');
return "'";
} //keep end-period in acronym
if (hasAcronym.test(str) === true) {
post = post.replace(/\./, '');
return '.';
}
return '';
}); //we went too far..
if (str === '') {
// do a very mild parse, and hope for the best.
original = original.replace(/ *$/, function (after) {
post = after || '';
return '';
});
str = original;
pre = '';
post = post;
} // create the various forms of our text,
var clean = clean_1(str);
var parsed = {
text: str,
clean: clean,
reduced: reduce(clean),
pre: pre,
post: post
}; // support aliases for slashes
if (hasSlash$1.test(str)) {
str.split(hasSlash$1).forEach(function (word) {
parsed.alias = parsed.alias || {};
parsed.alias[word.trim()] = true;
});
}
return parsed;
};
var parse = parseTerm;
function createCommonjsModule(fn, basedir, module) {
return module = {
path: basedir,
exports: {},
require: function (path, base) {
return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
}
}, fn(module, module.exports), module.exports;
}
function commonjsRequire () {
throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
}
var _01Case = createCommonjsModule(function (module, exports) {
var titleCase = /^[A-Z][a-z'\u00C0-\u00FF]/;
var upperCase = /^[A-Z]+s?$/;
/** convert all text to uppercase */
exports.toUpperCase = function () {
this.text = this.text.toUpperCase();
return this;
};
/** convert all text to lowercase */
exports.toLowerCase = function () {
this.text = this.text.toLowerCase();
return this;
};
/** only set the first letter to uppercase
* leave any existing uppercase alone
*/
exports.toTitleCase = function () {
this.text = this.text.replace(/^ *[a-z\u00C0-\u00FF]/, function (x) {
return x.toUpperCase();
}); //support unicode?
return this;
};
/** if all letters are uppercase */
exports.isUpperCase = function () {
return upperCase.test(this.text);
};
/** if the first letter is uppercase, and the rest are lowercase */
exports.isTitleCase = function () {
return titleCase.test(this.text);
};
exports.titleCase = exports.isTitleCase;
});
var _02Punctuation = createCommonjsModule(function (module, exports) {
// these methods are called with '@hasComma' in the match syntax
// various unicode quotation-mark formats
var startQuote = /(\u0022|\uFF02|\u0027|\u201C|\u2018|\u201F|\u201B|\u201E|\u2E42|\u201A|\u00AB|\u2039|\u2035|\u2036|\u2037|\u301D|\u0060|\u301F)/;
var endQuote = /(\u0022|\uFF02|\u0027|\u201D|\u2019|\u201D|\u2019|\u201D|\u201D|\u2019|\u00BB|\u203A|\u2032|\u2033|\u2034|\u301E|\u00B4|\u301E)/;
/** search the term's 'post' punctuation */
exports.hasPost = function (punct) {
return this.post.indexOf(punct) !== -1;
};
/** search the term's 'pre' punctuation */
exports.hasPre = function (punct) {
return this.pre.indexOf(punct) !== -1;
};
/** does it have a quotation symbol? */
exports.hasQuote = function () {
return startQuote.test(this.pre) || endQuote.test(this.post);
};
exports.hasQuotation = exports.hasQuote;
/** does it have a comma? */
exports.hasComma = function () {
return this.hasPost(',');
};
/** does it end in a period? */
exports.hasPeriod = function () {
return this.hasPost('.') === true && this.hasPost('...') === false;
};
/** does it end in an exclamation */
exports.hasExclamation = function () {
return this.hasPost('!');
};
/** does it end with a question mark? */
exports.hasQuestionMark = function () {
return this.hasPost('?') || this.hasPost('¿');
};
/** is there a ... at the end? */
exports.hasEllipses = function () {
return this.hasPost('..') || this.hasPost('…') || this.hasPre('..') || this.hasPre('…');
};
/** is there a semicolon after this word? */
exports.hasSemicolon = function () {
return this.hasPost(';');
};
/** is there a slash '/' in this word? */
exports.hasSlash = function () {
var slash = /\//;
return slash.test(this.text);
};
/** a hyphen connects two words like-this */
exports.hasHyphen = function () {
var hyphen = /(-||—)/;
return hyphen.test(this.post) || hyphen.test(this.pre);
};
/** a dash separates words - like that */
exports.hasDash = function () {
var hyphen = / (-||—) /;
return hyphen.test(this.post) || hyphen.test(this.pre);
};
/** is it multiple words combinded */
exports.hasContraction = function () {
return Boolean(this.implicit);
};
/** try to sensibly put this punctuation mark into the term */
exports.addPunctuation = function (punct) {
// dont add doubles
if (punct === ',' || punct === ';') {
this.post = this.post.replace(punct, '');
}
this.post = punct + this.post;
return this;
};
});
//declare it up here
var wrapMatch = function wrapMatch() {};
/** ignore optional/greedy logic, straight-up term match*/
var doesMatch = function doesMatch(t, reg, index, length) {
// support id matches
if (reg.id === t.id) {
return true;
} // support '.'
if (reg.anything === true) {
return true;
} // support '^' (in parentheses)
if (reg.start === true && index !== 0) {
return false;
} // support '$' (in parentheses)
if (reg.end === true && index !== length - 1) {
return false;
} //support a text match
if (reg.word !== undefined) {
//match contractions
if (t.implicit !== null && t.implicit === reg.word) {
return true;
} // term aliases for slashes and things
if (t.alias !== undefined && t.alias.hasOwnProperty(reg.word)) {
return true;
} // support ~ match
if (reg.soft === true && reg.word === t.root) {
return true;
} //match either .clean or .text
return reg.word === t.clean || reg.word === t.text || reg.word === t.reduced;
} //support #Tag
if (reg.tag !== undefined) {
return t.tags[reg.tag] === true;
} //support @method
if (reg.method !== undefined) {
if (typeof t[reg.method] === 'function' && t[reg.method]() === true) {
return true;
}
return false;
} //support /reg/
if (reg.regex !== undefined) {
return reg.regex.test(t.clean);
} // support optimized (one|two)
if (reg.oneOf !== undefined) {
return reg.oneOf.hasOwnProperty(t.reduced) || reg.oneOf.hasOwnProperty(t.text);
} //support (one|two)
if (reg.choices !== undefined) {
// try to support && operator
if (reg.operator === 'and') {
// must match them all
return reg.choices.every(function (r) {
return wrapMatch(t, r, index, length);
});
} // or must match one
return reg.choices.some(function (r) {
return wrapMatch(t, r, index, length);
});
}
return false;
}; // wrap result for !negative match logic
wrapMatch = function wrapMatch(t, reg, index, length) {
var result = doesMatch(t, reg, index, length);
if (reg.negative === true) {
return !result;
}
return result;
};
var _doesMatch = wrapMatch;
var boring = {};
/** check a match object against this term */
var doesMatch_1 = function doesMatch_1(reg, index, length) {
return _doesMatch(this, reg, index, length);
};
/** does this term look like an acronym? */
var isAcronym_1$1 = function isAcronym_1$1() {
return isAcronym_1(this.text);
};
/** is this term implied by a contraction? */
var isImplicit = function isImplicit() {
return this.text === '' && Boolean(this.implicit);
};
/** does the term have at least one good tag? */
var isKnown = function isKnown() {
return Object.keys(this.tags).some(function (t) {
return boring[t] !== true;
});
};
/** cache the root property of the term */
var setRoot = function setRoot(world) {
var transform = world.transforms;
var str = this.implicit || this.clean;
if (this.tags.Plural) {
str = transform.toSingular(str, world);
}
if (this.tags.Verb && !this.tags.Negative && !this.tags.Infinitive) {
var tense = null;
if (this.tags.PastTense) {
tense = 'PastTense';
} else if (this.tags.Gerund) {
tense = 'Gerund';
} else if (this.tags.PresentTense) {
tense = 'PresentTense';
} else if (this.tags.Participle) {
tense = 'Participle';
} else if (this.tags.Actor) {
tense = 'Actor';
}
str = transform.toInfinitive(str, world, tense);
}
this.root = str;
};
var _03Misc = {
doesMatch: doesMatch_1,
isAcronym: isAcronym_1$1,
isImplicit: isImplicit,
isKnown: isKnown,
setRoot: setRoot
};
var hasSpace = /[\s-]/;
var isUpperCase = /^[A-Z-]+$/; // const titleCase = str => {
// return str.charAt(0).toUpperCase() + str.substr(1)
// }
/** return various text formats of this term */
var textOut = function textOut(options, showPre, showPost) {
options = options || {};
var word = this.text;
var before = this.pre;
var after = this.post; // -word-
if (options.reduced === true) {
word = this.reduced || '';
}
if (options.root === true) {
word = this.root || '';
}
if (options.implicit === true && this.implicit) {
word = this.implicit || '';
}
if (options.normal === true) {
word = this.clean || this.text || '';
}
if (options.root === true) {
word = this.root || this.reduced || '';
}
if (options.unicode === true) {
word = unicode_1(word);
} // cleanup case
if (options.titlecase === true) {
if (this.tags.ProperNoun && !this.titleCase()) ; else if (this.tags.Acronym) {
word = word.toUpperCase(); //uppercase acronyms
} else if (isUpperCase.test(word) && !this.tags.Acronym) {
// lowercase everything else
word = word.toLowerCase();
}
}
if (options.lowercase === true) {
word = word.toLowerCase();
} // remove the '.'s from 'F.B.I.' (safely)
if (options.acronyms === true && this.tags.Acronym) {
word = word.replace(/\./g, '');
} // -before/after-
if (options.whitespace === true || options.root === true) {
before = '';
after = ' ';
if ((hasSpace.test(this.post) === false || options.last) && !this.implicit) {
after = '';
}
}
if (options.punctuation === true && !options.root) {
//normalized end punctuation
if (this.hasPost('.') === true) {
after = '.' + after;
} else if (this.hasPost('?') === true) {
after = '?' + after;
} else if (this.hasPost('!') === true) {
after = '!' + after;
} else if (this.hasPost(',') === true) {
after = ',' + after;
} else if (this.hasEllipses() === true) {
after = '...' + after;
}
}
if (showPre !== true) {
before = '';
}
if (showPost !== true) {
// let keep = after.match(/\)/) || ''
after = ''; //keep //after.replace(/[ .?!,]+/, '')
} // remove the '.' from 'Mrs.' (safely)
if (options.abbreviations === true && this.tags.Abbreviation) {
after = after.replace(/^\./, '');
}
return before + word + after;
};
var _04Text = {
textOut: textOut
};
var boringTags = {
Auxiliary: 1,
Possessive: 1
};
/** a subjective ranking of tags kinda tfidf-based */
var rankTags = function rankTags(term, world) {
var tags = Object.keys(term.tags);
var tagSet = world.tags;
tags = tags.sort(function (a, b) {
//bury the tags we dont want
if (boringTags[b] || !tagSet[b]) {
return -1;
} // unknown tags are interesting
if (!tagSet[b]) {
return 1;
}
if (!tagSet[a]) {
return 0;
} // then sort by #of parent tags (most-specific tags first)
if (tagSet[a].lineage.length > tagSet[b].lineage.length) {
return 1;
}
if (tagSet[a].isA.length > tagSet[b].isA.length) {
return -1;
}
return 0;
});
return tags;
};
var _bestTag = rankTags;
var jsonDefault = {
text: true,
tags: true,
implicit: true,
whitespace: true,
clean: false,
id: false,
index: false,
offset: false,
bestTag: false
};
/** return various metadata for this term */
var json = function json(options, world) {
options = options || {};
options = Object.assign({}, jsonDefault, options);
var result = {}; // default on
if (options.text) {
result.text = this.text;
}
if (options.normal) {
result.normal = this.normal;
}
if (options.tags) {
result.tags = Object.keys(this.tags);
} // default off
if (options.clean) {
result.clean = this.clean;
}
if (options.id || options.offset) {
result.id = this.id;
}
if (options.implicit && this.implicit !== null) {
result.implicit = this.implicit;
}
if (options.whitespace) {
result.pre = this.pre;
result.post = this.post;
}
if (options.bestTag) {
result.bestTag = _bestTag(this, world)[0];
}
return result;
};
var _05Json = {
json: json
};
var methods = Object.assign({}, _01Case, _02Punctuation, _03Misc, _04Text, _05Json);
function isClientSide() {
return typeof window !== 'undefined' && window.document;
}
/** add spaces at the end */
var padEnd = function padEnd(str, width) {
str = str.toString();
while (str.length < width) {
str += ' ';
}
return str;
};
/** output for verbose-mode */
var logTag = function logTag(t, tag, reason) {
if (isClientSide()) {
console.log('%c' + padEnd(t.clean, 3) + ' + ' + tag + ' ', 'color: #6accb2;');
return;
} //server-side
var log = '\x1b[33m' + padEnd(t.clean, 15) + '\x1b[0m + \x1b[32m' + tag + '\x1b[0m ';
if (reason) {
log = padEnd(log, 35) + ' ' + reason + '';
}
console.log(log);
};
/** output for verbose mode */
var logUntag = function logUntag(t, tag, reason) {
if (isClientSide()) {
console.log('%c' + padEnd(t.clean, 3) + ' - ' + tag + ' ', 'color: #AB5850;');
return;
} //server-side
var log = '\x1b[33m' + padEnd(t.clean, 3) + ' \x1b[31m - #' + tag + '\x1b[0m ';
if (reason) {
log = padEnd(log, 35) + ' ' + reason;
}
console.log(log);
};
var isArray = function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
};
var titleCase = function titleCase(str) {
return str.charAt(0).toUpperCase() + str.substr(1);
};
var fns = {
logTag: logTag,
logUntag: logUntag,
isArray: isArray,
titleCase: titleCase
};
/** add a tag, and its descendents, to a term */
var addTag = function addTag(t, tag, reason, world) {
var tagset = world.tags; //support '.' or '-' notation for skipping the tag
if (tag === '' || tag === '.' || tag === '-') {
return;
}
if (tag[0] === '#') {
tag = tag.replace(/^#/, '');
}
tag = fns.titleCase(tag); //if we already got this one
if (t.tags[tag] === true) {
return;
} // log it?
var isVerbose = world.isVerbose();
if (isVerbose === true) {
fns.logTag(t, tag, reason);
} //add tag
t.tags[tag] = true; //whee!
//check tagset for any additional things to do...
if (tagset.hasOwnProperty(tag) === true) {
//add parent Tags
tagset[tag].isA.forEach(function (down) {
t.tags[down] = true;
if (isVerbose === true) {
fns.logTag(t, '→ ' + down);
}
}); //remove any contrary tags
t.unTag(tagset[tag].notA, '←', world);
}
};
/** support an array of tags */
var addTags = function addTags(term, tags, reason, world) {
if (typeof tags !== 'string') {
for (var i = 0; i < tags.length; i++) {
addTag(term, tags[i], reason, world);
} // tags.forEach(tag => addTag(term, tag, reason, world))
} else {
addTag(term, tags, reason, world);
}
};
var add = addTags;
var lowerCase = /^[a-z]/;
var titleCase$1 = function titleCase(str) {
return str.charAt(0).toUpperCase() + str.substr(1);
};
/** remove this tag, and its descentents from the term */
var unTag = function unTag(t, tag, reason, world) {
var isVerbose = world.isVerbose(); //support '*' for removing all tags
if (tag === '*') {
t.tags = {};
return t;
}
tag = tag.replace(/^#/, '');
if (lowerCase.test(tag) === true) {
tag = titleCase$1(tag);
} // remove the tag
if (t.tags[tag] === true) {
delete t.tags[tag]; //log in verbose-mode
if (isVerbose === true) {
fns.logUntag(t, tag, reason);
}
} //delete downstream tags too
var tagset = world.tags;
if (tagset[tag]) {
var lineage = tagset[tag].lineage;
for (var i = 0; i < lineage.length; i++) {
if (t.tags[lineage[i]] === true) {
delete t.tags[lineage[i]];
if (isVerbose === true) {
fns.logUntag(t, ' - ' + lineage[i]);
}
}
}
}
return t;
}; //handle an array of tags
var untagAll = function untagAll(term, tags, reason, world) {
if (typeof tags !== 'string' && tags) {
for (var i = 0; i < tags.length; i++) {
unTag(term, tags[i], reason, world);
}
return;
}
unTag(term, tags, reason, world);
};
var unTag_1 = untagAll;
var canBe = function canBe(term, tag, world) {
var tagset = world.tags; // cleanup tag
if (tag[0] === '#') {
tag = tag.replace(/^#/, '');
} //fail-fast
if (tagset[tag] === undefined) {
return true;
} //loop through tag's contradictory tags
var enemies = tagset[tag].notA || [];
for (var i = 0; i < enemies.length; i++) {
if (term.tags[enemies[i]] === true) {
return false;
}
}
if (tagset[tag].isA !== undefined) {
return canBe(term, tagset[tag].isA, world); //recursive
}
return true;
};
var canBe_1 = canBe;
/** add a tag or tags, and their descendents to this term
* @param {string | string[]} tags - a tag or tags
* @param {string?} [reason] a clue for debugging
*/
var tag_1 = function tag_1(tags, reason, world) {
add(this, tags, reason, world);
return this;
};
/** only tag this term if it's consistent with it's current tags */
var tagSafe = function tagSafe(tags, reason, world) {
if (canBe_1(this, tags, world)) {
add(this, tags, reason, world);
}
return this;
};
/** remove a tag or tags, and their descendents from this term
* @param {string | string[]} tags - a tag or tags
* @param {string?} [reason] a clue for debugging
*/
var unTag_1$1 = function unTag_1$1(tags, reason, world) {
unTag_1(this, tags, reason, world);
return this;
};
/** is this tag consistent with the word's current tags?
* @param {string | string[]} tags - a tag or tags
* @returns {boolean}
*/
var canBe_1$1 = function canBe_1$1(tags, world) {
return canBe_1(this, tags, world);
};
var tag = {
tag: tag_1,
tagSafe: tagSafe,
unTag: unTag_1$1,
canBe: canBe_1$1
};
var Term = /*#__PURE__*/function () {
function Term() {
var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
_classCallCheck(this, Term);
text = String(text);
var obj = parse(text); // the various forms of our text
this.text = obj.text || '';
this.clean = obj.clean;
this.reduced = obj.reduced;
this.root = null;
this.implicit = null;
this.pre = obj.pre || '';
this.post = obj.post || '';
this.tags = {};
this.prev = null;
this.next = null;
this.id = _id(obj.clean);
this.isA = 'Term'; // easier than .constructor...
// support alternative matches
if (obj.alias) {
this.alias = obj.alias;
}
}
/** set the text of the Term to something else*/
_createClass(Term, [{
key: "set",
value: function set(str) {
var obj = parse(str);
this.text = obj.text;
this.clean = obj.clean;
return this;
}
}]);
return Term;
}();
/** create a deep-copy of this term */
Term.prototype.clone = function () {
var term = new Term(this.text);
term.pre = this.pre;
term.post = this.post;
term.clean = this.clean;
term.reduced = this.reduced;
term.root = this.root;
term.implicit = this.implicit;
term.tags = Object.assign({}, this.tags); //use the old id, so it can be matched with .match(doc)
// term.id = this.id
return term;
};
Object.assign(Term.prototype, methods);
Object.assign(Term.prototype, tag);
var Term_1 = Term;
/** return a flat array of Term objects */
var terms = function terms(n) {
if (this.length === 0) {
return [];
} // use cache, if it exists
if (this.cache.terms) {
if (n !== undefined) {
return this.cache.terms[n];
}
return this.cache.terms;
}
var terms = [this.pool.get(this.start)];
for (var i = 0; i < this.length - 1; i += 1) {
var id = terms[terms.length - 1].next;
if (id === null) {
// throw new Error('linked-list broken')
console.error("Compromise error: Linked list broken in phrase '" + this.start + "'");
break;
}
var term = this.pool.get(id);
terms.push(term); //return this one?
if (n !== undefined && n === i) {
return terms[n];
}
}
if (n === undefined) {
this.cache.terms = terms;
}
if (n !== undefined) {
return terms[n];
}
return terms;
};
/** return a shallow or deep copy of this phrase */
var clone = function clone(isShallow) {
var _this = this;
if (isShallow) {
var p = this.buildFrom(this.start, this.length);
p.cache = this.cache;
return p;
} //how do we clone part of the pool?
var terms = this.terms();
var newTerms = terms.map(function (t) {
return t.clone();
}); // console.log(newTerms)
//connect these new ids up
newTerms.forEach(function (t, i) {
//add it to the pool..
_this.pool.add(t);
if (newTerms[i + 1]) {
t.next = newTerms[i + 1].id;
}
if (newTerms[i - 1]) {
t.prev = newTerms[i - 1].id;
}
});
return this.buildFrom(newTerms[0].id, newTerms.length);
};
/** return last term object */
var lastTerm = function lastTerm() {
var terms = this.terms();
return terms[terms.length - 1];
};
/** quick lookup for a term id */
var hasId = function hasId(wantId) {
if (this.length === 0 || !wantId) {
return false;
}
if (this.start === wantId) {
return true;
} // use cache, if available
if (this.cache.terms) {
var _terms = this.cache.terms;
for (var i = 0; i < _terms.length; i++) {
if (_terms[i].id === wantId) {
return true;
}
}
return false;
} // otherwise, go through each term
var lastId = this.start;
for (var _i = 0; _i < this.length - 1; _i += 1) {
var term = this.pool.get(lastId);
if (term === undefined) {
console.error("Compromise error: Linked list broken. Missing term '".concat(lastId, "' in phrase '").concat(this.start, "'\n")); // throw new Error('linked List error')
return false;
}
if (term.next === wantId) {
return true;
}
lastId = term.next;
}
return false;
};
/** how many seperate, non-empty words is it? */
var wordCount = function wordCount() {
return this.terms().filter(function (t) {
return t.text !== '';
}).length;
};
/** get the full-sentence this phrase belongs to */
var fullSentence = function fullSentence() {
var t = this.terms(0); //find first term in sentence
while (t.prev) {
t = this.pool.get(t.prev);
}
var start = t.id;
var len = 1; //go to end of sentence
while (t.next) {
t = this.pool.get(t.next);
len += 1;
}
return this.buildFrom(start, len);
};
var _01Utils = {
terms: terms,
clone: clone,
lastTerm: lastTerm,
hasId: hasId,
wordCount: wordCount,
fullSentence: fullSentence
};
var trimEnd = function trimEnd(str) {
return str.replace(/ +$/, '');
};
/** produce output in the given format */
var text = function text() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var isFirst = arguments.length > 1 ? arguments[1] : undefined;
var isLast = arguments.length > 2 ? arguments[2] : undefined;
if (typeof options === 'string') {
if (options === 'normal') {
options = {
whitespace: true,
unicode: true,
lowercase: true,
punctuation: true,
acronyms: true,
abbreviations: true,
implicit: true,
normal: true
};
} else if (options === 'clean') {
options = {
titlecase: false,
lowercase: true,
punctuation: true,
whitespace: true,
unicode: true,
implicit: true
};
} else if (options === 'reduced') {
options = {
titlecase: false,
lowercase: true,
punctuation: false,
//FIXME: reversed?
whitespace: true,
unicode: true,
implicit: true,
reduced: true
};
} else if (options === 'root') {
options = {
titlecase: false,
lowercase: true,
punctuation: true,
whitespace: true,
unicode: true,
implicit: true,
root: true
};
} else {
options = {};
}
}
var terms = this.terms(); //this this phrase a complete sentence?
var isFull = false;
if (terms[0] && terms[0].prev === null && terms[terms.length - 1].next === null) {
isFull = true;
}
var text = terms.reduce(function (str, t, i) {
options.last = isLast && i === terms.length - 1;
var showPre = true;
var showPost = true;
if (isFull === false) {
// dont show beginning whitespace
if (i === 0 && isFirst) {
showPre = false;
} // dont show end-whitespace
if (i === terms.length - 1 && isLast) {
showPost = false;
}
}
var txt = t.textOut(options, showPre, showPost); // if (options.titlecase && i === 0) {
// txt = titleCase(txt)
// }
return str + txt;
}, ''); //full-phrases show punctuation, but not whitespace
if (isFull === true && isLast) {
text = trimEnd(text);
}
if (options.trim === true) {
text = text.trim();
}
return text;
};
var _02Text = {
text: text
};
/** remove start and end whitespace */
var trim = function trim() {
var terms = this.terms();
if (terms.length > 0) {
//trim starting
terms[0].pre = terms[0].pre.replace(/^\s+/, ''); //trim ending
var lastTerm = terms[terms.length - 1];
lastTerm.post = lastTerm.post.replace(/\s+$/, '');
}
return this;
};
var _03Change = {
trim: trim
};
var endOfSentence = /[.?!]\s*$/; // replacing a 'word.' with a 'word!'
var combinePost = function combinePost(before, after) {
//only transfer the whitespace
if (endOfSentence.test(after)) {
var whitespace = before.match(/\s*$/);
return after + whitespace;
}
return before;
}; //add whitespace to the start of the second bit
var addWhitespace = function addWhitespace(beforeTerms, newTerms) {
// add any existing pre-whitespace to beginning
newTerms[0].pre = beforeTerms[0].pre;
var lastTerm = beforeTerms[beforeTerms.length - 1]; //add any existing punctuation to end of our new terms
var newTerm = newTerms[newTerms.length - 1];
newTerm.post = combinePost(lastTerm.post, newTerm.post); // remove existing punctuation
lastTerm.post = ''; //before ←[space] - after
if (lastTerm.post === '') {
lastTerm.post += ' ';
}
}; //insert this segment into the linked-list
var stitchIn = function stitchIn(beforeTerms, newTerms, pool) {
var lastBefore = beforeTerms[beforeTerms.length - 1];
var lastNew = newTerms[newTerms.length - 1];
var afterId = lastBefore.next; //connect ours in (main → newPhrase)
lastBefore.next = newTerms[0].id; //stich the end in (newPhrase → after)
lastNew.next = afterId; //do it backwards, too
if (afterId) {
// newPhrase ← after
var afterTerm = pool.get(afterId);
afterTerm.prev = lastNew.id;
} // before ← newPhrase
var beforeId = beforeTerms[0].id;
if (beforeId) {
var newTerm = newTerms[0];
newTerm.prev = beforeId;
}
}; // avoid stretching a phrase twice.
var unique = function unique(list) {
return list.filter(function (o, i) {
return list.indexOf(o) === i;
});
}; //append one phrase onto another.
var appendPhrase = function appendPhrase(before, newPhrase, doc) {
var beforeTerms = before.terms();
var newTerms = newPhrase.terms(); //spruce-up the whitespace issues
addWhitespace(beforeTerms, newTerms); //insert this segment into the linked-list
stitchIn(beforeTerms, newTerms, before.pool); // stretch!
// make each effected phrase longer
var toStretch = [before];
var hasId = before.start;
var docs = [doc];
docs = docs.concat(doc.parents()); // find them all!
docs.forEach(function (parent) {
// only the phrases that should change
var shouldChange = parent.list.filter(function (p) {
return p.hasId(hasId);
});
toStretch = toStretch.concat(shouldChange);
}); // don't double-count a phrase
toStretch = unique(toStretch);
toStretch.forEach(function (p) {
p.length += newPhrase.length;
});
before.cache = {};
return before;
};
var append = appendPhrase;
var hasSpace$1 = / /; //a new space needs to be added, either on the new phrase, or the old one
// '[new] [◻old]' -or- '[old] [◻new] [old]'
var addWhitespace$1 = function addWhitespace(newTerms) {
//add a space before our new text?
// add a space after our text
var lastTerm = newTerms[newTerms.length - 1];
if (hasSpace$1.test(lastTerm.post) === false) {
lastTerm.post += ' ';
}
return;
}; //insert this segment into the linked-list
var stitchIn$1 = function stitchIn(main, newPhrase, newTerms) {
// [newPhrase] → [main]
var lastTerm = newTerms[newTerms.length - 1];
lastTerm.next = main.start; // [before] → [main]
var pool = main.pool;
var start = pool.get(main.start);
if (start.prev) {
var before = pool.get(start.prev);
before.next = newPhrase.start;
} //do it backwards, too
// before ← newPhrase
newTerms[0].prev = main.terms(0).prev; // newPhrase ← main
main.terms(0).prev = lastTerm.id;
};
var unique$1 = function unique(list) {
return list.filter(function (o, i) {
return list.indexOf(o) === i;
});
}; //append one phrase onto another
var joinPhrase = function joinPhrase(original, newPhrase, doc) {
var starterId = original.start;
var newTerms = newPhrase.terms(); //spruce-up the whitespace issues
addWhitespace$1(newTerms); //insert this segment into the linked-list
stitchIn$1(original, newPhrase, newTerms); //increase the length of our phrases
var toStretch = [original];
var docs = [doc];
docs = docs.concat(doc.parents());
docs.forEach(function (d) {
// only the phrases that should change
var shouldChange = d.list.filter(function (p) {
return p.hasId(starterId) || p.hasId(newPhrase.start);
});
toStretch = toStretch.concat(shouldChange);
}); // don't double-count
toStretch = unique$1(toStretch); // stretch these phrases
toStretch.forEach(function (p) {
p.length += newPhrase.length; // change the start too, if necessary
if (p.start === starterId) {
p.start = newPhrase.start;
}
p.cache = {};
});
return original;
};
var prepend = joinPhrase;
//recursively decrease the length of all the parent phrases
var shrinkAll = function shrinkAll(doc, id, deleteLength, after) {
var arr = doc.parents();
arr.push(doc);
arr.forEach(function (d) {
//find our phrase to shrink
var phrase = d.list.find(function (p) {
return p.hasId(id);
});
if (!phrase) {
return;
}
phrase.length -= deleteLength; // does it start with this soon-removed word?
if (phrase.start === id) {
phrase.start = after.id;
}
phrase.cache = {};
}); // cleanup empty phrase objects
doc.list = doc.list.filter(function (p) {
if (!p.start || !p.length) {
return false;
}
return true;
});
};
/** wrap the linked-list around these terms
* so they don't appear any more
*/
var deletePhrase = function deletePhrase(phrase, doc) {
var pool = doc.pool();
var terms = phrase.terms(); //grab both sides of the chain,
var prev = pool.get(terms[0].prev) || {};
var after = pool.get(terms[terms.length - 1].next) || {};
if (terms[0].implicit && prev.implicit) {
prev.set(prev.implicit);
prev.post += ' ';
} // //first, change phrase lengths
shrinkAll(doc, phrase.start, phrase.length, after); // connect [prev]->[after]
if (prev) {
prev.next = after.id;
} // connect [prev]<-[after]
if (after) {
after.prev = prev.id;
} // lastly, actually delete the terms from the pool?
// for (let i = 0; i < terms.length; i++) {
// pool.remove(terms[i].id)
// }
};
var _delete = deletePhrase;
/** put this text at the end */
var append_1 = function append_1(newPhrase, doc) {
append(this, newPhrase, doc);
return this;
};
/** add this text to the beginning */
var prepend_1 = function prepend_1(newPhrase, doc) {
prepend(this, newPhrase, doc);
return this;
};
var _delete$1 = function _delete$1(doc) {
_delete(this, doc);
return this;
}; // stich-in newPhrase, stretch 'doc' + parents
var replace = function replace(newPhrase, doc) {
//add it do the end
var firstLength = this.length;
append(this, newPhrase, doc); //delete original terms
var tmp = this.buildFrom(this.start, this.length);
tmp.length = firstLength;
_delete(tmp, doc);
};
/**
* Turn this phrase object into 3 phrase objects
*/
var splitOn = function splitOn(p) {
var terms = this.terms();
var result = {
before: null,
match: null,
after: null
};
var index = terms.findIndex(function (t) {
return t.id === p.start;
});
if (index === -1) {
return result;
} //make all three sections into phrase-objects
var start = terms.slice(0, index);
if (start.length > 0) {
result.before = this.buildFrom(start[0].id, start.length);
}
var match = terms.slice(index, index + p.length);
if (match.length > 0) {
result.match = this.buildFrom(match[0].id, match.length);
}
var end = terms.slice(index + p.length, terms.length);
if (end.length > 0) {
result.after = this.buildFrom(end[0].id, end.length, this.pool);
}
return result;
};
var _04Insert = {
append: append_1,
prepend: prepend_1,
"delete": _delete$1,
replace: replace,
splitOn: splitOn
};
/** return json metadata for this phrase */
var json$1 = function json() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var world = arguments.length > 1 ? arguments[1] : undefined;
var res = {}; // text data
if (options.text) {
res.text = this.text();
}
if (options.normal) {
res.normal = this.text('normal');
}
if (options.clean) {
res.clean = this.text('clean');
}
if (options.reduced) {
res.reduced = this.text('reduced');
}
if (options.root) {
res.root = this.text('root');
}
if (options.trim) {
if (res.text) {
res.text = res.text.trim();
}
if (res.normal) {
res.normal = res.normal.trim();
}
if (res.reduced) {
res.reduced = res.reduced.trim();
}
} // terms data
if (options.terms) {
if (options.terms === true) {
options.terms = {};
}
res.terms = this.terms().map(function (t) {
return t.json(options.terms, world);
});
}
return res;
};
var _05Json$1 = {
json: json$1
};
/** match any terms after this phrase */
var lookAhead = function lookAhead(regs) {
// if empty match string, return everything after
if (!regs) {
regs = '.*';
}
var pool = this.pool; // get a list of all terms preceding our start
var terms = [];
var getAfter = function getAfter(id) {
var term = pool.get(id);
if (!term) {
return;
}
terms.push(term);
if (term.prev) {
getAfter(term.next); //recursion
}
};
var all = this.terms();
var lastTerm = all[all.length - 1];
getAfter(lastTerm.next);
if (terms.length === 0) {
return [];
} // got the terms, make a phrase from them
var p = this.buildFrom(terms[0].id, terms.length);
return p.match(regs);
};
/** match any terms before this phrase */
var lookBehind = function lookBehind(regs) {
// if empty match string, return everything before
if (!regs) {
regs = '.*';
}
var pool = this.pool; // get a list of all terms preceding our start
var terms = [];
var getBefore = function getBefore(id) {
var term = pool.get(id);
if (!term) {
return;
}
terms.push(term);
if (term.prev) {
getBefore(term.prev); //recursion
}
};
var term = pool.get(this.start);
getBefore(term.prev);
if (terms.length === 0) {
return [];
} // got the terms, make a phrase from them
var p = this.buildFrom(terms[terms.length - 1].id, terms.length);
return p.match(regs);
};
var _06Lookahead = {
lookAhead: lookAhead,
lookBehind: lookBehind
};
var methods$1 = Object.assign({}, _01Utils, _02Text, _03Change, _04Insert, _05Json$1, _06Lookahead);
// try to avoid doing the match
var failFast = function failFast(p, regs) {
if (regs.length === 0) {
return true;
}
for (var i = 0; i < regs.length; i += 1) {
var reg = regs[i]; //logical quick-ones
if (reg.optional !== true && reg.negative !== true) {
//start/end impossibilites
if (reg.start === true && i > 0) {
return true;
}
} //this is not possible
if (reg.anything === true && reg.negative === true) {
return true;
}
}
return false;
};
var _02FailFast = failFast;
//found a match? it's greedy? keep going!
var getGreedy = function getGreedy(terms, t, reg, until, index, length) {
var start = t;
for (; t < terms.length; t += 1) {
//stop for next-reg match
if (until && terms[t].doesMatch(until, index + t, length)) {
return t;
}
var count = t - start + 1; // is it max-length now?
if (reg.max !== undefined && count === reg.max) {
return t;
} //stop here
if (terms[t].doesMatch(reg, index + t, length) === false) {
// is it too short?
if (reg.min !== undefined && count < reg.min) {
return null;
}
return t;
}
}
return t;
}; //'unspecific greedy' is a weird situation.
var greedyTo = function greedyTo(terms, t, nextReg, index, length) {
//if there's no next one, just go off the end!
if (!nextReg) {
return terms.length;
} //otherwise, we're looking for the next one
for (; t < terms.length; t += 1) {
if (terms[t].doesMatch(nextReg, index + t, length) === true) {
return t;
}
} //guess it doesn't exist, then.
return null;
}; // get or create named group
var getOrCreateGroup = function getOrCreateGroup(namedGroups, namedGroupId, terms, startIndex, group) {
var g = namedGroups[namedGroupId];
if (g) {
return g;
}
var id = terms[startIndex].id;
namedGroups[namedGroupId] = {
group: String(group),
start: id,
length: 0
};
return namedGroups[namedGroupId];
};
/** tries to match a sequence of terms, starting from here */
var tryHere = function tryHere(terms, regs, index, length) {
var namedGroups = {};
var previousGroupId = null;
var t = 0; // we must satisfy each rule in 'regs'
for (var r = 0; r < regs.length; r += 1) {
var reg = regs[r]; // Check if this reg has a named capture group
var isNamedGroup = typeof reg.named === 'string' || typeof reg.named === 'number';
var namedGroupId = null; // Reuse previous capture group if same
if (isNamedGroup) {
var prev = regs[r - 1];
if (prev && prev.named === reg.named && previousGroupId) {
namedGroupId = previousGroupId;
} else {
namedGroupId = _id(reg.named);
previousGroupId = namedGroupId;
}
} //should we fail here?
if (!terms[t]) {
//are all remaining regs optional?
var hasNeeds = regs.slice(r).some(function (remain) {
return !remain.optional;
});
if (hasNeeds === false) {
break;
} // have unmet needs
return [false, null];
} //support 'unspecific greedy' .* properly
if (reg.anything === true && reg.greedy === true) {
var skipto = greedyTo(terms, t, regs[r + 1], reg, index); // ensure it's long enough
if (reg.min !== undefined && skipto - t < reg.min) {
return [false, null];
} // reduce it back, if it's too long
if (reg.max !== undefined && skipto - t > reg.max) {
t = t + reg.max;
continue;
}
if (skipto === null) {
return [false, null]; //couldn't find it
} // is it really this easy?....
if (isNamedGroup) {
var g = getOrCreateGroup(namedGroups, namedGroupId, terms, t, reg.named); // Update group
g.length = skipto - t;
}
t = skipto;
continue;
} //if it looks like a match, continue
//we have a special case where an end-anchored greedy match may need to
//start matching before the actual end; we do this by (temporarily!)
//removing the "end" property from the matching token... since this is
//very situation-specific, we *only* do this when we really need to.
if (reg.anything === true || reg.end === true && reg.greedy === true && index + t < length - 1 && terms[t].doesMatch(Object.assign({}, reg, {
end: false
}), index + t, length) === true || terms[t].doesMatch(reg, index + t, length) === true) {
var startAt = t; // okay, it was a match, but if it optional too,
// we should check the next reg too, to skip it?
if (reg.optional && regs[r + 1]) {
// does the next reg match it too?
if (terms[t].doesMatch(regs[r + 1], index + t, length) === true) {
// but does the next reg match the next term??
// only skip if it doesn't
if (!terms[t + 1] || terms[t + 1].doesMatch(regs[r + 1], index + t, length) === false) {
r += 1;
}
}
} //advance to the next term!
t += 1; //check any ending '$' flags
if (reg.end === true) {
//if this isn't the last term, refuse the match
if (t !== terms.length && reg.greedy !== true) {
return [false, null];
}
} //try keep it going!
if (reg.greedy === true) {
// for greedy checking, we no longer care about the reg.start
// value, and leaving it can cause failures for anchored greedy
// matches. ditto for end-greedy matches: we need an earlier non-
// ending match to succceed until we get to the actual end.
t = getGreedy(terms, t, Object.assign({}, reg, {
start: false,
end: false
}), regs[r + 1], index, length);
if (t === null) {
return [false, null]; //greedy was too short
}
if (reg.min && reg.min > t) {
return [false, null]; //greedy was too short
} // if this was also an end-anchor match, check to see we really
// reached the end
if (reg.end === true && index + t !== length) {
return [false, null]; //greedy didn't reach the end
}
}
if (isNamedGroup) {
// Get or create capture group
var _g = getOrCreateGroup(namedGroups, namedGroupId, terms, startAt, reg.named); // Update group - add greedy or increment length
if (t > 1 && reg.greedy) {
_g.length += t - startAt;
} else {
_g.length++;
}
}
continue;
} //bah, who cares, keep going
if (reg.optional === true) {
continue;
} // should we skip-over an implicit word?
if (terms[t].isImplicit() && regs[r - 1] && terms[t + 1]) {
// does the next one match?
if (terms[t + 1].doesMatch(reg, index + t, length)) {
t += 2;
continue;
}
} // console.log(' ❌\n\n')
return [false, null];
} //return our result
return [terms.slice(0, t), namedGroups];
};
var _03TryMatch = tryHere;
var postProcess = function postProcess(terms, regs, matches) {
if (!matches || matches.length === 0) {
return matches;
} // ensure end reg has the end term
var atEnd = regs.some(function (r) {
return r.end;
});
if (atEnd) {
var lastTerm = terms[terms.length - 1];
matches = matches.filter(function (_ref) {
var arr = _ref.match;
return arr.indexOf(lastTerm) !== -1;
});
}
return matches;
};
var _04PostProcess = postProcess;
/* break-down a match expression into this:
{
word:'',
tag:'',
regex:'',
start:false,
end:false,
negative:false,
anything:false,
greedy:false,
optional:false,
named:'',
choices:[],
}
*/
var hasMinMax = /\{([0-9]+,?[0-9]*)\}/;
var andSign = /&&/;
var captureName = new RegExp(/^<(\S+)>/);
var titleCase$2 = function titleCase(str) {
return str.charAt(0).toUpperCase() + str.substr(1);
};
var end = function end(str) {
return str[str.length - 1];
};
var start = function start(str) {
return str[0];
};
var stripStart = function stripStart(str) {
return str.substr(1);
};
var stripEnd = function stripEnd(str) {
return str.substr(0, str.length - 1);
};
var stripBoth = function stripBoth(str) {
str = stripStart(str);
str = stripEnd(str);
return str;
}; //
var parseToken = function parseToken(w) {
var obj = {}; //collect any flags (do it twice)
for (var i = 0; i < 2; i += 1) {
//end-flag
if (end(w) === '$') {
obj.end = true;
w = stripEnd(w);
} //front-flag
if (start(w) === '^') {
obj.start = true;
w = stripStart(w);
} //capture group (this one can span multiple-terms)
if (start(w) === '[' || end(w) === ']') {
obj.named = true;
if (start(w) === '[') {
obj.groupType = end(w) === ']' ? 'single' : 'start';
} else {
obj.groupType = 'end';
}
w = w.replace(/^\[/, '');
w = w.replace(/\]$/, ''); // Use capture group name
if (start(w) === '<') {
var res = captureName.exec(w);
if (res.length >= 2) {
obj.named = res[1];
w = w.replace(res[0], '');
}
}
} //back-flags
if (end(w) === '+') {
obj.greedy = true;
w = stripEnd(w);
}
if (w !== '*' && end(w) === '*' && w !== '\\*') {
obj.greedy = true;
w = stripEnd(w);
}
if (end(w) === '?') {
obj.optional = true;
w = stripEnd(w);
}
if (start(w) === '!') {
obj.negative = true;
w = stripStart(w);
} //wrapped-flags
if (start(w) === '(' && end(w) === ')') {
// support (one && two)
if (andSign.test(w)) {
obj.choices = w.split(andSign);
obj.operator = 'and';
} else {
obj.choices = w.split('|');
obj.operator = 'or';
} //remove '(' and ')'
obj.choices[0] = stripStart(obj.choices[0]);
var last = obj.choices.length - 1;
obj.choices[last] = stripEnd(obj.choices[last]); // clean up the results
obj.choices = obj.choices.map(function (s) {
return s.trim();
});
obj.choices = obj.choices.filter(function (s) {
return s;
}); //recursion alert!
obj.choices = obj.choices.map(parseToken);
w = '';
} //regex
if (start(w) === '/' && end(w) === '/') {
w = stripBoth(w);
obj.regex = new RegExp(w); //potential vuln - security/detect-non-literal-regexp
return obj;
} //soft-match
if (start(w) === '~' && end(w) === '~') {
w = stripBoth(w);
obj.soft = true;
obj.word = w;
return obj;
}
} // support #Tag{0,9}
if (hasMinMax.test(w) === true) {
w = w.replace(hasMinMax, function (a, b) {
var arr = b.split(/,/g);
if (arr.length === 1) {
// '{3}' Exactly three times
obj.min = Number(arr[0]);
obj.max = Number(arr[0]);
} else {
// '{2,4}' Two to four times
// '{3,}' Three or more times
obj.min = Number(arr[0]);
obj.max = Number(arr[1] || 999);
}
obj.greedy = true;
return '';
});
} //do the actual token content
if (start(w) === '#') {
obj.tag = stripStart(w);
obj.tag = titleCase$2(obj.tag);
return obj;
} //dynamic function on a term object
if (start(w) === '@') {
obj.method = stripStart(w);
return obj;
}
if (w === '.') {
obj.anything = true;
return obj;
} //support alone-astrix
if (w === '*') {
obj.anything = true;
obj.greedy = true;
obj.optional = true;
return obj;
}
if (w) {
//somehow handle encoded-chars?
w = w.replace('\\*', '*');
w = w.replace('\\.', '.');
obj.word = w.toLowerCase();
}
return obj;
};
var parseToken_1 = parseToken;
var isNamed = function isNamed(capture) {
return typeof capture === 'string' || typeof capture === 'number';
};
var fillGroups = function fillGroups(tokens) {
var convert = false;
var index = -1;
var current; //'fill in' capture groups between start-end
for (var i = 0; i < tokens.length; i++) {
var n = tokens[i]; // Give name to un-named single tokens
if (n.groupType === 'single' && n.named === true) {
index += 1;
n.named = index;
continue;
} // Start converting tokens
if (n.groupType === 'start') {
convert = true;
if (isNamed(n.named)) {
current = n.named;
} else {
index += 1;
current = index;
}
} // Ensure this token has the right name
if (convert) {
n.named = current;
} // Stop converting tokens
if (n.groupType === 'end') {
convert = false;
}
}
return tokens;
};
var useOneOf = function useOneOf(tokens) {
return tokens.map(function (token) {
if (token.choices !== undefined) {
// are they all straight non-optional words?
var shouldPack = token.choices.every(function (c) {
return c.optional !== true && c.negative !== true && c.word !== undefined;
});
if (shouldPack === true) {
var oneOf = {};
token.choices.forEach(function (c) {
return oneOf[c.word] = true;
});
token.oneOf = oneOf;
delete token.choices;
}
}
return token;
});
};
var postProcess$1 = function postProcess(tokens) {
// ensure all capture groups are filled between start and end
// give all capture groups names
var count = tokens.filter(function (t) {
return t.groupType;
}).length;
if (count > 0) {
tokens = fillGroups(tokens);
} // convert 'choices' format to 'oneOf' format
tokens = useOneOf(tokens); // console.log(tokens)
return tokens;
};
var postProcess_1 = postProcess$1;
var isArray$1 = function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}; //split-up by (these things)
var byParentheses = function byParentheses(str) {
var arr = str.split(/([\^\[\!]*(?:<\S+>)?\(.*?\)[?+*]*\]?\$?)/);
arr = arr.map(function (s) {
return s.trim();
});
return arr;
};
var byWords = function byWords(arr) {
var words = [];
arr.forEach(function (a) {
//keep brackets lumped together
if (/^[[^_/]?\(/.test(a[0])) {
words.push(a);
return;
}
var list = a.split(' ');
list = list.filter(function (w) {
return w;
});
words = words.concat(list);
});
return words;
}; //turn an array into a 'choices' list
var byArray = function byArray(arr) {
return [{
choices: arr.map(function (s) {
return {
word: s
};
})
}];
};
var fromDoc = function fromDoc(doc) {
if (!doc || !doc.list || !doc.list[0]) {
return [];
}
var ids = [];
doc.list.forEach(function (p) {
p.terms().forEach(function (t) {
ids.push({
id: t.id
});
});
});
return [{
choices: ids,
greedy: true
}];
};
/** parse a match-syntax string into json */
var syntax = function syntax(input) {
// fail-fast
if (input === null || input === undefined || input === '') {
return [];
} //try to support a ton of different formats:
if (_typeof(input) === 'object') {
if (isArray$1(input)) {
if (input.length === 0 || !input[0]) {
return [];
} //is it a pre-parsed reg-list?
if (_typeof(input[0]) === 'object') {
return input;
} //support a flat array of normalized words
if (typeof input[0] === 'string') {
return byArray(input);
}
} //support passing-in a compromise object as a match
if (input && input.isA === 'Doc') {
return fromDoc(input);
}
return [];
}
if (typeof input === 'number') {
input = String(input); //go for it?
}
var tokens = byParentheses(input);
tokens = byWords(tokens);
tokens = tokens.map(parseToken_1); //clean up anything weird
tokens = postProcess_1(tokens); // console.log(JSON.stringify(tokens, null, 2))
return tokens;
};
var syntax_1 = syntax;
/** returns a simple array of arrays */
var matchAll = function matchAll(p, regs) {
var matchOne = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
//if we forgot to parse it..
if (typeof regs === 'string') {
regs = syntax_1(regs);
} //try to dismiss it, at-once
if (_02FailFast(p, regs) === true) {
return [];
} //any match needs to be this long, at least
var minLength = regs.filter(function (r) {
return r.optional !== true;
}).length;
var terms = p.terms();
var matches = []; //optimisation for '^' start logic
if (regs[0].start === true) {
var _tryMatch = _03TryMatch(terms, regs, 0, terms.length),
_tryMatch2 = _slicedToArray(_tryMatch, 2),
match = _tryMatch2[0],
groups = _tryMatch2[1];
if (match !== false && match.length > 0) {
match = match.filter(function (m) {
return m;
});
matches.push({
match: match,
groups: groups
});
}
return _04PostProcess(terms, regs, matches);
} //try starting, from every term
for (var i = 0; i < terms.length; i += 1) {
// slice may be too short
if (i + minLength > terms.length) {
break;
} //try it!
var _tryMatch3 = _03TryMatch(terms.slice(i), regs, i, terms.length),
_tryMatch4 = _slicedToArray(_tryMatch3, 2),
_match = _tryMatch4[0],
_groups = _tryMatch4[1];
if (_match !== false && _match.length > 0) {
//zoom forward!
i += _match.length - 1; //[capture-groups] return some null responses
_match = _match.filter(function (m) {
return m;
});
matches.push({
match: _match,
groups: _groups
}); //ok, maybe that's enough?
if (matchOne === true) {
return _04PostProcess(terms, regs, matches);
}
}
}
return _04PostProcess(terms, regs, matches);
};
var _01MatchAll = matchAll;
/** return anything that doesn't match.
* returns a simple array of arrays
*/
var notMatch = function notMatch(p, regs) {
var found = {};
var arr = _01MatchAll(p, regs);
arr.forEach(function (_ref) {
var ts = _ref.match;
ts.forEach(function (t) {
found[t.id] = true;
});
}); //return anything not found
var terms = p.terms();
var result = [];
var current = [];
terms.forEach(function (t) {
if (found[t.id] === true) {
if (current.length > 0) {
result.push(current);
current = [];
}
return;
}
current.push(t);
});
if (current.length > 0) {
result.push(current);
}
return result;
};
var not = notMatch;
/** return an array of matching phrases */
var match_1 = function match_1(regs) {
var _this = this;
var justOne = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var matches = _01MatchAll(this, regs, justOne); //make them phrase objects
matches = matches.map(function (_ref) {
var match = _ref.match,
groups = _ref.groups;
var p = _this.buildFrom(match[0].id, match.length, groups);
p.cache.terms = match;
return p;
});
return matches;
};
/** return boolean if one match is found */
var has = function has(regs) {
var matches = _01MatchAll(this, regs, true);
return matches.length > 0;
};
/** remove all matches from the result */
var not$1 = function not$1(regs) {
var _this2 = this;
var matches = not(this, regs); //make them phrase objects
matches = matches.map(function (list) {
return _this2.buildFrom(list[0].id, list.length);
});
return matches;
};
/** return a list of phrases that can have this tag */
var canBe$1 = function canBe(tag, world) {
var _this3 = this;
var results = [];
var terms = this.terms();
var previous = false;
for (var i = 0; i < terms.length; i += 1) {
var can = terms[i].canBe(tag, world);
if (can === true) {
if (previous === true) {
//add it to the end
results[results.length - 1].push(terms[i]);
} else {
results.push([terms[i]]); //make a new one
}
previous = can;
}
} //turn them into Phrase objects
results = results.filter(function (a) {
return a.length > 0;
}).map(function (arr) {
return _this3.buildFrom(arr[0].id, arr.length);
});
return results;
};
var match = {
match: match_1,
has: has,
not: not$1,
canBe: canBe$1
};
var Phrase = function Phrase(id, length, pool) {
_classCallCheck(this, Phrase);
this.start = id;
this.length = length;
this.isA = 'Phrase'; // easier than .constructor...
Object.defineProperty(this, 'pool', {
enumerable: false,
writable: true,
value: pool
});
Object.defineProperty(this, 'cache', {
enumerable: false,
writable: true,
value: {}
});
Object.defineProperty(this, 'groups', {
enumerable: false,
writable: true,
value: {}
});
};
/** create a new Phrase object from an id and length */
Phrase.prototype.buildFrom = function (id, length, groups) {
var p = new Phrase(id, length, this.pool); //copy-over or replace capture-groups too
if (groups && Object.keys(groups).length > 0) {
p.groups = groups;
} else {
p.groups = this.groups;
}
return p;
}; //apply methods
Object.assign(Phrase.prototype, match);
Object.assign(Phrase.prototype, methods$1); //apply aliases
var aliases = {
term: 'terms'
};
Object.keys(aliases).forEach(function (k) {
return Phrase.prototype[k] = Phrase.prototype[aliases[k]];
});
var Phrase_1 = Phrase;
/** a key-value store of all terms in our Document */
var Pool = /*#__PURE__*/function () {
function Pool() {
var words = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, Pool);
//quiet this property in console.logs
Object.defineProperty(this, 'words', {
enumerable: false,
value: words
});
}
/** throw a new term object in */
_createClass(Pool, [{
key: "add",
value: function add(term) {
this.words[term.id] = term;
return this;
}
/** find a term by it's id */
}, {
key: "get",
value: function get(id) {
return this.words[id];
}
/** find a term by it's id */
}, {
key: "remove",
value: function remove(id) {
delete this.words[id];
}
}, {
key: "merge",
value: function merge(pool) {
Object.assign(this.words, pool.words);
return this;
}
/** helper method */
}, {
key: "stats",
value: function stats() {
return {
words: Object.keys(this.words).length
};
}
}]);
return Pool;
}();
/** make a deep-copy of all terms */
Pool.prototype.clone = function () {
var _this = this;
var keys = Object.keys(this.words);
var words = keys.reduce(function (h, k) {
var t = _this.words[k].clone();
h[t.id] = t;
return h;
}, {});
return new Pool(words);
};
var Pool_1 = Pool;
//add forward/backward 'linked-list' prev/next ids
var linkTerms = function linkTerms(terms) {
terms.forEach(function (term, i) {
if (i > 0) {
term.prev = terms[i - 1].id;
}
if (terms[i + 1]) {
term.next = terms[i + 1].id;
}
});
};
var _linkTerms = linkTerms;
//(Rule-based sentence boundary segmentation) - chop given text into its proper sentences.
// Ignore periods/questions/exclamations used in acronyms/abbreviations/numbers, etc.
// @spencermountain 2017 MIT
//proper nouns with exclamation marks
// const blacklist = {
// yahoo: true,
// joomla: true,
// jeopardy: true,
// }
//regs-
var initSplit = /(\S.+?[.!?\u203D\u2E18\u203C\u2047-\u2049])(?=\s+|$)/g;
var hasSomething = /\S/;
var isAcronym$1 = /[ .][A-Z]\.? *$/i;
var hasEllipse = /(?:\u2026|\.{2,}) *$/;
var newLine = /((?:\r?\n|\r)+)/; // Match different new-line formats
var hasLetter = /[a-z0-9\u00C0-\u00FF\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]/i;
var startWhitespace = /^\s+/; // Start with a regex:
var naiive_split = function naiive_split(text) {
var all = []; //first, split by newline
var lines = text.split(newLine);
for (var i = 0; i < lines.length; i++) {
//split by period, question-mark, and exclamation-mark
var arr = lines[i].split(initSplit);
for (var o = 0; o < arr.length; o++) {
all.push(arr[o]);
}
}
return all;
};
/** does this look like a sentence? */
var isSentence = function isSentence(str, abbrevs) {
// check for 'F.B.I.'
if (isAcronym$1.test(str) === true) {
return false;
} //check for '...'
if (hasEllipse.test(str) === true) {
return false;
} // must have a letter
if (hasLetter.test(str) === false) {
return false;
}
var txt = str.replace(/[.!?\u203D\u2E18\u203C\u2047-\u2049] *$/, '');
var words = txt.split(' ');
var lastWord = words[words.length - 1].toLowerCase(); // check for 'Mr.'
if (abbrevs.hasOwnProperty(lastWord)) {
return false;
} // //check for jeopardy!
// if (blacklist.hasOwnProperty(lastWord)) {
// return false
// }
return true;
};
var splitSentences = function splitSentences(text, world) {
var abbrevs = world.cache.abbreviations;
text = text || '';
text = String(text);
var sentences = []; // First do a greedy-split..
var chunks = []; // Ensure it 'smells like' a sentence
if (!text || typeof text !== 'string' || hasSomething.test(text) === false) {
return sentences;
} // cleanup unicode-spaces
text = text.replace('\xa0', ' '); // Start somewhere:
var splits = naiive_split(text); // Filter-out the crap ones
for (var i = 0; i < splits.length; i++) {
var s = splits[i];
if (s === undefined || s === '') {
continue;
} //this is meaningful whitespace
if (hasSomething.test(s) === false) {
//add it to the last one
if (chunks[chunks.length - 1]) {
chunks[chunks.length - 1] += s;
continue;
} else if (splits[i + 1]) {
//add it to the next one
splits[i + 1] = s + splits[i + 1];
continue;
}
} //else, only whitespace, no terms, no sentence
chunks.push(s);
} //detection of non-sentence chunks:
//loop through these chunks, and join the non-sentence chunks back together..
for (var _i = 0; _i < chunks.length; _i++) {
var c = chunks[_i]; //should this chunk be combined with the next one?
if (chunks[_i + 1] && isSentence(c, abbrevs) === false) {
chunks[_i + 1] = c + (chunks[_i + 1] || '');
} else if (c && c.length > 0) {
//&& hasLetter.test(c)
//this chunk is a proper sentence..
sentences.push(c);
chunks[_i] = '';
}
} //if we never got a sentence, return the given text
if (sentences.length === 0) {
return [text];
} //move whitespace to the ends of sentences, when possible
//['hello',' world'] -> ['hello ','world']
for (var _i2 = 1; _i2 < sentences.length; _i2 += 1) {
var ws = sentences[_i2].match(startWhitespace);
if (ws !== null) {
sentences[_i2 - 1] += ws[0];
sentences[_i2] = sentences[_i2].replace(startWhitespace, '');
}
}
return sentences;
};
var _01Sentences = splitSentences; // console.log(sentence_parser('john f. kennedy'));
var wordlike = /\S/;
var isBoundary = /^[!?.]+$/;
var naiiveSplit = /(\S+)/;
var isSlash = /[a-z] ?\/ ?[a-z]*$/;
var notWord = {
'.': true,
'-': true,
//dash
'': true,
//en-dash
'—': true,
//em-dash
'--': true,
'...': true // '/': true, // 'one / two'
};
var hasHyphen = function hasHyphen(str) {
//dont split 're-do'
if (/^(re|un)-?[^aeiou]./.test(str) === true) {
return false;
} //letter-number
var reg = /^([a-z\u00C0-\u00FF`"'/]+)(-||—)([a-z0-9\u00C0-\u00FF].*)/i;
if (reg.test(str) === true) {
return true;
} //support weird number-emdash combo '20102011'
// let reg2 = /^([0-9]+)(|—)([0-9].*)/i
// if (reg2.test(str)) {
// return true
// }
return false;
}; // 'he / she' should be one word
var combineSlashes = function combineSlashes(arr) {
for (var i = 1; i < arr.length - 1; i++) {
if (isSlash.test(arr[i])) {
arr[i - 1] += arr[i] + arr[i + 1];
arr[i] = null;
arr[i + 1] = null;
}
}
return arr;
};
var splitHyphens = function splitHyphens(word) {
var arr = []; //support multiple-hyphenated-terms
var hyphens = word.split(/[-–—]/);
var whichDash = '-';
var found = word.match(/[-–—]/);
if (found && found[0]) {
whichDash = found;
}
for (var o = 0; o < hyphens.length; o++) {
if (o === hyphens.length - 1) {
arr.push(hyphens[o]);
} else {
arr.push(hyphens[o] + whichDash);
}
}
return arr;
};
var isArray$2 = function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}; //turn a string into an array of strings (naiive for now, lumped later)
var splitWords = function splitWords(str) {
var result = [];
var arr = []; //start with a naiive split
str = str || '';
if (typeof str === 'number') {
str = String(str);
}
if (isArray$2(str)) {
return str;
}
var words = str.split(naiiveSplit);
for (var i = 0; i < words.length; i++) {
//split 'one-two'
if (hasHyphen(words[i]) === true) {
arr = arr.concat(splitHyphens(words[i]));
continue;
}
arr.push(words[i]);
} //greedy merge whitespace+arr to the right
var carry = '';
for (var _i = 0; _i < arr.length; _i++) {
var word = arr[_i]; //if it's more than a whitespace
if (wordlike.test(word) === true && notWord.hasOwnProperty(word) === false && isBoundary.test(word) === false) {
//put whitespace on end of previous term, if possible
if (result.length > 0) {
result[result.length - 1] += carry;
result.push(word);
} else {
//otherwise, but whitespace before
result.push(carry + word);
}
carry = '';
} else {
carry += word;
}
} //handle last one
if (carry) {
if (result.length === 0) {
result[0] = '';
}
result[result.length - 1] += carry; //put it on the end
} // combine 'one / two'
result = combineSlashes(result); // remove empty results
result = result.filter(function (s) {
return s;
});
return result;
};
var _02Words = splitWords;
var isArray$3 = function isArray(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
};
/** turn a string into an array of Phrase objects */
var fromText = function fromText() {
var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var world = arguments.length > 1 ? arguments[1] : undefined;
var pool = arguments.length > 2 ? arguments[2] : undefined;
var sentences = null; //a bit of validation, first
if (typeof text !== 'string') {
if (typeof text === 'number') {
text = String(text);
} else if (isArray$3(text)) {
sentences = text;
}
} //tokenize into words
sentences = sentences || _01Sentences(text, world);
sentences = sentences.map(function (str) {
return _02Words(str);
}); //turn them into proper objects
pool = pool || new Pool_1();
var phrases = sentences.map(function (terms) {
terms = terms.map(function (str) {
var term = new Term_1(str);
pool.add(term);
return term;
}); //add next/previous ids
_linkTerms(terms); //return phrase objects
var p = new Phrase_1(terms[0].id, terms.length, pool);
p.cache.terms = terms;
return p;
}); //return them ready for a Document object
return phrases;
};
var _01Tokenizer = fromText;
var fromJSON = function fromJSON(json, world) {
var pool = new Pool_1();
var phrases = json.map(function (p, k) {
var terms = p.terms.map(function (o, i) {
var term = new Term_1(o.text);
term.pre = o.pre !== undefined ? o.pre : '';
if (o.post === undefined) {
o.post = ' '; //no given space for very last term
if (i >= p.terms.length - 1) {
o.post = '. ';
if (k >= p.terms.length - 1) {
o.post = '.';
}
}
}
term.post = o.post !== undefined ? o.post : ' ';
if (o.tags) {
o.tags.forEach(function (tag) {
return term.tag(tag, '', world);
});
}
pool.add(term);
return term;
}); //add prev/next links
_linkTerms(terms); // return a proper Phrase object
return new Phrase_1(terms[0].id, terms.length, pool);
});
return phrases;
};
var fromJSON_1 = fromJSON;
var _version = '13.5.0X003';
var _data = {
"Comparative": "true¦better",
"Superlative": "true¦earlier",
"PresentTense": "true¦is,sounds",
"Value": "true¦a few",
"Noun": "true¦a5b4c2f1here,ie,lit,m0no doubt,pd,tce;a,d;t,y;a,ca,o0;l,rp;a,l;d,l,rc",
"Copula": "true¦a1is,w0;as,ere;m,re",
"PastTense": "true¦be3came,d2had,lied,meant,sa2taken,w0;as,e0;nt,re;id;en,gan",
"Condition": "true¦if,lest,unless",
"Gerund": "true¦accord0be0develop0go0result0stain0;ing",
"Negative": "true¦n0;ever,o0;!n,t",
"QuestionWord": "true¦how3wh0;at,e1ich,o0y;!m,se;n,re; come,'s",
"Plural": "true¦records",
"Conjunction": "true¦&,aEbAcuz,how8in caDno7o6p4supposing,t1vers5wh0yet;eth8ile;h0o;eref9o0;!uC;l0rovided that;us;r,therwi6; matt1r;!ev0;er;e0ut;cau1f0;ore;se;lthou1nd,s 0;far as,if;gh",
"Pronoun": "true¦'em,elle,h4i3me,ourselves,she5th1us,we,you0;!rself;e0ou;m,y;!l,t;e0im;!'s",
"Singular": "true¦0:0Z;1:12;a0Yb0Mc0Dd06e04fZgUhQiPjel0kitty,lOmKnJoIpEquestion mark,rCs7t4u2womY;nc0Ts 2;doll0Fst0H; rex,a3h2ic,ragedy,v show;ere,i1;l0x return;i5ky,omeone,t2uper bowl,yst0Y;ep3ri1u2;de0Rff;faOmoO;st0Nze;al0i1o2;om,se;a4i0Kl06r3u2;dMrpoE;erogaWobl0P;rt,te0J;bjTceHthers;othi1umb0F;a4ee05o2;del,m2nopo0th0D;!my;n,yf0;i0unch;ci1nsect;ead start,o2;l0me3u2;se;! run;adf0entlem5irlZlaci04od,rand3u2;l0y; slam,fa2mo2;th01;an;a5ella,ly,ol0r3un2;di1;iTo2;ntiWsN;mi0thV;conomy,gg,ner5veWx2;ampQecu7;ad7e4innSo2ragonf0ude;cumentFg2i0l0or;gy;ath,t2;ec2;tive;!dy;a8eili1h6i4o2redit card;ttage,u2;riJsin;ty,vil w2;ar;andeliGocol2;ate;n2rD;ary;aAel0lesHo6r4u2;n2tterf0;ti1;eakfast,o2;!th8;dy,tt4y2;!fri2;end;le;nki1r2;ri2;er;d4l0noma0u2;nt;ly; homin4verti2;si1;ng;em",
"Actor": "true¦aJbGcFdCengineIfAgardenIh9instructPjournalLlawyIm8nurse,opeOp5r3s1t0;echnCherapK;ailNcientJoldiGu0;pervKrgeon;e0oofE;ceptionGsearC;hotographClumbColi1r0sychologF;actitionBogrammB;cem6t5;echanic,inist9us4;airdress8ousekeep8;arm7ire0;fight6m2;eputy,iet0;ici0;an;arpent2lerk;ricklay1ut0;ch0;er;ccoun6d2ge7r0ssis6ttenda7;chitect,t0;ist;minist1v0;is1;rat0;or;ta0;nt",
"Honorific": "true¦a03b00cSdReQfiLgKhon,jr,king,lJmEoDp8queen,r4s0taoiseach,vice7;e1fc,gt,ir,r,u0;ltTpt,rg;c0nDrgeaL;ond liJretary;abbi,e0;ar1pAs,v0;!erend; admirY;astPhd,r0vt;esideEi1of0;!essN;me mini5nce0;!ss;fficOp,rd;a3essrs,i2lle,me,r1s0;!tr;!s;stK;gistrate,j,r6yF;i3lb,t;en,ov;eld mar3rst l0;ady,i0;eutena0;nt;shG;sq,xcellency;et,oct6r,utchess;apt6hance4mdr,o0pl;lonel,m2ngress0unci3;m0wom0;an;dr,mand5;ll0;or;!ain;ldg,rig0;!adi0;er;d0sst,tty,yatullah;j,m0v;!ir0;al",
"SportsTeam": "true¦0:1A;1:1H;2:1G;a1Eb16c0Td0Kfc dallas,g0Ihouston 0Hindiana0Gjacksonville jagua0k0El0Bm01newToQpJqueens parkIreal salt lake,sAt5utah jazz,vancouver whitecaps,w3yW;ashington 3est ham0Rh10;natio1Oredski2wizar0W;ampa bay 6e5o3;ronto 3ttenham hotspur;blue ja0Mrapto0;nnessee tita2xasC;buccanee0ra0K;a7eattle 5heffield0Kporting kansas0Wt3;. louis 3oke0V;c1Frams;marine0s3;eah15ounG;cramento Rn 3;antonio spu0diego 3francisco gJjose earthquak1;char08paA; ran07;a8h5ittsburgh 4ortland t3;imbe0rail blaze0;pirat1steele0;il3oenix su2;adelphia 3li1;eagl1philNunE;dr1;akland 3klahoma city thunder,rlando magic;athle0Mrai3;de0; 3castle01;england 7orleans 6york 3;city fc,g4je0FknXme0Fred bul0Yy3;anke1;ian0D;pelica2sain0C;patrio0Brevolut3;ion;anchester Be9i3ontreal impact;ami 7lwaukee b6nnesota 3;t4u0Fvi3;kings;imberwolv1wi2;rewe0uc0K;dolphi2heat,marli2;mphis grizz3ts;li1;cXu08;a4eicesterVos angeles 3;clippe0dodDla9; galaxy,ke0;ansas city 3nE;chiefs,roya0E; pace0polis colU;astr06dynamo,rockeTtexa2;olden state warrio0reen bay pac3;ke0;.c.Aallas 7e3i05od5;nver 5troit 3;lio2pisto2ti3;ge0;broncZnuggeM;cowbo4maver3;ic00;ys; uQ;arCelKh8incinnati 6leveland 5ol3;orado r3umbus crew sc;api5ocki1;brow2cavalie0india2;bengaWre3;ds;arlotte horAicago 3;b4cubs,fire,wh3;iteB;ea0ulR;diff3olina panthe0; c3;ity;altimore 9lackburn rove0oston 5rooklyn 3uffalo bilN;ne3;ts;cel4red3; sox;tics;rs;oriol1rave2;rizona Ast8tlanta 3;brav1falco2h4u3;nited;aw9;ns;es;on villa,r3;os;c5di3;amondbac3;ks;ardi3;na3;ls",
"Uncountable": "true¦a1Ib1Ac11d0Ye0Rf0Lg0Hh0Ci08j07knowled1Hl02mUnews,oTpQrLsAt5vi4w0;a2ea05i1oo0;d,l;ldlife,ne;rmth,t17;neg0Yol06tae;e3h2oothpaste,r0una;affPou0;ble,sers,t;ermod1Eund12;a,nnis;a8cene04eri0Oh7il6kittl0Onow,o5p3t1u0;g0Rnshi0H;ati1De0;am,el;ace16e0;ci0Jed;ap,cc0U;k,v0T;eep,ingl0G;d04fe10l0nd;m0St;a3e1ic0;e,ke0D;c0laxa09search;ogni08rea08;bi09in;aJe1hys10last5o0ressV;lit0Zrk,w0J;a0Vtrol;bstetr0Xil,xygen;a5e3ilk,o2u0;mps,s0;ic;nGo0A;a0chan0S;slZt;chine0il,themat0Q; learn05ry;aught08e2i1ogi0Nu0;ck,g0C;ce,ghtn02ngui0LteratH;a0isG;th04;ewel7usti0G;ce,mp0nformaOtself;a0ortan0E;ti0;en0C;a3isto2o0;ck0mework,n0spitali06;ey;ry;ir,libut,ppi7;en01o1r0um,ymna08;a6ound;l0ssip;d,f;i4lour,o1urnit0;ure;od,rgive0uriNwl;ne0;ss;c6sh;conomZduca5lectr4n2quip3thZvery0;body,o0thE;ne;joy0tertain0;ment;iciNonU;tiF;ar1iabet0raugh1;es;ts;a7elcius,h3ivPl2o0urrency;al,ld w0nfusiAttA;ar;assMoth2;aos,e0;e1w0;ing;se;r4sh;a4eef,i1lood,owls,read,utt0;er;lliar1s0;on;ds;g0ss;ga0;ge;c6dvi5ero3ir2mnes1rt,thl0;et7;ty;craft;b4d0naut4;ynam3;ce;id,ou0;st0;ics",
"Infinitive": "true¦0:6K;1:6Y;2:57;3:6W;4:6V;5:5Z;6:67;7:6U;8:6Q;9:6I;A:6S;B:6P;C:6Z;D:6D;E:56;F:5P;a6Cb61c52d4Ae3Uf3Hg3Bh34i2Rj2Pk2Nl2Fm25n22o1Xp1Iques3Ir0Qs05tXuSvOwHyG;awn,ield;aJe1Yhist6iIoGre65;nd0rG;k,ry;pe,sh,th0;lk,nHrGsh,tDve;n,raC;d0t;aIiGo7;eGsB;!w;l6Cry;nHpGr4se;gra4Mli3Z;dGi7lo5Spub3O;erGo;mi58w1I;aMeLhKoJrHuGwi8;ne,rn;aGe0Mi5Nu8y;de,in,nsf0p,v5F;r2XuD;ank,reat2N;nd,st;lk,rg1Ps7;aZcWeVhTi4Akip,lSmRnee3Jo4YpQtJuGwitD;bmBck,ff0gge8ppHrGspe5;ge,pri1rou4Vvi3;ly,o34;aLeKoJrHuG;dy,mb6;aEeGi3;ngth2Dss,tD;p,re;m,p;in,ke,r0Qy;laFoil,rink6;e1Xi6o3H;am,ip;a2iv0oG;ck,ut;arDem,le5n1r3tt6;aHo2rG;atDew;le,re;il,ve;a05eIisk,oHuG;in,le,sh;am,ll;a01cZdu9fYgXje5lUmTnt,pQquPsKtJvGwa5O;eGiew,o34;al,l,rG;se,t;aEi2u40;eJi8oItG;!o2rG;i5uc1Y;l3rt;mb6nt,r3;e8i2;air,eHlGo3ZreseC;a9y;at;aEemb0i3Vo3;aHeGi3y;a1nt;te,x;a56r0I;act1Wer,le5u1;a11ei3k5IoGyc6;gni2Anci6rd;ch,li29s5G;i1nG;ge,k;aTerSiRlOoMrIuG;b1Zll,mp,rGsh;cha1s4J;ai1eIiCoG;cGdu9greAhibBmi1te8vi2T;eAlaim;di5pa2ss,veC;iCp,rtr3ZsGur;e,t;aHuG;g,n4;n,y;ck,le;fo30mBsi8;ck,iCrt4Fss,u1;bJccur,ff0pera7utweIverGwe;co40lap,ta20u1wG;helm;igh;ser3taE;eHotG;e,i9;ed,gle5;aLeKiIoHuG;ltip3Crd0;nit11ve;nGrr10;d,g6us;asu2lt,n0Nr4;intaEna4rHtG;ch,t0;ch,kGry;et;aLeKiIoGu1B;aGck,ok,ve;d,n;ft,ke,mBnGst2Wve;e,k;a2Dc0Et;b0Nck,uG;gh,nD;iGno2Z;ck,ll,ss;am,oEuG;d4mp;gno2mQnGss3C;cOdica7flu0MhNsKtIvG;eGol3;nt,st;erGrodu9;a5fe2;i8tG;aGru5;ll;abBibB;lu1Er1C;agi22pG;lemeCo20ro3;aKeIi2oHuG;nt,rry;n02pe,st;aGlp;d,t;nd6ppGrm,te;en;aKloAove1MrIuG;arGeAi13;ant33d;aGip,umb6;b,sp;in,th0ze;aQeaPiNlLoIracHuncG;ti3D;tu2;cus,lHrG;ce,eca8m,s2V;d,l1Z;aFoG;at,od,w;gu2lGniFx;e,l;r,tu2;il,vG;or;a13cho,le5mSnPstNvalua7xG;a0AcLerKi8pGte17;a16eHi2laEoGreA;rt,se;ct,riG;en9;ci1t;el,han4;abGima7;liF;ab6couXdHfor9ga4han9j03riDsu2t0vG;isi2Qy;!u2;body,er4pG;hasiGow0;ze;a06eUiLoKrHuG;mp;aHeAiG;ft;g,in;d4ubt;ff0p,re5sHvG;iYor9;aKcHliGmiApl16tinguiF;ke;oGuA;uGv0;ra4;gr1TppG;ear,ro3;cNem,fLliv0ma0Dny,pKsHterG;mi0E;cribe,er3iHtrG;oy;gn,re;a09e08i5osB;eGi09y;at,ct;iIlHrG;ea1;a2i05;de;ma4n9re,te;a0Ae09h06i7l04oJrG;aHeGoAuFy;a7dB;ck,ve;llZmSnHok,py,uGv0;gh,nt;cePdu5fMsKtIvG;eGin9;rt,y;aEin0SrG;a8ibu7ol;iGtitu7;d0st;iHoGroC;rm;gu2rm;rn;biLfoKmaJpG;a2laE;in;re;nd;rt;ne;ap1e5;aGip,o1;im,w;aHeG;at,ck,w;llen4n4r4se;a1nt0;ll,ncIrGt0u1;eGry;!en;el;aPeMloLoJruFuG;lGry;ly;sh;a8mb,o8rrGth0un9;ow;ck;ar,lHnefBtrG;ay;ie3ong;ng,se;band0Jc0Bd06ffo05gr04id,l01mu1nYppTrQsKttGvoid,waB;acIeHra5;ct;m0Fnd;h,k;k,sG;eIiHocia7uG;me;gn,st;mb6rt;le;chHgGri3;ue;!i3;eaJlIroG;aDve;ch;aud,y;l,r;noun9sw0tG;icipa7;ce;lHt0;er;e4ow;ee;rd;aRdIju8mBoR;it;st;!reA;ss;cJhie3knowled4tiva7;te;ge;ve;eIouCu1;se;nt;pt;on",
"Unit": "true¦0:19;a14b12c0Od0Ne0Lf0Gg0Ch09in0Hjoule0k02l00mNnMoLpIqHsqCt7volts,w6y4z3°2µ1;g,s;c,f,n;b,e2;a0Nb,d0Dears old,o1;tt0H;att0b;able4b3d,e2on1sp;!ne0;a2r0D;!l,sp;spo04; ft,uare 1;c0Id0Hf3i0Fkilo0Jm1ya0E;e0Mil1;e0li0H;eet0o0D;t,uart0;ascals,e2i1ou0Pt;c0Mnt0;rcent,t02;hms,uYz;an0JewtT;/s,b,e9g,i3l,m2p1²,³;h,s;!²;!/h,cro5l1;e1li08;! pFs1²;! 1;anEpD;g06s0B;gQter1;! 2s1;! 1;per second;b,i00m,u1x;men0x0;b,elvin0g,ilo2m1nR;!/h,ph,²;byZgXmeter1;! p2s1;! p1;er1; hour;e1g,r0z;ct1rtz0;aXogQ;al2b,igAra1;in0m0;!l1;on0;a4emtPl2t1;²,³; oz,uid ou1;nce0;hrenheit0rad0;b,x1;abyH;eciCg,l,mA;arat0eAg,m9oulomb0u1;bic 1p0;c5d4fo3i2meAya1;rd0;nch0;ot0;eci2;enti1;me4;!²,³;lsius0nti1;g2li1me1;ter0;ram0;bl,y1;te0;c4tt1;os1;eco1;nd0;re0;!s",
"Organization": "true¦0:46;a3Ab2Qc2Ad21e1Xf1Tg1Lh1Gi1Dj19k17l13m0Sn0Go0Dp07qu06rZsStFuBv8w3y1;amaha,m0Xou1w0X;gov,tu2S;a3e1orld trade organizati41;lls fargo,st1;fie22inghou16;l1rner br3D;-m11gree31l street journ25m11;an halNeriz3Wisa,o1;dafo2Gl1;kswagLvo;bs,kip,n2ps,s1;a tod2Rps;es35i1;lev2Xted natio2Uv; mobi2Kaco bePd bMeAgi frida9h3im horto2Tmz,o1witt2W;shiba,y1;ota,s r Y;e 1in lizzy;b3carpen33daily ma2Xguess w2holli0rolling st1Ms1w2;mashing pumpki2Ouprem0;ho;ea1lack eyed pe3Fyrds;ch bo1tl0;ys;l2s1;co,la m12;efoni07us;a6e4ieme2Gnp,o2pice gir5ta1ubaru;rbucks,to2N;ny,undgard1;en;a2Rx pisto1;ls;few25insbu26msu1X;.e.m.,adiohead,b6e3oyal 1yan2X;b1dutch she4;ank;/max,aders dige1Ed 1vl32;bu1c1Uhot chili peppe2Klobst28;ll;c,s;ant2Vizno2F;an5bs,e3fiz24hilip morrBi2r1;emier27octer & gamb1Rudenti14;nk floyd,zza hut;psi28tro1uge08;br2Qchina,n2Q; 2ason1Xda2G;ld navy,pec,range juli2xf1;am;us;a9b8e5fl,h4i3o1sa,wa;kia,tre dame,vart1;is;ke,ntendo,ss0K;l,s;c,st1Etflix,w1; 1sweek;kids on the block,york08;a,c;nd1Us2t1;ional aca2Fo,we0Q;a,cYd0O;aAcdonald9e5i3lb,o1tv,yspace;b1Nnsanto,ody blu0t1;ley crue,or0O;crosoft,t1;as,subisO;dica3rcedes2talli1;ca;!-benz;id,re;'s,s;c's milk,tt13z1Y;'ore09a3e1g,ittle caesa1Ktd;novo,x1;is,mark; pres5-z-boy,bour party;atv,fc,kk,m1od1K;art;iffy lu0Lo3pmorgan1sa;! cha1;se;hnson & johns1Sy d1R;bm,hop,n1tv;c,g,te1;l,rpol; & m,asbro,ewlett-packaTi3o1sbc,yundai;me dep1n1J;ot;tac1zbollah;hi;eneral 6hq,l5mb,o2reen d0Iu1;cci,ns n ros0;ldman sachs,o1;dye1g0B;ar;axo smith kliZencore;electr0Im1;oto0V;a3bi,da,edex,i1leetwood mac,oGrito-l0A;at,nancial1restoV; tim0;cebook,nnie mae;b06sa,u3xxon1; m1m1;ob0H;!rosceptics;aiml0Ae5isney,o3u1;nkin donuts,po0Wran dur1;an;j,w j1;on0;a,f leppa3ll,p2r spiegZstiny's chi1;ld;eche mode,t;rd;aEbc,hBi9nn,o3r1;aigsli5eedence clearwater reviv1ossra05;al;!ca c5l4m1o0Ast05;ca2p1;aq;st;dplMgate;ola;a,sco1tigroup;! systems;ev2i1;ck fil-a,na daily;r0Hy;dbury,pital o1rl's jr;ne;aGbc,eCfAl6mw,ni,o2p,r1;exiteeWos;ei3mbardiJston 1;glo1pizza;be;ng;ack & deckFo2ue c1;roX;ckbuster video,omingda1;le; g1g1;oodriN;cht3e ge0n & jer2rkshire hathaw1;ay;ryH;el;nana republ3s1xt5y5;f,kin robbi1;ns;ic;bXcSdidRerosmith,ig,lLmFnheuser-busEol,ppleAr7s3t&t,v2y1;er;is,on;hland2s1;n,ociated F; o1;il;by4g2m1;co;os; compu2bee1;'s;te1;rs;ch;c,d,erican3t1;!r1;ak; ex1;pre1;ss; 4catel2t1;air;!-luce1;nt;jazeera,qae1;da;as;/dc,a3er,t1;ivisi1;on;demy of scienc0;es;ba,c",
"Demonym": "true¦0:16;1:13;a0Wb0Nc0Cd0Ae09f07g04h02iYjVkTlPmLnIomHpDqatari,rBs7t5u4v3wel0Rz2;am0Fimbabwe0;enezuel0ietnam0H;g9krai1;aiwThai,rinida0Iu2;ni0Qrkmen;a4cot0Ke3ingapoOlovak,oma0Tpa05udRw2y0X;edi0Kiss;negal0Br08;mo0uU;o6us0Lw2;and0;a3eru0Hhilipp0Po2;li0Ertugu06;kist3lesti1na2raguay0;ma1;ani;amiZi2orweP;caragu0geri2;an,en;a3ex0Mo2;ngo0Erocc0;cedo1la2;gasy,y08;a4eb9i2;b2thua1;e0Dy0;o,t02;azakh,eny0o2uwaiti;re0;a2orda1;ma0Bp2;anN;celandic,nd4r2sraeli,ta02vo06;a2iT;ni0qi;i0oneV;aiDin2ondur0unN;di;amDe2hanai0reek,uatemal0;or2rm0;gi0;i2ren7;lipino,n4;cuadoVgyp6ngliJsto1thiopi0urope0;a2ominXut4;niH;a9h6o4roa3ub0ze2;ch;ti0;lom2ngol5;bi0;a6i2;le0n2;ese;lifor1m2na3;bo2eroo1;di0;angladeshi,el8o6r3ul2;gaG;aziBi2;ti2;sh;li2s1;vi0;aru2gi0;si0;fAl7merBngol0r5si0us2;sie,tr2;a2i0;li0;gent2me1;ine;ba1ge2;ri0;ni0;gh0r2;ic0;an",
"Possessive": "true¦anyAh5its,m3noCo1sometBthe0yo1;ir1mselves;ur0;!s;i8y0;!se4;er1i0;mse2s;!s0;!e0;lf;o1t0;hing;ne",
"Currency": "true¦$,aud,bScQdLeurKfJgbp,hkd,iIjpy,kGlEp8r7s3usd,x2y1z0¢,£,¥,ден,лв,руб,฿,₡,₨,€,₭,﷼;lotySł;en,uanR;af,of;h0t5;e0il5;k0q0;elM;iel,oubleLp,upeeL;e2ound st0;er0;lingI;n0soH;ceGn0;ies,y;e0i8;i,mpi7;n,r0wanzaCyatC;!onaBw;ls,nr;ori7ranc9;!o8;en3i2kk,o0;b0ll2;ra5;me4n0rham4;ar3;ad,e0ny;nt1;aht,itcoin0;!s",
"City": "true¦a2Wb26c1Wd1Re1Qf1Og1Ih1Ai18jakar2Hk0Zl0Tm0Gn0Co0ApZquiYrVsLtCuBv8w3y1z0;agreb,uri1Z;ang1Te0okohama;katerin1Hrev34;ars3e2i0rocl3;ckl0Vn0;nipeg,terth0W;llingt1Oxford;aw;a1i0;en2Hlni2Z;lenc2Uncouv0Gr2G;lan bat0Dtrecht;a6bilisi,e5he4i3o2rondheim,u0;nVr0;in,ku;kyo,ronIulouC;anj23l13miso2Jra2A; haJssaloni0X;gucigalpa,hr2Ol av0L;i0llinn,mpe2Bngi07rtu;chu22n2MpT;a3e2h1kopje,t0ydney;ockholm,uttga12;angh1Fenzh1X;o0KvZ;int peters0Ul3n0ppo1F; 0ti1B;jo0salv2;se;v0z0Q;adU;eykjavik,i1o0;me,sario,t25;ga,o de janei17;to;a8e6h5i4o2r0ueb1Qyongya1N;a0etor24;gue;rt0zn24; elizabe3o;ls1Grae24;iladelph1Znom pe07oenix;r0tah tik19;th;lerJr0tr10;is;dessa,s0ttawa;a1Hlo;a2ew 0is;delTtaip0york;ei;goya,nt0Upl0Uv1R;a5e4i3o1u0;mb0Lni0I;nt0scH;evideo,real;l1Mn01skolc;dellín,lbour0S;drid,l5n3r0;ib1se0;ille;or;chest0dalWi0Z;er;mo;a4i1o0vAy01;nd00s angel0F;ege,ma0nz,sbZverpo1;!ss0;ol; pla0Iusan0F;a5hark4i3laipeda,o1rak0uala lump2;ow;be,pavog0sice;ur;ev,ng8;iv;b3mpa0Kndy,ohsiu0Hra0un03;c0j;hi;ncheMstanb0̇zmir;ul;a5e3o0; chi mi1ms,u0;stI;nh;lsin0rakliG;ki;ifa,m0noi,va0A;bu0SiltD;alw4dan3en2hent,iza,othen1raz,ua0;dalaj0Gngzhou;bu0P;eUoa;sk;ay;es,rankfu0;rt;dmont4indhovU;a1ha01oha,u0;blRrb0Eshanbe;e0kar,masc0FugavpiJ;gu,je0;on;a7ebu,h2o0raioJuriti01;lo0nstanJpenhagNrk;gFmbo;enn3i1ristchur0;ch;ang m1c0ttagoL;ago;ai;i0lgary,pe town,rac4;ro;aHeBirminghWogoAr5u0;char3dap3enos air2r0sZ;g0sa;as;es;est;a2isba1usse0;ls;ne;silPtisla0;va;ta;i3lgrade,r0;g1l0n;in;en;ji0rut;ng;ku,n3r0sel;celo1ranquil0;la;na;g1ja lu0;ka;alo0kok;re;aBb9hmedabad,l7m4n2qa1sh0thens,uckland;dod,gabat;ba;k0twerp;ara;m5s0;terd0;am;exandr0maty;ia;idj0u dhabi;an;lbo1rh0;us;rg",
"Abbreviation": "true¦a0Tb0Qc0Kd0Ie0Ff0Cg0Ah08i06j04k02l00mRnOoNpIqHrFs9t6u5v2w0yb,µg;is0r,y0L;!c;a,b,e1i0ol,s,t;tro,vo;r,t;niv,safa,t;b1ce,d,e0sp;l,mp,nn,x;!l,sp;ask,e3fc,gt,i2q1r,s,t,u0;pt,rg;! ft;r,tu;c,nVp0;!t;b,d,e0;pSs,v;t,ue;a,d,enn3hd,l,p,r1s0t,vt;!eud;ef,o0;b,f,n;!a;ct,kla,nt,p,rd,z;e0ov;b0e;!r;a7b,d,essrs,g,i4l3m2p1rHs0t;!tr;h,s;!e;!le;!n1s0;c,ter;!n;!j,r,sc;at,b,it,lb,m,ng,t0x;!d;an6b,g,m0;!ph;an,d,r,u0;l,n;a,da,e,n0;c,f;g,on,r0wy,z;!s;a0b,en,ov;!l;e1ig,l0m,r,t,y;! oz,a;b,m;a,g,ng,s1tc,x0;!p;p,q,t;ak,e0g,ist,l,m,r;c,f,pt,t;a3ca,g,l,m2o0pl,res,t,yn;!l0mdr,nn,rp;!o;!dr;!l0pt;!if;a,c,l1r0;ig,os;!dg,vd;d4l3p2r1ss0tty,ug,ve;n,t;c,iz;prox,r,t;!ta;!j,m,v",
"Country": "true¦0:38;1:2L;a2Wb2Dc21d1Xe1Rf1Lg1Bh19i13j11k0Zl0Um0Gn05om3CpZqat1JrXsKtCu6v4wal3yemTz2;a24imbabwe;es,lis and futu2X;a2enezue31ietnam;nuatu,tican city;.5gTkraiZnited 3ruXs2zbeE;a,sr;arab emirat0Kkingdom,states2;! of am2X;k.,s.2; 27a.;a7haBimor-les0Bo6rinidad4u2;nis0rk2valu;ey,me2Xs and caic1T; and 2-2;toba1J;go,kel0Ynga;iw2Vji2nz2R;ki2T;aCcotl1eBi8lov7o5pa2Bri lanka,u4w2yr0;az2ed9itzerl1;il1;d2Qriname;lomon1Vmal0uth 2;afr2IkLsud2O;ak0en0;erra leoEn2;gapo1Wt maart2;en;negKrb0ychellY;int 2moa,n marino,udi arab0;hele24luc0mart1Z;epublic of ir0Com2Cuss0w2;an25;a3eHhilippinTitcairn1Ko2uerto riM;l1rtugE;ki2Bl3nama,pua new0Tra2;gu6;au,esti2;ne;aAe8i6or2;folk1Gth3w2;ay; k2ern mariana1B;or0M;caragua,ger2ue;!ia;p2ther18w zeal1;al;mib0u2;ru;a6exi5icro09o2yanm04;ldova,n2roc4zamb9;a3gol0t2;enegro,serrat;co;c9dagascZl6r4urit3yot2;te;an0i14;shall0Vtin2;ique;a3div2i,ta;es;wi,ys0;ao,ed00;a5e4i2uxembourg;b2echtenste10thu1E;er0ya;ban0Gsotho;os,tv0;azakh1De2iriba02osovo,uwait,yrgyz1D;eling0Jnya;a2erF;ma15p1B;c6nd5r3s2taly,vory coast;le of m19rael;a2el1;n,q;ia,oI;el1;aiSon2ungary;dur0Mg kong;aAermany,ha0Pibralt9re7u2;a5ern4inea2ya0O;!-biss2;au;sey;deloupe,m,tema0P;e2na0M;ce,nl1;ar;bTmb0;a6i5r2;ance,ench 2;guia0Dpoly2;nes0;ji,nl1;lklandTroeT;ast tim6cu5gypt,l salv5ngl1quatorial3ritr4st2thiop0;on0; guin2;ea;ad2;or;enmark,jibou4ominica3r con2;go;!n B;ti;aAentral african 9h7o4roat0u3yprQzech2; 8ia;ba,racao;c3lo2morPngo-brazzaville,okFsta r03te d'ivoiK;mb0;osD;i2ristmasF;le,na;republic;m2naTpe verde,yman9;bod0ero2;on;aFeChut00o8r4u2;lgar0r2;kina faso,ma,undi;azil,itish 2unei;virgin2; is2;lands;liv0nai4snia and herzegoviGtswaGuvet2; isl1;and;re;l2n7rmuF;ar2gium,ize;us;h3ngladesh,rbad2;os;am3ra2;in;as;fghaFlCmAn5r3ustr2zerbaijH;al0ia;genti2men0uba;na;dorra,g4t2;arct6igua and barbu2;da;o2uil2;la;er2;ica;b2ger0;an0;ia;ni2;st2;an",
"Region": "true¦0:1U;a20b1Sc1Id1Des1Cf19g13h10i0Xj0Vk0Tl0Qm0FnZoXpSqPrMsDtAut9v6w3y1zacatec22;o05u1;cat18kZ;a1est vi4isconsin,yomi14;rwick0shington1;! dc;er2i1;rgin1S;acruz,mont;ah,tar pradesh;a2e1laxca1DuscaA;nnessee,x1R;bas0Kmaulip1QsmJ;a6i4o2taf0Ou1ylh13;ffVrr00s0Y;me10no1Auth 1;cSdR;ber1Ic1naloa;hu0Sily;n2skatchew0Rxo1;ny; luis potosi,ta catari1I;a1hode7;j1ngp02;asth0Mshahi;inghai,u1;e1intana roo;bec,ensWreta0E;ara4e2rince edward1; isU;i,nnsylv1rnambu02;an14;!na;axa0Ndisha,h1klaho1Bntar1reg4x04;io;ayarit,eBo3u1;evo le1nav0L;on;r1tt0Rva scot0X;f6mandy,th1; 1ampton0;c3d2yo1;rk0;ako0Y;aroli0V;olk;bras0Xva01w1; 2foundland1;! and labrador;brunswick,hamp0jers1mexiJyork state;ey;a6i2o1;nta0Nrelos;ch3dlanBn2ss1;issippi,ouri;as geraGneso0M;igQoacQ;dhya,harasht04ine,ni3r1ssachusetts;anhao,y1;land;p1toba;ur;anca0e1incoln0ouis8;e1iH;ds;a1entucky,hul0A;ns08rnata0Dshmir;alis1iangxi;co;daho,llino2nd1owa;ia05;is;a2ert1idalEunA;ford0;mp0waii;ansu,eorgWlou5u1;an2erre1izhou,jarat;ro;ajuato,gdo1;ng;cester0;lori2uji1;an;da;sex;e4o2uran1;go;rs1;et;lawaErby0;a8ea7hi6o1umbrH;ahui4l3nnectic2rsi1ventry;ca;ut;iMorado;la;apEhuahua;ra;l8m1;bridge0peche;a5r4uck1;ingham0;shi1;re;emen,itish columb3;h2ja cal1sque,var2;iforn1;ia;guascalientes,l4r1;izo2kans1;as;na;a2ber1;ta;ba2s1;ka;ma",
"FemaleName": "true¦0:FY;1:G2;2:FR;3:FD;4:FC;5:FS;6:ER;7:EP;8:GF;9:EZ;A:GB;B:E5;C:G8;D:FO;E:FL;F:EG;aE2bD4cB8dAIe9Gf91g8Hh83i7Sj6Uk60l4Om38n2To2Qp2Fqu2Er1Os0Qt04ursu6vUwOyLzG;aJeHoG;e,la,ra;lGna;da,ma;da,ra;as7EeHol1TvG;et7onB9;le0sen3;an9endBNhiB4iG;lInG;if3AniGo0;e,f39;a,helmi0lGma;a,ow;aMeJiG;cHviG;an9XenG1;kCZtor3;da,l8Vnus,rG;a,nGoniD2;a,iDC;leGnesEC;nDLrG;i1y;aSePhNiMoJrGu6y4;acG3iGu0E;c3na,sG;h9Mta;nHrG;a,i;i9Jya;a5IffaCGna,s5;al3eGomasi0;a,l8Go6Xres1;g7Uo6WrHssG;!a,ie;eFi,ri8;bNliMmKnIrHs5tGwa0;ia0um;a,yn;iGya;a,ka,s5;a4e4iGmCAra;!ka;a,t5;at5it5;a05carlet2Ye04hUiSkye,oQtMuHyG;bFJlvi1;e,sHzG;an2Tet7ie,y;anGi8;!a,e,nG;aEe;aIeG;fGl3DphG;an2;cF8r6;f3nGphi1;d4ia,ja,ya;er4lv3mon1nGobh75;dy;aKeGirlBLo0y6;ba,e0i6lIrG;iGrBPyl;!d70;ia,lBV;ki4nIrHu0w0yG;la,na;i,leAon,ron;a,da,ia,nGon;a,on;l5Yre0;bMdLi9lKmIndHrGs5vannaE;aEi0;ra,y;aGi4;nt5ra;lBNome;e,ie;in1ri0;a02eXhViToHuG;by,thBK;bQcPlOnNsHwe0xG;an94ie,y;aHeGie,lC;ann8ll1marBFtB;!lGnn1;iGyn;e,nG;a,d7W;da,i,na;an9;hel53io;bin,erByn;a,cGkki,na,ta;helBZki;ea,iannDXoG;da,n12;an0bIgi0i0nGta,y0;aGee;!e,ta;a,eG;cARkaE;chGe,i0mo0n5EquCDvDy0;aCCelGi9;!e,le;een2ia0;aMeLhJoIrG;iGudenAW;scil1Uyamva9;lly,rt3;ilome0oebe,ylG;is,lis;arl,ggy,nelope,r6t4;ige,m0Fn4Oo6rvaBBtHulG;a,et7in1;ricGsy,tA8;a,e,ia;ctav3deHfAWlGphAW;a,ga,iv3;l3t7;aQePiJoGy6;eHrG;aEeDma;ll1mi;aKcIkGla,na,s5ta;iGki;!ta;hoB2k8BolG;a,eBH;!mh;l7Tna,risF;dIi5PnHo23taG;li1s5;cy,et7;eAiCO;a01ckenz2eViLoIrignayani,uriBGyG;a,rG;a,na,tAS;i4ll9XnG;a,iG;ca,ka,qB4;a,chOkaNlJmi,nIrGtzi;aGiam;!n9;a,dy,erva,h,n2;a,dIi9JlG;iGy;cent,e;red;!e6;ae6el3G;ag4KgKi,lHrG;edi61isFyl;an2iGliF;nGsAM;a,da;!an,han;b08c9Ed06e,g04i03l01nZrKtJuHv6Sx87yGz2;a,bell,ra;de,rG;a,eD;h75il9t2;a,cSgOiJjor2l6In2s5tIyG;!aGbe5QjaAlou;m,n9S;a,ha,i0;!aIbALeHja,lCna,sGt53;!a,ol,sa;!l06;!h,m,nG;!a,e,n1;arIeHie,oGr3Kueri7;!t;!ry;et3IiB;elGi61y;a,l1;dGon,ue6;akranBy;iGlo36;a,ka,n9;a,re,s2;daGg2;!l2W;alCd2elGge,isBGon0;eiAin1yn;el,le;a0Ie08iWoQuKyG;d3la,nG;!a,dHe9SnGsAQ;!a,e9R;a,sAO;aB1cJelIiFlHna,pGz;e,iB;a,u;a,la;iGy;a2Ae,l25n9;is,l1GrHtt2uG;el6is1;aIeHi8na,rG;a6Zi8;lei,n1tB;!in1;aQbPd3lLnIsHv3zG;!a,be4Ket7z2;a,et7;a,dG;a,sGy;ay,ey,i,y;a,iaIlG;iGy;a8Ge;!n4F;b7Terty;!n5R;aNda,e0iLla,nKoIslARtGx2;iGt2;c3t3;la,nGra;a,ie,o4;a,or1;a,gh,laG;!ni;!h,nG;a,d4e,n4N;cNdon7Si6kes5na,rMtKurIvHxGy6;mi;ern1in3;a,eGie,yn;l,n;as5is5oG;nya,ya;a,isF;ey,ie,y;aZeUhadija,iMoLrIyG;lGra;a,ee,ie;istGy5B;a,en,iGy;!e,n48;ri,urtn9A;aMerLl99mIrGzzy;a,stG;en,in;!berlG;eGi,y;e,y;a,stD;!na,ra;el6PiJlInHrG;a,i,ri;d4na;ey,i,l9Qs2y;ra,s5;c8Wi5XlOma6nyakumari,rMss5LtJviByG;!e,lG;a,eG;e,i78;a5EeHhGi3PlCri0y;ar5Cer5Cie,leDr9Fy;!lyn73;a,en,iGl4Uyn;!ma,n31sF;ei72i,l2;a04eVilToMuG;anKdJliGst56;aHeGsF;!nAt0W;!n8X;i2Ry;a,iB;!anLcelCd5Vel71han6IlJni,sHva0yG;a,ce;eGie;fi0lCph4X;eGie;en,n1;!a,e,n36;!i10lG;!i0Z;anLle0nIrHsG;i5Qsi5Q;i,ri;!a,el6Pif1RnG;a,et7iGy;!e,f1P;a,e72iHnG;a,e71iG;e,n1;cLd1mi,nHqueliAsmin2Uvie4yAzG;min8;a8eHiG;ce,e,n1s;!lGsFt06;e,le;inHk2lCquelG;in1yn;da,ta;lPmNnMo0rLsHvaG;!na;aHiGob6U;do4;!belGdo4;!a,e,l2G;en1i0ma;a,di4es,gr5R;el9ogG;en1;a,eAia0o0se;aNeKilHoGyacin1N;ll2rten1H;aHdGlaH;a,egard;ry;ath0WiHlGnrietBrmiAst0W;en24ga;di;il75lKnJrGtt2yl75z6D;iGmo4Fri4G;etG;!te;aEnaE;ey,l2;aYeTiOlMold12rIwG;enGyne18;!dolC;acHetGisel9;a,chD;e,ieG;!la;adys,enGor3yn1Y;a,da,na;aJgi,lHna,ov71selG;a,e,le;da,liG;an;!n0;mYnIorgHrG;ald35i,m2Stru73;et7i5T;a,eGna;s1Nvieve;briel3Fil,le,rnet,yle;aReOio0loMrG;anHe9iG;da,e9;!cG;esHiGoi0G;n1s3V;!ca;!rG;a,en43;lHrnG;!an9;ec3ic3;rHtiGy8;ma;ah,rah;d0FileDkBl00mUn4ArRsMtLuKvG;aIelHiG;e,ta;in0Ayn;!ngel2H;geni1la,ni3R;h52ta;meral9peranJtG;eHhGrel6;er;l2Pr;za;iGma,nest29yn;cGka,n;a,ka;eJilImG;aGie,y;!liA;ee,i1y;lGrald;da,y;aTeRiMlLma,no4oJsIvG;a,iG;na,ra;a,ie;iGuiG;se;a,en,ie,y;a0c3da,nJsGzaH;aGe;!beG;th;!a,or;anor,nG;!a;in1na;en,iGna,wi0;e,th;aWeKiJoGul2U;lor51miniq3Yn30rGtt2;a,eDis,la,othGthy;ea,y;an09naEonAx2;anPbOde,eNiLja,lImetr3nGsir4U;a,iG;ce,se;a,iHla,orGphiA;es,is;a,l5J;dGrdG;re;!d4Mna;!b2CoraEra;a,d4nG;!a,e;hl3i0mMnKphn1rHvi1WyG;le,na;a,by,cHia,lG;a,en1;ey,ie;a,et7iG;!ca,el1Aka;arGia;is;a0Qe0Mh04i02lUoJrHynG;di,th3;istGy04;al,i0;lOnLrHurG;tn1D;aId28iGn28riA;!nG;a,e,n1;!l1S;n2sG;tanGuelo;ce,za;eGleD;en,t7;aIeoHotG;il4B;!pat4;ir8rIudG;et7iG;a,ne;a,e,iG;ce,sX;a4er4ndG;i,y;aPeMloe,rG;isHyG;stal;sy,tG;aHen,iGy;!an1e,n1;!l;lseHrG;!i8yl;a,y;nLrG;isJlHmG;aiA;a,eGot7;n1t7;!sa;d4el1PtG;al,el1O;cHlG;es7i3F;el3ilG;e,ia,y;iYlXmilWndVrNsLtGy6;aJeIhGri0;erGleDrCy;in1;ri0;li0ri0;a2GsG;a2Fie;a,iMlKmeIolHrG;ie,ol;!e,in1yn;lGn;!a,la;a,eGie,y;ne,y;na,sF;a0Di0D;a,e,l1;isBl2;tlG;in,yn;arb0CeYianXlVoTrG;andRePiIoHyG;an0nn;nwCok8;an2NdgKg0ItG;n27tG;!aHnG;ey,i,y;ny;etG;!t8;an0e,nG;da,na;i8y;bbi8nG;iBn2;ancGossom,ythe;a,he;ca;aRcky,lin9niBrNssMtIulaEvG;!erlG;ey,y;hHsy,tG;e,i0Zy8;!anG;ie,y;!ie;nGt5yl;adHiG;ce;et7iA;!triG;ce,z;a4ie,ra;aliy29b24d1Lg1Hi19l0Sm0Nn01rWsNthe0uJvIyG;anGes5;a,na;a,r25;drIgusHrG;el3;ti0;a,ey,i,y;hHtrG;id;aKlGt1P;eHi8yG;!n;e,iGy;gh;!nG;ti;iIleHpiB;ta;en,n1t7;an19elG;le;aYdWeUgQiOja,nHtoGya;inet7n3;!aJeHiGmI;e,ka;!mGt7;ar2;!belHliFmT;sa;!le;ka,sGta;a,sa;elGie;a,iG;a,ca,n1qG;ue;!t7;te;je6rea;la;!bHmGstas3;ar3;el;aIberHel3iGy;e,na;!ly;l3n9;da;aTba,eNiKlIma,yG;a,c3sG;a,on,sa;iGys0J;e,s0I;a,cHna,sGza;a,ha,on,sa;e,ia;c3is5jaIna,ssaIxG;aGia;!nd4;nd4;ra;ia;i0nHyG;ah,na;a,is,naE;c5da,leDmLnslKsG;haElG;inGyW;g,n;!h;ey;ee;en;at5g2nG;es;ie;ha;aVdiSelLrG;eIiG;anLenG;a,e,ne;an0;na;aKeJiHyG;nn;a,n1;a,e;!ne;!iG;de;e,lCsG;on;yn;!lG;iAyn;ne;agaJbHiG;!gaI;ey,i8y;!e;il;ah",
"Place": "true¦a07b05cZdYeXfVgRhQiOjfk,kMlKmHneEoDp9que,rd,s8t5u4v3w0yyz;is1y0;!o;!c;a,t;pYsafa,t;e1he 0;bronx,hamptons;nn,x;ask,fo,oho,t,under6yd;a2e1h0;l,x;k,nnK;!cifX;kla,nt;b1w eng0;land;!r;a1co,i0t,uc;dKnn;libu,nhattS;a0gw,hr;s,x;an0ul;!s;a0cn,da,ndianMst;!x;arlem,kg,nd,wy;a2re0;at 0enwich;britain,lak6;!y village;co,l0ra;!a;urope,verglad2;ak,en,fw,ist,own4xb;al4dg,gk,hina3l2o1r0t;es;lo,nn;!t;town;!if;cn,e0kk,lvd,rooklyn;l air,verly hills;frica,lta,m5ntarct2r1sia,tl0ve;!ant1;ct0iz;ic0; oce0;an;ericas,s",
"WeekDay": "true¦fri2mon2s1t0wednesd3;hurs1ues1;aturd1und1;!d0;ay0;!s",
"Month": "true¦aBdec9feb7j2mar,nov9oct1sep0;!t8;!o8;an3u0;l1n0;!e;!y;!u1;!ru0;ary;!em0;ber;pr1ug0;!ust;!il",
"Date": "true¦ago,t0weekend,yesterd2;mr2o0;d0morrow;ay;!w",
"FirstName": "true¦aEblair,cCdevBj8k6lashawn,m3nelly,quinn,re2sh0;ay,e0iloh;a,lby;g1ne;ar1el,org0;an;ion,lo;as8e0r9;ls7nyatta,rry;am0ess1ude;ie,m0;ie;an,on;as0heyenne;ey,sidy;lex1ndra,ubr0;ey;is",
"LastName": "true¦0:34;1:3B;2:39;3:2Y;4:2E;5:30;a3Bb31c2Od2Ee2Bf25g1Zh1Pi1Kj1Ek17l0Zm0Nn0Jo0Gp05rYsMtHvFwCxBy8zh6;a6ou,u;ng,o;a6eun2Uoshi1Kun;ma6ng;da,guc1Zmo27sh21zaR;iao,u;a7eb0il6o3right,u;li3Bs2;gn0lk0ng,tanabe;a6ivaldi;ssilj37zqu1;a9h8i2Go7r6sui,urn0;an,ynisJ;lst0Prr1Uth;at1Uomps2;kah0Vnaka,ylor;aEchDeChimizu,iBmiAo9t7u6zabo;ar1lliv2AzuE;a6ein0;l23rm0;sa,u3;rn4th;lva,mmo24ngh;mjon4rrano;midt,neid0ulz;ito,n7sa6to;ki;ch1dLtos,z;amBeag1Zi9o7u6;bio,iz,sD;b6dri1MgIj0Tme24osevelt,ssi,ux;erts,ins2;c6ve0F;ci,hards2;ir1os;aEeAh8ic6ow20ut1N;as6hl0;so;a6illips;m,n1T;ders5et8r7t6;e0Nr4;ez,ry;ers;h21rk0t6vl4;el,te0J;baBg0Blivei01r6;t6w1O;ega,iz;a6eils2guy5ix2owak,ym1E;gy,ka6var1K;ji6muW;ma;aEeCiBo8u6;ll0n6rr0Bssolini,ñ6;oz;lina,oKr6zart;al0Me6r0U;au,no;hhail4ll0;rci0ssi6y0;!er;eWmmad4r6tsu07;in6tin1;!o;aCe8i6op1uo;!n6u;coln,dholm;fe7n0Qr6w0J;oy;bv6v6;re;mmy,rs5u;aBennedy,imuAle0Lo8u7wo6;k,n;mar,znets4;bay6vacs;asY;ra;hn,rl9to,ur,zl4;aAen9ha3imen1o6u3;h6nYu3;an6ns2;ss2;ki0Es5;cks2nsse0D;glesi9ke8noue,shik7to,vano6;u,v;awa;da;as;aBe8itchcock,o7u6;!a3b0ghNynh;a3ffmann,rvat;mingw7nde6rN;rs2;ay;ns5rrQs7y6;asDes;an4hi6;moJ;a9il,o8r7u6;o,tierr1;ayli3ub0;m1nzal1;nd6o,rcia;hi;erAis9lor8o7uj6;ita;st0urni0;es;ch0;nand1;d7insteHsposi6vaL;to;is2wards;aCeBi9omin8u6;bo6rand;is;gu1;az,mitr4;ov;lgado,vi;nkula,rw7vi6;es,s;in;aFhBlarkAo6;h5l6op0rbyn,x;em7li6;ns;an;!e;an8e7iu,o6ristens5u3we;i,ng,u3w,y;!n,on6u3;!g;mpb7rt0st6;ro;ell;aBe8ha3lanco,oyko,r6yrne;ooks,yant;ng;ck7ethov5nnett;en;er,ham;ch,h8iley,rn6;es,i0;er;k,ng;dDl9nd6;ers6rA;en,on,s2;on;eks7iy8var1;ez;ej6;ev;ams",
"MaleName": "true¦0:CE;1:BL;2:C2;3:BT;4:B5;5:BZ;6:AT;7:9V;8:BD;9:AX;A:AO;aB4bA8c97d87e7Gf6Yg6Gh5Wi5Ij4Lk4Bl3Rm2Pn2Eo28p22qu20r1As0Qt06u05v00wNxavi3yGzB;aBor0;cBh8Ine;hCkB;!aB1;ar51eB0;ass2i,oCuB;sDu25;nEsDusB;oBsC;uf;ef;at0g;aJeHiCoByaAP;lfgang,odrow;lBn1O;bDey,frBJlB;aA5iB;am,e,s;e89ur;i,nde7sB;!l6t1;de,lCrr5yB;l1ne;lBt3;a93y;aEern1iBladimir;cCha0kt5CnceBrg9Bva0;!nt;ente,t5A;lentin49n8Yughn;lyss4Msm0;aTeOhKiIoErCyB;!l3ro8s1;av9QeBist0oy,um0;nt9Iv54y;bDd7XmBny;!as,mBoharu;aAYie,y;i83y;mBt9;!my,othy;adDeoCia7DomB;!as;!do7M;!de9;dErB;en8HrB;an8GeBy;ll,n8F;!dy;dgh,ic9Tnn3req,ts45;aRcotPeNhJiHoFpenc3tBur1Oylve8Hzym1;anDeBua7B;f0phAFvBwa7A;e57ie;!islaw,l6;lom1nA3uB;leyma8ta;dBl7Jm1;!n6;aDeB;lBrm0;d1t1;h6Sne,qu0Uun,wn,y8;aBbasti0k1Xl41rg40th,ymo9I;m9n;!tB;!ie,y;lCmBnti21q4Iul;!mAu4;ik,vato6V;aWeShe92iOoFuCyB;an,ou;b6LdCf9pe6QssB;!elAI;ol2Uy;an,bIcHdGel,geFh0landA9mEnDry,sCyB;!ce;coe,s;!a95nA;an,eo;l3Jr;e4Qg3n6olfo,ri68;co,ky;bAe9U;cBl6;ar5Oc5NhCkBo;!ey,ie,y;a85ie;gCid,ub5x,yBza;ansh,nS;g8WiB;na8Ss;ch5Yfa4lDmCndBpha4sh6Uul,ymo70;al9Yol2By;i9Ion;f,ph;ent2inB;cy,t1;aFeDhilCier62ol,reB;st1;!ip,lip;d9Brcy,tB;ar,e2V;b3Sdra6Ft44ul;ctav2Vliv3m96rFsCtBum8Uw5;is,to;aCc8SvB;al52;ma;i,l49vJ;athJeHiDoB;aBel,l0ma0r2X;h,m;cCg4i3IkB;h6Uola;hol5XkBol5X;!ol5W;al,d,il,ls1vB;il50;anBy;!a4i4;aWeTiKoFuCyB;l21r1;hamCr5ZstaB;fa,p4G;ed,mF;dibo,e,hamDis1XntCsBussa;es,he;e,y;ad,ed,mB;ad,ed;cGgu4kElDnCtchB;!e7;a78ik;house,o03t1;e,olB;aj;ah,hBk6;a4eB;al,l;hClv2rB;le,ri7v2;di,met;ck,hNlLmOnu4rHs1tDuricCxB;!imilian8Cwe7;e,io;eo,hCi52tB;!eo,hew,ia;eBis;us,w;cDio,k86lCqu6Gsha7tBv2;i2Hy;in,on;!el,oKus;achBcolm,ik;ai,y;amBdi,moud;adB;ou;aReNiMlo2RoIuCyB;le,nd1;cEiDkBth3;aBe;!s;gi,s;as,iaB;no;g0nn6RrenDuBwe7;!iB;e,s;!zo;am,on4;a7Bevi,la4SnDoBst3vi;!nB;!a60el;!ny;mCnBr67ur4Twr4T;ce,d1;ar,o4N;aIeDhaled,iBrist4Vu48y3B;er0p,rB;by,k,ollos;en0iEnBrmit,v2;!dCnBt5C;e0Yy;a7ri4N;r,th;na68rBthem;im,l;aYeQiOoDuB;an,liBst2;an,o,us;aqu2eJhnInGrEsB;eChBi7Bue;!ua;!ph;dBge;an,i,on;!aBny;h,s,th4X;!ath4Wie,nA;!l,sBy;ph;an,e,mB;!mA;d,ffGrDsB;sBus;!e;a5JemCmai8oBry;me,ni0O;i6Uy;!e58rB;ey,y;cHd5kGmFrDsCvi3yB;!d5s1;on,p3;ed,od,rBv4M;e4Zod;al,es,is1;e,ob,ub;k,ob,quB;es;aNbrahMchika,gKkeJlija,nuIrGsDtBv0;ai,sB;uki;aBha0i6Fma4sac;ac,iaB;h,s;a,vinBw2;!g;k,nngu52;!r;nacBor;io;im;in,n;aJeFina4VoDuByd56;be25gBmber4CsD;h,o;m3ra33sBwa3X;se2;aDctCitCn4ErB;be20m0;or;th;bKlJmza,nIo,rDsCyB;a43d5;an,s0;lEo4FrDuBv6;hi40ki,tB;a,o;is1y;an,ey;k,s;!im;ib;aQeMiLlenKoIrEuB;illerCsB;!tavo;mo;aDegBov3;!g,orB;io,y;dy,h57nt;nzaBrd1;lo;!n;lbe4Qno,ovan4R;ne,oDrB;aBry;ld,rd4U;ffr6rge;bri4l5rBv2;la1Zr3Eth,y;aReNiLlJorr0IrB;anDedBitz;!dAeBri24;ri23;cDkB;!ie,lB;in,yn;esJisB;!co,zek;etch3oB;yd;d4lBonn;ip;deriDliCng,rnB;an01;pe,x;co;bi0di;arZdUfrTit0lNmGnFo2rCsteb0th0uge8vBym5zra;an,ere2V;gi,iCnBrol,v2w2;est45ie;c07k;och,rique,zo;aGerFiCmB;aFe2P;lCrB;!h0;!io;s1y;nu4;be09d1iEliDmCt1viBwood;n,s;er,o;ot1Ts;!as,j43sB;ha;a2en;!dAg32mEuCwB;a25in;arB;do;o0Su0S;l,nB;est;aYeOiLoErDuCwByl0;ay8ight;a8dl6nc0st2;ag0ew;minFnDri0ugCyB;le;!l03;!a29nBov0;e7ie,y;go,icB;!k;armuCeBll1on,rk;go;id;anIj0lbeHmetri9nFon,rEsDvCwBxt3;ay8ey;en,in;hawn,mo08;ek,ri0F;is,nBv3;is,y;rt;!dB;re;lKmInHrDvB;e,iB;!d;en,iDne7rByl;eBin,yl;l2Vn;n,o,us;!e,i4ny;iBon;an,en,on;e,lB;as;a06e04hWiar0lLoGrEuCyrB;il,us;rtB;!is;aBistobal;ig;dy,lEnCrB;ey,neli9y;or,rB;ad;by,e,in,l2t1;aGeDiByI;fBnt;fo0Ct1;meCt9velaB;nd;nt;rDuCyB;!t1;de;enB;ce;aFeErisCuB;ck;!tB;i0oph3;st3;d,rlBs;eBie;s,y;cBdric,s11;il;lEmer1rB;ey,lCro7y;ll;!os,t1;eb,v2;ar02eUilTlaSoPrCuByr1;ddy,rtI;aJeEiDuCyB;an,ce,on;ce,no;an,ce;nCtB;!t;dCtB;!on;an,on;dCndB;en,on;!foBl6y;rd;bCrByd;is;!by;i8ke;al,lA;nFrBshoi;at,nCtB;!r10;aBie;rd0S;!edict,iCjam2nA;ie,y;to;n6rBt;eBy;tt;ey;ar0Xb0Nd0Jgust2hm0Gid5ja0ElZmXnPputsiOrFsaEuCveBya0ziz;ry;gust9st2;us;hi;aIchHi4jun,maFnDon,tBy0;hBu06;ur;av,oB;ld;an,nd0A;el;ie;ta;aq;dGgel05tB;hoEoB;i8nB;!i02y;ne;ny;reBy;!as,s,w;ir,mBos;ar;an,beOd5eIfFi,lEonDphonHt1vB;aMin;on;so,zo;an,en;onCrB;edP;so;c,jaEksandDssaExB;!and3;er;ar,er;ndB;ro;rtH;ni;en;ad,eB;d,t;in;aColfBri0vik;!o;mBn;!a;dFeEraCuB;!bakr,lfazl;hBm;am;!l;allEel,oulaye,ulB;!lCrahm0;an;ah,o;ah;av,on",
"Person": "true¦ashton kutchSbRcMdKeIgastNhGinez,jEkDleCmBnettJoAp8r4s3t2v0;a0irgin maG;lentino rossi,n go3;heresa may,iger woods,yra banks;addam hussain,carlett johanssJlobodan milosevic,uB;ay romano,eese witherspoIo1ush limbau0;gh;d stewart,nald0;inho,o;a0ipJ;lmIris hiltD;prah winfrFra;essiaen,itt romnEubarek;bron james,e;anye west,iefer sutherland,obe bryant;aime,effers8k rowli0;ng;alle ber0itlBulk hogan;ry;ff0meril lagasse,zekiel;ie;a0enzel washingt2ick wolf;lt1nte;ar1lint0ruz;on;dinal wols1son0;! palm2;ey;arack obama,rock;er",
"Verb": "true¦awak9born,cannot,fr8g7h5k3le2m1s0wors9;e8h3;ake sure,sg;ngth6ss6;eep tabs,n0;own;as0e2;!t2;iv1onna;ight0;en",
"PhrasalVerb": "true¦0:72;1:6Q;2:7E;3:74;4:6J;5:7H;6:76;7:6P;8:6C;9:6D;A:5I;B:71;C:70;a7Hb63c5Dd5Ae58f46g3Oh38iron0j34k2Zl2Km2Bn29o27p1Pr1Es09tQuOvacuum 1wGyammerCzD;eroAip EonD;e0k0;by,up;aJeGhFiEorDrit53;d 1k2R;mp0n4Ape0r8s8;eel Bip 7L;aEiD;gh 06rd0;n Br 3D;it 5Kk8lk6rm 0Qsh 74t67v4P;rgeCsD;e 9herA;aRePhNiJoHrFuDype 0N;ckArn D;d2in,o3Gup;ade YiDot0y 28;ckle68p 7A;ne67p Ds4D;d2o6Lup;ck FdEe Dgh5Tme0p o0Dre0;aw3ba4d2in,up;e5Ky 1;by,o6V;ink Drow 5V;ba4ov7up;aDe 4Ill4O;m 1r W;ckCke Elk D;ov7u4O;aDba4d2in,o31up;ba4ft7p4Tw3;a0Gc0Fe09h05i02lYmXnWoVpSquare RtJuHwD;earFiD;ngEtch D;aw3ba4o6P; by;ck Dit 1m 1ss0;in,up;aIe0RiHoFrD;aigh1MiD;ke 5Yn2Y;p Drm1P;by,in,o6B;n2Zr 1tc3I;c2Ymp0nd Dr6Hve6y 1;ba4d2up;d2o67up;ar2Vell0ill4UlErDurC;ingCuc8;a33it 3U;be4Crt0;ap 4Eow B;ash 4Zoke0;eep EiDow 9;c3Np 1;in,oD;ff,v7;gn Eng2Zt Dz8;d2o5up;in,o5up;aFoDu4F;ot Dut0w 5X;aw3ba4f37o5R;c2FdeAk4Sve6;e Hll0nd GtD; Dtl43;d2in,o5upD;!on;aw3ba4d2in,o1Yup;o5to;al4Lout0rap4L;il6v8;at0eKiJoGuD;b 4Ele0n Dstl8;aDba4d2in53o3Gt30u3E;c1Xw3;ot EuD;g2Knd6;a1Xf2Ro5;ng 4Op6;aDel6inAnt0;c4Yd D;o2Tu0C;aQePiOlMoKrHsyc2AuD;ll Ft D;aDba4d2in,o1Ht34up;p39w3;ap38d2in,o5t32up;attleCess EiGoD;p 1;ah1Hon;iDp 53re3Mur45wer 53;nt0;ay3ZuD;gAmp 9;ck 53g0leCn 9p3W;el 47ncilA;c3Pir 2In0ss FtEy D;ba4o4R; d2c1Y;aw3ba4o12;pDw3K;e3Jt B;arrow3Terd0oD;d6te3S;aJeHiGoEuD;ddl8ll37;c17p 1uth6ve D;al3Bd2in,o5up;ss0x 1;asur8lt 9ss D;a1Aup;ke Dn 9r30s1Lx0;do,o3Yup;aPeNiIoDuck0;a17c37g GoDse0;k Dse35;aft7ba4d2forw2Bin3Wov7uD;nd7p;in,o0J;e GghtFnEsDv1T;ten 4D;e 1k 1; 1e2Y;ar43d2;av1Ht 2YvelD; o3L;p 1sh DtchCugh6y1U;in3Lo5;eEick6nock D;d2o3H;eDyA;l2Hp D;aw3ba4d2fSin,o05to,up;aFoEuD;ic8mpA;ke2St2W;c31zz 1;aPeKiHoEuD;nker2Ts0U;lDneArse2O;d De 1;ba4d2fast,oZup;de Et D;ba4on,up;aw3o5;aDlp0;d Fl22r Dt 1;fDof;rom;in,oRu1A;cZm 1nDve it,ze1Y;d Dg 27kerF;d2in,o5;aReLive Jloss1VoFrEunD; f0M;in39ow 23; Dof 0U;aEb17it,oDr35t0Ou12;ff,n,v7;bo5ft7hJw3;aw3ba4d2in,oDup,w3;ff,n,ut;a17ek0t D;aEb11d2oDr2Zup;ff,n,ut,v7;cEhDl1Pr2Xt,w3;ead;ross;d aEnD;g 1;bo5;a08e01iRlNoJrFuD;cDel 1;k 1;eEighten DownCy 1;aw3o2L;eDshe1G; 1z8;lFol D;aDwi19;bo5r2I;d 9;aEeDip0;sh0;g 9ke0mDrD;e 2K;gLlJnHrFsEzzD;le0;h 2H;e Dm 1;aw3ba4up;d0isD;h 1;e Dl 11;aw3fI;ht ba4ure0;eInEsD;s 1;cFd D;fDo1X;or;e B;dQl 1;cHll Drm0t0O;apYbFd2in,oEtD;hrough;ff,ut,v7;a4ehi1S;e E;at0dge0nd Dy8;o1Mup;o09rD;ess 9op D;aw3bNin,o15;aShPlean 9oDross But 0T;me FoEuntD; o1M;k 1l6;aJbIforGin,oFtEuD;nd7;ogeth7;ut,v7;th,wD;ard;a4y;pDr19w3;art;eDipA;ck BeD;r 1;lJncel0rGsFtch EveA; in;o16up;h Bt6;ry EvD;e V;aw3o12;l Dm02;aDba4d2o10up;r0Vw3;a0He08l01oSrHuD;bbleFcklTilZlEndlTrn 05tDy 10zz6;t B;k 9; ov7;anMeaKiDush6;ghHng D;aEba4d2forDin,o5up;th;bo5lDr0Lw3;ong;teD;n 1;k D;d2in,o5up;ch0;arKgJil 9n8oGssFttlEunce Dx B;aw3ba4;e 9; ar0B;k Bt 1;e 1;d2up; d2;d 1;aIeed0oDurt0;cFw D;aw3ba4d2o5up;ck;k D;in,oK;ck0nk0st6; oJaGef 1nd D;d2ov7up;er;up;r0t D;d2in,oDup;ff,ut;ff,nD;to;ck Jil0nFrgEsD;h B;ainCe B;g BkC; on;in,o5; o5;aw3d2o5up;ay;cMdIsk Fuction6; oD;ff;arDo5;ouD;nd;d D;d2oDup;ff,n;own;t D;o5up;ut",
"Modal": "true¦c5lets,m4ought3sh1w0;ill,o5;a0o4;ll,nt;! to;ay,ight,ust;an,o0;uld",
"Adjective": "true¦0:73;1:7I;2:7O;3:7H;4:7A;5:5B;6:4R;7:49;8:48;9:7F;A:60;a6Eb60c5Md52e4Pf45g3Xh3Mi31j2Zk2Yl2Nm2Cn23o1Np16quack,r0Ws0Ct05uMvJwByear5;arp0eFholeEiDoB;man5oBu67;d69zy;despr6Zs5B;!sa7;eClBste22;co1El o4H;!k5;aCiBola47;b7Nce versa,ol50;ca2gabo5Ynilla;ltSnFpCrb55su4tterB;!mo6U; f30b1KpCsBti1D;ca7et,ide dItairs;er,i3J;aLbeco6Lconvin23deIeHfair,ivers4knGprecedUrEsCwB;iel1Writt5U;i1RuB;pervis0specti3;eBu5;cognHgul6Bl6B;own;ndi3v5Oxpect0;cid0rB;!grou5JsB;iz0tood;b7ppeaHssu6AuthorB;iz0;i20ra;aFeDhough4KoCrB;i1oubl0;geth6p,rp6B;en5LlBm4Vrr2Q;li3;boo,lBn;ent0;aTcSeQhPiNmug,nobbi3AoLpKqueami3AtFuBymb5Y;bDi gener50pBrprisi3;erBre0H;! dup6b,i25;du0seq4P;anda6OeEi0LrBy34;aightBip0; fBfB;or56;adfa5Wreotyp0;a4Uec2Cir1Flend5Wot on; call0le,mb6phist1TrBu0Tvi3X;d5Ury;gnifica2nB;ce4Qg7;am2Le6ocki3ut;cBda1em5lfi2Uni1Spa63re8;o1Cr3R;at53ient24reec53;cr0me,ns serif;aIeEiCoB;bu5Ktt4PuOy4;ghtBv4;!-25fA;ar,bel,condi1du5Xfres4XlDpublic3RsBtard0;is43oB;lu1na2;e1Auc41;b5EciB;al,st;aMeKicayu8lac5Copuli5BrCuB;bl54mp0;eFiCoB;!b06fu5Cmi2Xp6;mCor,sBva1;ti8;a4Re;ci58mB;a0EiB;er,um;ac1WrBti1;fe9ma2Pplexi3v2Z;rBst;allelDtB;-tiBi4;me;!ed;bMffKkJld fashion0nIpHrg1Dth6utGvB;al,erB;!aDniCt,wB;eiBrouB;ght;ll;do0Rer,g2Hsi41;en,posi1; boa5Ag2Fli8;!ay; gua58bBli8;eat;eDsB;cBer0Dole1;e8u3F;d2Ose;ak0eIiHoBua4J;nFrCtB;ab7;thB;!eB;rn;chala2descri4Ustop;ght5;arby,cessa3Sighbor5xt;aJeHiEoBultip7;bi7derClBnth5ot,st;dy;a1n;nBx0;iaBor;tu2Y;di49naBre;ci3;cBgenta,in,jZkeshift,le,mmoth,ny,sculi8;ab2Uho;aKeFiCoBu0Z;uti0Yvi3;mCteraB;l,te;it0;ftEgBth4;al,eCitiB;ma1;nda38;!-08;ngu3Lst,tt6;ap1Oind5no06;agg0uB;niKstifi0veni7;de4gno46lleg4mOnDpso 1RrB;a1releB;va2; JaIbr0corHdFfluenPiPnEsDtB;a9en3GoxB;ic31;a8i2N;a1er,oce2;iCoB;or;re9;deq3Eppr2T;fBsitu,vitro;ro2;mFpB;arDerfe9oBrop6;li1rtB;a2ed;ti4;eBi0M;d2Ln30;aGelFiDoBumdr36;ne2Uok0rrBs03ur5;if2N;ghfalut1KspB;an2L;liVpfA;lEnDrB;d01roB;wi3;dy,gi3;f,low0;ainfAener2Eiga1YlHoGraDuB;ilBng ho;ty;cCtB;efAis;efA;ne,od;ea28ob4;aQeKinJlIoDrB;a1PeBoz1G;e28q0YtfA;oDrB; keeps,eBm6tuna1;g00ign;liB;sh;ag2Uue2;al,i1;dFmCrB;ti7;a7ini8;ne;le; up;bl0i2l20r Cux,voB;ri1uri1;oBreac1A;ff;aJfficie2lImiHnFre9there4veExB;a9cess,pe1JtraCuB;be2Gl0D;!va19;n,ryday; Bcouragi3ti0M;rou1sui1;ne2;abo1YdMe14i1;g6sB;t,ygB;oi3;er;aReJiDoBrea11ue;mina2ne,ubB;le,tfA;dact16fficu1JsCvB;er1F;creDeas0gruntl0hone1AordCtB;a2ress0;er5;et; HadpGfFgene1KliDrang0spe1KtCvoB;ut;ail0ermin0;be1Hca1ghB;tfA;ia2;an;facto;i5magBngeroVs0E;ed,i3;ly;ertaNhief,ivil,oDrB;aBowd0u0D;mp0vYz0;loJmHnCoi3rrBve0K;e9u1D;cre1grEsDtB;emBra0B;po09;ta2;ue2;mer04pleB;te,x;ni4ss4;in;aLeHizarGlFoCrB;and new,isk,okL;gCna fiSttom,urgeoB;is;us;ank,iE;re;autifAhiClov0nBst,yoC;eRt;nd;ul;ckCnkru0SrrB;en;!wards; priori,b0Ic0Fd05fra04g00hZlUma01ntiquTppQrKsIttracti02utheHvEwB;aCkB;wa0P;ke,re;ant garCerB;age;de;ntQ;leep,tonisB;hi3;ab,bitEroDtiB;fiB;ci4;ga2;raB;ry;are2etiLrB;oprB;ia1;at0;arEcohCeBiIl,oof;rt;olB;ic;mi3;ead;ainDgressiConiB;zi3;ve;st;id; IeGuFvB;aCerB;se;nc0;ed;lt;pt,qB;ua1;hoc,infinitB;um;cuCtu4u1;al;ra1;erLlKoIruHsCuB;nda2;e2oCtra9;ct;lu1rbi3;ng;te;pt;aBve;rd;aze,e;ra2;nt",
"Comparable": "true¦0:3Z;1:4G;2:43;3:49;4:3V;5:2W;a4Mb42c3Md3Be33f2Pg2Dh22i1Tj1Sk1Pl1Hm1Bn15o13p0Tqu0Rr0IsRtKuIvFw7y6za11;ell25ou3;aBe9hi1Wi7r6;o3y;ck0Mde,l6n1ry,se;d,y;a6i4Kt;k,ry;n1Rr6sI;m,y;a7e6ulgar;nge4rda2xi3;gue,in,st;g0n6pco3Kse4;like0ti1;aAen9hi8i7ough,r6;anqu2Oen1ue;dy,g3Sme0ny,r09;ck,n,rs2P;d40se;ll,me,rt,s6wd45;te4;aVcarUeThRiQkin0FlMmKoHpGqua1FtAu7w6;eet,ift;b7dd13per0Gr6;e,re2H;sta2Ft5;aAe9iff,r7u6;pXr1;a6ict,o3;ig3Fn0U;a1ep,rn;le,rk;e22i3Fright0;ci28ft,l7o6re,ur;n,thi3;emn,id;a6el0ooth;ll,rt;e8i6ow,y;ck,g35m6;!y;ek,nd3D;ck,l0mp5;a6iTort,rill,y;dy,ll0Xrp;cu0Rve0Rxy;ce,ed,y;d,fe,int0l1Vv14;aBe9i8o6ude;mantic,o1Isy,u6;gh,nd;ch,pe,tzy;a6d,mo0H;dy,l;gg7ndom,p6re,w;id;ed;ai2i6;ck,et;aEhoDi1QlCoBr8u6;ny,r6;e,p5;egna2ic7o6;fouYud;ey,k0;li04or,te1B;ain,easa2;ny;in4le;dd,f6i0ld,ranQ;fi10;aAe8i7o6;b5isy,rm15sy;ce,mb5;a6w;r,t;ive,rr01;aAe8ild,o7u6;nda19te;ist,o1;a6ek,llX;n,s0ty;d,tuQ;aBeAi9o6ucky;f0Un7o1Du6ve0w17y0T;d,sy;e0g;g1Tke0tt5ve0;an,wd;me,r6te;ge;e7i6;nd;en;ol0ui1P;cy,ll,n6;sBt6;e6ima8;llege2r6;es7media6;te;ti3;ecu6ta2;re;aEeBiAo8u6;ge,m6ng1R;b5id;ll6me0t;ow;gh,l0;a6f04sita2;dy,v6;en0y;nd1Hppy,r6te4;d,sh;aGenFhDiClBoofy,r6;a9e8is0o6ue1E;o6ss;vy;at,en,y;nd,y;ad,ib,ooI;a2d1;a6o6;st0;t5uiY;u1y;aIeeb5iDlat,oAr8u6;ll,n6r14;!ny;aHe6iend0;e,sh;a7r6ul;get4mG;my;erce8n6rm,t;an6e;ciC;! ;le;ir,ke,n0Fr,st,t,ulA;aAerie,mp9sse7v6xtre0Q;il;nti6;al;ty;r7s6;tern,y;ly,th0;aFeCi9r7u6;ll,mb;u6y;nk;r7vi6;ne;e,ty;a6ep,nD;d6f,r;!ly;mp,pp03rk;aHhDlAo8r7u6;dd0r0te;isp,uel;ar6ld,mmon,ol,st0ward0zy;se;e6ou1;a6vW;n,r;ar8e6il0;ap,e6;sy;mi3;gey,lm8r6;e4i3;ful;!i3;aNiLlIoEr8u6;r0sy;ly;aAi7o6;ad,wn;ef,g7llia2;nt;ht;sh,ve;ld,r7un6;cy;ed,i3;ng;a7o6ue;nd,o1;ck,nd;g,tt6;er;d,ld,w1;dy;bsu9ng8we6;so6;me;ry;rd",
"TextValue": "true¦bOeJfDhundredNmOninAone,qu8s6t0zeroN;enMh3rNw0;e0o;l0ntD;fHve;ir0ousandKree;d,t6;e0ix8;cond,pt1ven7xt1;adr0int0;illionD;e0th;!t0;e9ie8y;i3o0;rt1ur0;!t2;ie4y;ft0rst,ve;e3h,ie2y;ight0lev2;!e1h,ie0y;th;en0;!th;illion0;!s,th",
"Ordinal": "true¦bGeDf9hundredHmGnin7qu6s4t0zeroH;enGh1rFwe0;lfFn9;ir0ousandE;d,t4;e0ixt9;cond,ptAvent8xtA;adr9int9;et0th;e6ie8;i2o0;r0urt3;tie5;ft1rst;ight0lev1;e0h,ie2;en1;illion0;th",
"Cardinal": "true¦bHeEf8hundred,mHnineAone,qu6s4t0zero;en,h2rGw0;e0o;lve,n8;irt9ousandEree;e0ix5;pt1ven4xt1;adr0int0;illion;i3o0;r1ur0;!t2;ty;ft0ve;e2y;ight0lev1;!e0y;en;illion0;!s",
"Expression": "true¦a02b01dXeVfuck,gShLlImHnGoDpBshAtsk,u7voi04w3y0;a1eLu0;ck,p;!a,hoo,y;h1ow,t0;af,f;e0oa;e,w;gh,h0;! 0h,m;huh,oh;eesh,hh,it;ff,hew,l0sst;ease,z;h1o0w,y;h,o,ps;!h;ah,ope;eh,mm;m1ol0;!s;ao,fao;a4e2i,mm,oly1urr0;ah;! mo6;e,ll0y;!o;ha0i;!ha;ah,ee,o0rr;l0odbye;ly;e0h,t cetera,ww;k,p;'oh,a0uh;m0ng;mit,n0;!it;ah,oo,ye; 1h0rgh;!em;la",
"Adverb": "true¦a07by 05d01eYfShQinPjustOkinda,mMnJoEpCquite,r9s5t2up1very,w0Bye0;p,s; to,wards5;h1o0wiO;o,t6ward;en,us;everal,o0uch;!me1rt0; of;hXtimes,w07;a1e0;alS;ndomRthN;ar excellDer0oint blank; Mhaps;f3n0;ce0ly;! 0;ag00moU; courHten;ewJo0; longEt 0;onHwithstanding;aybe,eanwhiAore0;!ovB;! aboS;deed,steT;en0;ce;or2u0;l9rther0;!moH; 0ev3;examp0good,suF;le;n mas1v0;er;se;e0irect1; 1finite0;ly;ju7trop;far,n0;ow; CbroBd nauseam,gAl5ny2part,side,t 0w3;be5l0mo5wor5;arge,ea4;mo1w0;ay;re;l 1mo0one,ready,so,ways;st;b1t0;hat;ut;ain;ad;lot,posteriori",
"Preposition": "true¦'o,-,aKbHcGdFexcept,fEinDmidPnotwithstandiQoBpRqua,sAt6u3vi2w0;/o,hereMith0;!in,oQ;a,s-a-vis;n1p0;!on;like,til;h0ill,owards;an,r0;ough0u;!oI;ans,ince,o that;',f0n1ut;!f;!to;or,rom;espite,own,u3;hez,irca;ar1e0oAy;low,sides,tween;ri6;',bo7cross,ft6lo5m3propos,round,s1t0;!op;! long 0;as;id0ong0;!st;ng;er;ut",
"Determiner": "true¦aAboth,d8e5few,l3mu7neiCown,plenty,some,th2various,wh0;at0ich0;evB;at,e3is,ose;a,e0;!ast,s;a1i6l0nough,very;!se;ch;e0u;!s;!n0;!o0y;th0;er"
};
var entity = ['Person', 'Place', 'Organization'];
var nouns = {
Noun: {
notA: ['Verb', 'Adjective', 'Adverb']
},
// - singular
Singular: {
isA: 'Noun',
notA: 'Plural'
},
//a specific thing that's capitalized
ProperNoun: {
isA: 'Noun'
},
// -- people
Person: {
isA: ['ProperNoun', 'Singular'],
notA: ['Place', 'Organization', 'Date']
},
FirstName: {
isA: 'Person'
},
MaleName: {
isA: 'FirstName',
notA: ['FemaleName', 'LastName']
},
FemaleName: {
isA: 'FirstName',
notA: ['MaleName', 'LastName']
},
LastName: {
isA: 'Person',
notA: ['FirstName']
},
NickName: {
isA: 'Person',
notA: ['FirstName', 'LastName']
},
Honorific: {
isA: 'Noun',
notA: ['FirstName', 'LastName', 'Value']
},
// -- places
Place: {
isA: 'Singular',
notA: ['Person', 'Organization']
},
Country: {
isA: ['Place', 'ProperNoun'],
notA: ['City']
},
City: {
isA: ['Place', 'ProperNoun'],
notA: ['Country']
},
Region: {
isA: ['Place', 'ProperNoun']
},
Address: {
isA: 'Place'
},
//---Orgs---
Organization: {
isA: ['Singular', 'ProperNoun'],
notA: ['Person', 'Place']
},
SportsTeam: {
isA: 'Organization'
},
School: {
isA: 'Organization'
},
Company: {
isA: 'Organization'
},
// - plural
Plural: {
isA: 'Noun',
notA: ['Singular']
},
//(not plural or singular)
Uncountable: {
isA: 'Noun'
},
Pronoun: {
isA: 'Noun',
notA: entity
},
//a word for someone doing something -'plumber'
Actor: {
isA: 'Noun',
notA: entity
},
//a gerund-as-noun - 'swimming'
Activity: {
isA: 'Noun',
notA: ['Person', 'Place']
},
//'kilograms'
Unit: {
isA: 'Noun',
notA: entity
},
//'Canadians'
Demonym: {
isA: ['Noun', 'ProperNoun'],
notA: entity
},
//`john's`
Possessive: {
isA: 'Noun' // notA: 'Pronoun',
}
};
var verbs = {
Verb: {
notA: ['Noun', 'Adjective', 'Adverb', 'Value']
},
// walks
PresentTense: {
isA: 'Verb',
notA: ['PastTense', 'Copula', 'FutureTense']
},
// neutral form - 'walk'
Infinitive: {
isA: 'PresentTense',
notA: ['PastTense', 'Gerund']
},
// walking
Gerund: {
isA: 'PresentTense',
notA: ['PastTense', 'Copula', 'FutureTense']
},
// walked
PastTense: {
isA: 'Verb',
notA: ['FutureTense']
},
// will walk
FutureTense: {
isA: 'Verb'
},
// is
Copula: {
isA: 'Verb'
},
// would have
Modal: {
isA: 'Verb',
notA: ['Infinitive']
},
// had walked
PerfectTense: {
isA: 'Verb',
notA: 'Gerund'
},
Pluperfect: {
isA: 'Verb'
},
// shown
Participle: {
isA: 'Verb'
},
// show up
PhrasalVerb: {
isA: 'Verb'
},
//'up' part
Particle: {
isA: 'PhrasalVerb'
}
};
var values = {
Value: {
notA: ['Verb', 'Adjective', 'Adverb']
},
Ordinal: {
isA: 'Value',
notA: ['Cardinal']
},
Cardinal: {
isA: 'Value',
notA: ['Ordinal']
},
RomanNumeral: {
isA: 'Cardinal',
//can be a person, too
notA: ['Ordinal', 'TextValue']
},
TextValue: {
isA: 'Value',
notA: ['NumericValue']
},
NumericValue: {
isA: 'Value',
notA: ['TextValue']
},
Money: {
isA: 'Cardinal'
},
Percent: {
isA: 'Value'
}
};
var anything = ['Noun', 'Verb', 'Adjective', 'Adverb', 'Value', 'QuestionWord'];
var misc = {
//--Adjectives--
Adjective: {
notA: ['Noun', 'Verb', 'Adverb', 'Value']
},
// adjectives that can conjugate
Comparable: {
isA: ['Adjective']
},
// better
Comparative: {
isA: ['Adjective']
},
// best
Superlative: {
isA: ['Adjective'],
notA: ['Comparative']
},
NumberRange: {
isA: ['Contraction']
},
Adverb: {
notA: ['Noun', 'Verb', 'Adjective', 'Value']
},
// Dates:
//not a noun, but usually is
Date: {
notA: ['Verb', 'Conjunction', 'Adverb', 'Preposition', 'Adjective']
},
Month: {
isA: ['Date', 'Singular'],
notA: ['Year', 'WeekDay', 'Time']
},
WeekDay: {
isA: ['Date', 'Noun']
},
// '9:20pm'
Time: {
isA: ['Date'],
notA: ['Value']
},
//glue
Determiner: {
notA: anything
},
Conjunction: {
notA: anything
},
Preposition: {
notA: anything
},
// what, who, why
QuestionWord: {
notA: ['Determiner']
},
// peso, euro
Currency: {},
// ughh
Expression: {
notA: ['Noun', 'Adjective', 'Verb', 'Adverb']
},
// dr.
Abbreviation: {},
// internet tags
Url: {
notA: ['HashTag', 'PhoneNumber', 'Verb', 'Adjective', 'Value', 'AtMention', 'Email']
},
PhoneNumber: {
notA: ['HashTag', 'Verb', 'Adjective', 'Value', 'AtMention', 'Email']
},
HashTag: {},
AtMention: {
isA: ['Noun'],
notA: ['HashTag', 'Verb', 'Adjective', 'Value', 'Email']
},
Emoji: {
notA: ['HashTag', 'Verb', 'Adjective', 'Value', 'AtMention']
},
Emoticon: {
notA: ['HashTag', 'Verb', 'Adjective', 'Value', 'AtMention']
},
Email: {
notA: ['HashTag', 'Verb', 'Adjective', 'Value', 'AtMention']
},
//non-exclusive
Auxiliary: {
notA: ['Noun', 'Adjective', 'Value']
},
Acronym: {
notA: ['Plural', 'RomanNumeral']
},
Negative: {
notA: ['Noun', 'Adjective', 'Value']
},
// if, unless, were
Condition: {
notA: ['Verb', 'Adjective', 'Noun', 'Value']
}
};
// i just made these up
var colorMap = {
Noun: 'blue',
Verb: 'green',
Negative: 'green',
Date: 'red',
Value: 'red',
Adjective: 'magenta',
Preposition: 'cyan',
Conjunction: 'cyan',
Determiner: 'cyan',
Adverb: 'cyan'
};
/** add a debug color to some tags */
var addColors = function addColors(tags) {
Object.keys(tags).forEach(function (k) {
// assigned from plugin, for example
if (tags[k].color) {
tags[k].color = tags[k].color;
return;
} // defined above
if (colorMap[k]) {
tags[k].color = colorMap[k];
return;
}
tags[k].isA.some(function (t) {
if (colorMap[t]) {
tags[k].color = colorMap[t];
return true;
}
return false;
});
});
return tags;
};
var _color = addColors;
var unique$2 = function unique(arr) {
return arr.filter(function (v, i, a) {
return a.indexOf(v) === i;
});
}; //add 'downward' tags (that immediately depend on this one)
var inferIsA = function inferIsA(tags) {
Object.keys(tags).forEach(function (k) {
var tag = tags[k];
var len = tag.isA.length;
for (var i = 0; i < len; i++) {
var down = tag.isA[i];
if (tags[down]) {
tag.isA = tag.isA.concat(tags[down].isA);
}
} // clean it up
tag.isA = unique$2(tag.isA);
});
return tags;
};
var _isA = inferIsA;
var unique$3 = function unique(arr) {
return arr.filter(function (v, i, a) {
return a.indexOf(v) === i;
});
}; // crawl the tag-graph and infer any conflicts
// faster than doing this at tag-time
var inferNotA = function inferNotA(tags) {
var keys = Object.keys(tags);
keys.forEach(function (k) {
var tag = tags[k];
tag.notA = tag.notA || [];
tag.isA.forEach(function (down) {
if (tags[down] && tags[down].notA) {
// borrow its conflicts
var notA = typeof tags[down].notA === 'string' ? [tags[down].isA] : tags[down].notA || [];
tag.notA = tag.notA.concat(notA);
}
}); // any tag that lists us as a conflict, we conflict it back.
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (tags[key].notA.indexOf(k) !== -1) {
tag.notA.push(key);
}
} // clean it up
tag.notA = unique$3(tag.notA);
});
return tags;
};
var _notA = inferNotA;
// a lineage is all 'incoming' tags that have this as 'isA'
var inferLineage = function inferLineage(tags) {
var keys = Object.keys(tags);
keys.forEach(function (k) {
var tag = tags[k];
tag.lineage = []; // find all tags with it in their 'isA' set
for (var i = 0; i < keys.length; i++) {
if (tags[keys[i]].isA.indexOf(k) !== -1) {
tag.lineage.push(keys[i]);
}
}
});
return tags;
};
var _lineage = inferLineage;
var validate = function validate(tags) {
// cleanup format
Object.keys(tags).forEach(function (k) {
var tag = tags[k]; // ensure isA is an array
tag.isA = tag.isA || [];
if (typeof tag.isA === 'string') {
tag.isA = [tag.isA];
} // ensure notA is an array
tag.notA = tag.notA || [];
if (typeof tag.notA === 'string') {
tag.notA = [tag.notA];
}
});
return tags;
}; // build-out the tag-graph structure
var inferTags = function inferTags(tags) {
// validate data
tags = validate(tags); // build its 'down tags'
tags = _isA(tags); // infer the conflicts
tags = _notA(tags); // debug tag color
tags = _color(tags); // find incoming links
tags = _lineage(tags);
return tags;
};
var inference = inferTags;
var addIn = function addIn(obj, tags) {
Object.keys(obj).forEach(function (k) {
tags[k] = obj[k];
});
};
var build = function build() {
var tags = {};
addIn(nouns, tags);
addIn(verbs, tags);
addIn(values, tags);
addIn(misc, tags); // do the graph-stuff
tags = inference(tags);
return tags;
};
var tags = build();
var seq = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
cache = seq.split("").reduce(function (n, o, e) {
return n[o] = e, n;
}, {}),
toAlphaCode = function toAlphaCode(n) {
if (void 0 !== seq[n]) return seq[n];
var o = 1,
e = 36,
t = "";
for (; n >= e; n -= e, o++, e *= 36) {
}
for (; o--;) {
var _o = n % 36;
t = String.fromCharCode((_o < 10 ? 48 : 55) + _o) + t, n = (n - _o) / 36;
}
return t;
},
fromAlphaCode = function fromAlphaCode(n) {
if (void 0 !== cache[n]) return cache[n];
var o = 0,
e = 1,
t = 36,
r = 1;
for (; e < n.length; o += t, e++, t *= 36) {
}
for (var _e = n.length - 1; _e >= 0; _e--, r *= 36) {
var _t = n.charCodeAt(_e) - 48;
_t > 10 && (_t -= 7), o += _t * r;
}
return o;
};
var encoding = {
toAlphaCode: toAlphaCode,
fromAlphaCode: fromAlphaCode
},
symbols = function symbols(n) {
var o = new RegExp("([0-9A-Z]+):([0-9A-Z]+)");
for (var e = 0; e < n.nodes.length; e++) {
var t = o.exec(n.nodes[e]);
if (!t) {
n.symCount = e;
break;
}
n.syms[encoding.fromAlphaCode(t[1])] = encoding.fromAlphaCode(t[2]);
}
n.nodes = n.nodes.slice(n.symCount, n.nodes.length);
};
var indexFromRef = function indexFromRef(n, o, e) {
var t = encoding.fromAlphaCode(o);
return t < n.symCount ? n.syms[t] : e + t + 1 - n.symCount;
},
toArray = function toArray(n) {
var o = [],
e = function e(t, r) {
var s = n.nodes[t];
"!" === s[0] && (o.push(r), s = s.slice(1));
var c = s.split(/([A-Z0-9,]+)/g);
for (var _s = 0; _s < c.length; _s += 2) {
var u = c[_s],
i = c[_s + 1];
if (!u) continue;
var l = r + u;
if ("," === i || void 0 === i) {
o.push(l);
continue;
}
var f = indexFromRef(n, i, t);
e(f, l);
}
};
return e(0, ""), o;
},
unpack = function unpack(n) {
var o = {
nodes: n.split(";"),
syms: [],
symCount: 0
};
return n.match(":") && symbols(o), toArray(o);
};
var unpack_1 = unpack,
unpack_1$1 = function unpack_1$1(n) {
var o = n.split("|").reduce(function (n, o) {
var e = o.split("¦");
return n[e[0]] = e[1], n;
}, {}),
e = {};
return Object.keys(o).forEach(function (n) {
var t = unpack_1(o[n]);
"true" === n && (n = !0);
for (var _o2 = 0; _o2 < t.length; _o2++) {
var r = t[_o2];
!0 === e.hasOwnProperty(r) ? !1 === Array.isArray(e[r]) ? e[r] = [e[r], n] : e[r].push(n) : e[r] = n;
}
}), e;
};
var efrtUnpack_min = unpack_1$1;
//safely add it to the lexicon
var addWord = function addWord(word, tag, lex) {
if (lex[word] !== undefined &&
typeof lex[word] != 'function') { // @blab+ override prototype of object!!!
if (typeof lex[word] === 'string') {
lex[word] = [lex[word]];
}
if (typeof tag === 'string') {
lex[word].push(tag);
} else {
lex[word] = lex[word].concat(tag);
}
} else {
lex[word] = tag;
}
}; // blast-out more forms for some given words
var addMore = function addMore(word, tag, world) {
var lexicon = world.words;
var transform = world.transforms; // cache multi-words
var words = word.split(' ');
if (words.length > 1) {
//cache the beginning word
world.hasCompound[words[0]] = true;
} // inflect our nouns
if (tag === 'Singular') {
var plural = transform.toPlural(word, world);
lexicon[plural] = lexicon[plural] || 'Plural'; // only if it's safe
} //conjugate our verbs
if (tag === 'Infinitive') {
var conj = transform.conjugate(word, world);
var tags = Object.keys(conj);
for (var i = 0; i < tags.length; i++) {
var w = conj[tags[i]];
lexicon[w] = lexicon[w] || tags[i]; // only if it's safe
}
} //derive more adjective forms
if (tag === 'Comparable') {
var _conj = transform.adjectives(word);
var _tags = Object.keys(_conj);
for (var _i = 0; _i < _tags.length; _i++) {
var _w = _conj[_tags[_i]];
lexicon[_w] = lexicon[_w] || _tags[_i]; // only if it's safe
}
} //conjugate phrasal-verbs
if (tag === 'PhrasalVerb') {
//add original form
addWord(word, 'Infinitive', lexicon); //conjugate first word
var _conj2 = transform.conjugate(words[0], world);
var _tags2 = Object.keys(_conj2);
for (var _i2 = 0; _i2 < _tags2.length; _i2++) {
//add it to our cache
world.hasCompound[_conj2[_tags2[_i2]]] = true; //first + last words
var _w2 = _conj2[_tags2[_i2]] + ' ' + words[1];
addWord(_w2, _tags2[_i2], lexicon);
addWord(_w2, 'PhrasalVerb', lexicon);
}
} // inflect our demonyms - 'germans'
if (tag === 'Demonym') {
var _plural = transform.toPlural(word, world);
lexicon[_plural] = lexicon[_plural] || ['Demonym', 'Plural']; // only if it's safe
}
}; // throw a bunch of words in our lexicon
// const doWord = function(words, tag, world) {
// let lexicon = world.words
// for (let i = 0; i < words.length; i++) {
// addWord(words[i], tag, lexicon)
// // do some fancier stuff
// addMore(words[i], tag, world)
// }
// }
var addWords = {
addWord: addWord,
addMore: addMore
};
// add words from plurals and conjugations data
var addIrregulars = function addIrregulars(world) {
//add irregular plural nouns
var nouns = world.irregulars.nouns;
var words = Object.keys(nouns);
for (var i = 0; i < words.length; i++) {
var w = words[i];
world.words[w] = 'Singular';
world.words[nouns[w]] = 'Plural';
} // add irregular verb conjugations
var verbs = world.irregulars.verbs;
var keys = Object.keys(verbs);
var _loop = function _loop(_i) {
var inf = keys[_i]; //add only if it it's safe...
world.words[inf] = world.words[inf] || 'Infinitive';
var forms = world.transforms.conjugate(inf, world);
forms = Object.assign(forms, verbs[inf]); //add the others
Object.keys(forms).forEach(function (tag) {
world.words[forms[tag]] = world.words[forms[tag]] || tag;
});
};
for (var _i = 0; _i < keys.length; _i++) {
_loop(_i);
}
};
var addIrregulars_1 = addIrregulars;
//words that can't be compressed, for whatever reason
var misc$1 = {
// numbers
'20th century fox': 'Organization',
// '3m': 'Organization',
'7 eleven': 'Organization',
'7-eleven': 'Organization',
g8: 'Organization',
'motel 6': 'Organization',
vh1: 'Organization',
q1: 'Date',
q2: 'Date',
q3: 'Date',
q4: 'Date'
};
//nouns with irregular plural/singular forms
//used in noun.inflect, and also in the lexicon.
var plurals = {
addendum: 'addenda',
alga: 'algae',
alumna: 'alumnae',
alumnus: 'alumni',
analysis: 'analyses',
antenna: 'antennae',
appendix: 'appendices',
avocado: 'avocados',
axis: 'axes',
bacillus: 'bacilli',
barracks: 'barracks',
beau: 'beaux',
bus: 'buses',
cactus: 'cacti',
chateau: 'chateaux',
child: 'children',
circus: 'circuses',
clothes: 'clothes',
corpus: 'corpora',
criterion: 'criteria',
curriculum: 'curricula',
database: 'databases',
deer: 'deer',
diagnosis: 'diagnoses',
echo: 'echoes',
embargo: 'embargoes',
epoch: 'epochs',
foot: 'feet',
formula: 'formulae',
fungus: 'fungi',
genus: 'genera',
goose: 'geese',
halo: 'halos',
hippopotamus: 'hippopotami',
index: 'indices',
larva: 'larvae',
leaf: 'leaves',
libretto: 'libretti',
loaf: 'loaves',
man: 'men',
matrix: 'matrices',
memorandum: 'memoranda',
modulus: 'moduli',
mosquito: 'mosquitoes',
mouse: 'mice',
move: 'moves',
nebula: 'nebulae',
nucleus: 'nuclei',
octopus: 'octopi',
opus: 'opera',
ovum: 'ova',
ox: 'oxen',
parenthesis: 'parentheses',
person: 'people',
phenomenon: 'phenomena',
prognosis: 'prognoses',
quiz: 'quizzes',
radius: 'radii',
referendum: 'referenda',
rodeo: 'rodeos',
sex: 'sexes',
shoe: 'shoes',
sombrero: 'sombreros',
stimulus: 'stimuli',
stomach: 'stomachs',
syllabus: 'syllabi',
synopsis: 'synopses',
tableau: 'tableaux',
thesis: 'theses',
thief: 'thieves',
tooth: 'teeth',
tornado: 'tornados',
tuxedo: 'tuxedos',
vertebra: 'vertebrae' // virus: 'viri',
// zero: 'zeros',
};
// a list of irregular verb conjugations
// used in verbs().conjugate()
// but also added to our lexicon
//use shorter key-names
var mapping = {
g: 'Gerund',
prt: 'Participle',
perf: 'PerfectTense',
pst: 'PastTense',
fut: 'FuturePerfect',
pres: 'PresentTense',
pluperf: 'Pluperfect',
a: 'Actor'
}; // '_' in conjugations is the infinitive form
var conjugations = {
act: {
a: '_or'
},
ache: {
pst: 'ached',
g: 'aching'
},
age: {
g: 'ageing',
pst: 'aged',
pres: 'ages'
},
aim: {
a: '_er',
g: '_ing',
pst: '_ed'
},
arise: {
prt: '_n',
pst: 'arose'
},
babysit: {
a: '_ter',
pst: 'babysat'
},
ban: {
a: '',
g: '_ning',
pst: '_ned'
},
be: {
a: '',
g: 'am',
prt: 'been',
pst: 'was',
pres: 'is'
},
beat: {
a: '_er',
g: '_ing',
prt: '_en'
},
become: {
prt: '_'
},
begin: {
g: '_ning',
prt: 'begun',
pst: 'began'
},
being: {
g: 'are',
pst: 'were',
pres: 'are'
},
bend: {
prt: 'bent'
},
bet: {
a: '_ter',
prt: '_'
},
bind: {
pst: 'bound'
},
bite: {
g: 'biting',
prt: 'bitten',
pst: 'bit'
},
bleed: {
prt: 'bled',
pst: 'bled'
},
blow: {
prt: '_n',
pst: 'blew'
},
boil: {
a: '_er'
},
brake: {
prt: 'broken'
},
"break": {
pst: 'broke'
},
breed: {
pst: 'bred'
},
bring: {
prt: 'brought',
pst: 'brought'
},
broadcast: {
pst: '_'
},
budget: {
pst: '_ed'
},
build: {
prt: 'built',
pst: 'built'
},
burn: {
prt: '_ed'
},
burst: {
prt: '_'
},
buy: {
prt: 'bought',
pst: 'bought'
},
can: {
a: '',
fut: '_',
g: '',
pst: 'could',
perf: 'could',
pluperf: 'could',
pres: '_'
},
"catch": {
pst: 'caught'
},
choose: {
g: 'choosing',
prt: 'chosen',
pst: 'chose'
},
cling: {
prt: 'clung'
},
come: {
prt: '_',
pst: 'came',
g: 'coming'
},
compete: {
a: 'competitor',
g: 'competing',
pst: '_d'
},
cost: {
pst: '_'
},
creep: {
prt: 'crept'
},
cut: {
prt: '_'
},
deal: {
prt: '_t',
pst: '_t'
},
develop: {
a: '_er',
g: '_ing',
pst: '_ed'
},
die: {
g: 'dying',
pst: '_d'
},
dig: {
g: '_ging',
prt: 'dug',
pst: 'dug'
},
dive: {
prt: '_d'
},
"do": {
pst: 'did',
pres: '_es'
},
draw: {
prt: '_n',
pst: 'drew'
},
dream: {
prt: '_t'
},
drink: {
prt: 'drunk',
pst: 'drank'
},
drive: {
g: 'driving',
prt: '_n',
pst: 'drove'
},
drop: {
g: '_ping',
pst: '_ped'
},
eat: {
a: '_er',
g: '_ing',
prt: '_en',
pst: 'ate'
},
edit: {
pst: '_ed',
g: '_ing'
},
egg: {
pst: '_ed'
},
fall: {
prt: '_en',
pst: 'fell'
},
feed: {
prt: 'fed',
pst: 'fed'
},
feel: {
a: '_er',
pst: 'felt'
},
fight: {
prt: 'fought',
pst: 'fought'
},
find: {
pst: 'found'
},
flee: {
g: '_ing',
prt: 'fled'
},
fling: {
prt: 'flung'
},
fly: {
prt: 'flown',
pst: 'flew'
},
forbid: {
pst: 'forbade'
},
forget: {
g: '_ing',
prt: 'forgotten',
pst: 'forgot'
},
forgive: {
g: 'forgiving',
prt: '_n',
pst: 'forgave'
},
free: {
a: '',
g: '_ing'
},
freeze: {
g: 'freezing',
prt: 'frozen',
pst: 'froze'
},
get: {
pst: 'got',
prt: 'gotten'
},
give: {
g: 'giving',
prt: '_n',
pst: 'gave'
},
go: {
prt: '_ne',
pst: 'went',
pres: 'goes'
},
grow: {
prt: '_n'
},
hang: {
prt: 'hung',
pst: 'hung'
},
have: {
g: 'having',
prt: 'had',
pst: 'had',
pres: 'has'
},
hear: {
prt: '_d',
pst: '_d'
},
hide: {
prt: 'hidden',
pst: 'hid'
},
hit: {
prt: '_'
},
hold: {
prt: 'held',
pst: 'held'
},
hurt: {
prt: '_',
pst: '_'
},
ice: {
g: 'icing',
pst: '_d'
},
imply: {
pst: 'implied',
pres: 'implies'
},
is: {
a: '',
g: 'being',
pst: 'was',
pres: '_'
},
keep: {
prt: 'kept'
},
kneel: {
prt: 'knelt'
},
know: {
prt: '_n'
},
lay: {
prt: 'laid',
pst: 'laid'
},
lead: {
prt: 'led',
pst: 'led'
},
leap: {
prt: '_t'
},
leave: {
prt: 'left',
pst: 'left'
},
lend: {
prt: 'lent'
},
lie: {
g: 'lying',
pst: 'lay'
},
light: {
prt: 'lit',
pst: 'lit'
},
log: {
g: '_ging',
pst: '_ged'
},
loose: {
prt: 'lost'
},
lose: {
g: 'losing',
pst: 'lost'
},
make: {
prt: 'made',
pst: 'made'
},
mean: {
prt: '_t',
pst: '_t'
},
meet: {
a: '_er',
g: '_ing',
prt: 'met',
pst: 'met'
},
miss: {
pres: '_'
},
name: {
g: 'naming'
},
pay: {
prt: 'paid',
pst: 'paid'
},
prove: {
prt: '_n'
},
puke: {
g: 'puking'
},
put: {
prt: '_'
},
quit: {
prt: '_'
},
read: {
prt: '_',
pst: '_'
},
ride: {
prt: 'ridden'
},
ring: {
prt: 'rung',
pst: 'rang'
},
rise: {
fut: 'will have _n',
g: 'rising',
prt: '_n',
pst: 'rose',
pluperf: 'had _n'
},
rub: {
g: '_bing',
pst: '_bed'
},
run: {
g: '_ning',
prt: '_',
pst: 'ran'
},
say: {
prt: 'said',
pst: 'said',
pres: '_s'
},
seat: {
prt: 'sat'
},
see: {
g: '_ing',
prt: '_n',
pst: 'saw'
},
seek: {
prt: 'sought'
},
sell: {
prt: 'sold',
pst: 'sold'
},
send: {
prt: 'sent'
},
set: {
prt: '_'
},
sew: {
prt: '_n'
},
shake: {
prt: '_n'
},
shave: {
prt: '_d'
},
shed: {
g: '_ding',
pst: '_',
pres: '_s'
},
shine: {
prt: 'shone',
pst: 'shone'
},
shoot: {
prt: 'shot',
pst: 'shot'
},
show: {
pst: '_ed'
},
shut: {
prt: '_'
},
sing: {
prt: 'sung',
pst: 'sang'
},
sink: {
pst: 'sank',
pluperf: 'had sunk'
},
sit: {
pst: 'sat'
},
ski: {
pst: '_ied'
},
slay: {
prt: 'slain'
},
sleep: {
prt: 'slept'
},
slide: {
prt: 'slid',
pst: 'slid'
},
smash: {
pres: '_es'
},
sneak: {
prt: 'snuck'
},
speak: {
fut: 'will have spoken',
prt: 'spoken',
pst: 'spoke',
perf: 'have spoken',
pluperf: 'had spoken'
},
speed: {
prt: 'sped'
},
spend: {
prt: 'spent'
},
spill: {
prt: '_ed',
pst: 'spilt'
},
spin: {
g: '_ning',
prt: 'spun',
pst: 'spun'
},
spit: {
prt: 'spat'
},
split: {
prt: '_'
},
spread: {
pst: '_'
},
spring: {
prt: 'sprung'
},
stand: {
pst: 'stood'
},
steal: {
a: '_er',
pst: 'stole'
},
stick: {
pst: 'stuck'
},
sting: {
pst: 'stung'
},
stink: {
prt: 'stunk',
pst: 'stunk'
},
stream: {
a: '_er'
},
strew: {
prt: '_n'
},
strike: {
g: 'striking',
pst: 'struck'
},
suit: {
a: '_er',
g: '_ing',
pst: '_ed'
},
sware: {
prt: 'sworn'
},
swear: {
pst: 'swore'
},
sweep: {
prt: 'swept'
},
swim: {
g: '_ming',
pst: 'swam'
},
swing: {
pst: 'swung'
},
take: {
fut: 'will have _n',
pst: 'took',
perf: 'have _n',
pluperf: 'had _n'
},
teach: {
pst: 'taught',
pres: '_es'
},
tear: {
pst: 'tore'
},
tell: {
pst: 'told'
},
think: {
pst: 'thought'
},
thrive: {
prt: '_d'
},
tie: {
g: 'tying',
pst: '_d'
},
undergo: {
prt: '_ne'
},
understand: {
pst: 'understood'
},
upset: {
prt: '_'
},
wait: {
a: '_er',
g: '_ing',
pst: '_ed'
},
wake: {
pst: 'woke'
},
wear: {
pst: 'wore'
},
weave: {
prt: 'woven'
},
wed: {
pst: 'wed'
},
weep: {
prt: 'wept'
},
win: {
g: '_ning',
pst: 'won'
},
wind: {
prt: 'wound'
},
withdraw: {
pst: 'withdrew'
},
wring: {
prt: 'wrung'
},
write: {
g: 'writing',
prt: 'written',
pst: 'wrote'
}
}; //uncompress our ad-hoc compression scheme
var keys = Object.keys(conjugations);
var _loop = function _loop(i) {
var inf = keys[i];
var _final = {};
Object.keys(conjugations[inf]).forEach(function (key) {
var str = conjugations[inf][key]; //swap-in infinitives for '_'
str = str.replace('_', inf);
var full = mapping[key];
_final[full] = str;
}); //over-write original
conjugations[inf] = _final;
};
for (var i = 0; i < keys.length; i++) {
_loop(i);
}
var conjugations_1 = conjugations;
var endsWith = {
b: [{
reg: /([^aeiou][aeiou])b$/i,
repl: {
pr: '$1bs',
pa: '$1bbed',
gr: '$1bbing'
}
}],
d: [{
reg: /(end)$/i,
repl: {
pr: '$1s',
pa: 'ent',
gr: '$1ing',
ar: '$1er'
}
}, {
reg: /(eed)$/i,
repl: {
pr: '$1s',
pa: '$1ed',
gr: '$1ing',
ar: '$1er'
}
}, {
reg: /(ed)$/i,
repl: {
pr: '$1s',
pa: '$1ded',
ar: '$1der',
gr: '$1ding'
}
}, {
reg: /([^aeiou][ou])d$/i,
repl: {
pr: '$1ds',
pa: '$1dded',
gr: '$1dding'
}
}],
e: [{
reg: /(eave)$/i,
repl: {
pr: '$1s',
pa: '$1d',
gr: 'eaving',
ar: '$1r'
}
}, {
reg: /(ide)$/i,
repl: {
pr: '$1s',
pa: 'ode',
gr: 'iding',
ar: 'ider'
}
}, {
//shake
reg: /(t|sh?)(ake)$/i,
repl: {
pr: '$1$2s',
pa: '$1ook',
gr: '$1aking',
ar: '$1$2r'
}
}, {
//awake
reg: /w(ake)$/i,
repl: {
pr: 'w$1s',
pa: 'woke',
gr: 'waking',
ar: 'w$1r'
}
}, {
//make
reg: /m(ake)$/i,
repl: {
pr: 'm$1s',
pa: 'made',
gr: 'making',
ar: 'm$1r'
}
}, {
reg: /(a[tg]|i[zn]|ur|nc|gl|is)e$/i,
repl: {
pr: '$1es',
pa: '$1ed',
gr: '$1ing' // prt: '$1en',
}
}, {
reg: /([bd]l)e$/i,
repl: {
pr: '$1es',
pa: '$1ed',
gr: '$1ing'
}
}, {
reg: /(om)e$/i,
repl: {
pr: '$1es',
pa: 'ame',
gr: '$1ing'
}
}],
g: [{
reg: /([^aeiou][ou])g$/i,
repl: {
pr: '$1gs',
pa: '$1gged',
gr: '$1gging'
}
}],
h: [{
reg: /(..)([cs]h)$/i,
repl: {
pr: '$1$2es',
pa: '$1$2ed',
gr: '$1$2ing'
}
}],
k: [{
reg: /(ink)$/i,
repl: {
pr: '$1s',
pa: 'unk',
gr: '$1ing',
ar: '$1er'
}
}],
m: [{
reg: /([^aeiou][aeiou])m$/i,
repl: {
pr: '$1ms',
pa: '$1mmed',
gr: '$1mming'
}
}],
n: [{
reg: /(en)$/i,
repl: {
pr: '$1s',
pa: '$1ed',
gr: '$1ing'
}
}],
p: [{
reg: /(e)(ep)$/i,
repl: {
pr: '$1$2s',
pa: '$1pt',
gr: '$1$2ing',
ar: '$1$2er'
}
}, {
reg: /([^aeiou][aeiou])p$/i,
repl: {
pr: '$1ps',
pa: '$1pped',
gr: '$1pping'
}
}, {
reg: /([aeiu])p$/i,
repl: {
pr: '$1ps',
pa: '$1p',
gr: '$1pping'
}
}],
r: [{
reg: /([td]er)$/i,
repl: {
pr: '$1s',
pa: '$1ed',
gr: '$1ing'
}
}, {
reg: /(er)$/i,
repl: {
pr: '$1s',
pa: '$1ed',
gr: '$1ing'
}
}],
s: [{
reg: /(ish|tch|ess)$/i,
repl: {
pr: '$1es',
pa: '$1ed',
gr: '$1ing'
}
}],
t: [{
reg: /(ion|end|e[nc]t)$/i,
repl: {
pr: '$1s',
pa: '$1ed',
gr: '$1ing'
}
}, {
reg: /(.eat)$/i,
repl: {
pr: '$1s',
pa: '$1ed',
gr: '$1ing'
}
}, {
reg: /([aeiu])t$/i,
repl: {
pr: '$1ts',
pa: '$1t',
gr: '$1tting'
}
}, {
reg: /([^aeiou][aeiou])t$/i,
repl: {
pr: '$1ts',
pa: '$1tted',
gr: '$1tting'
}
}],
w: [{
reg: /(..)(ow)$/i,
repl: {
pr: '$1$2s',
pa: '$1ew',
gr: '$1$2ing',
prt: '$1$2n'
}
}],
y: [{
reg: /([i|f|rr])y$/i,
repl: {
pr: '$1ies',
pa: '$1ied',
gr: '$1ying'
}
}],
z: [{
reg: /([aeiou]zz)$/i,
repl: {
pr: '$1es',
pa: '$1ed',
gr: '$1ing'
}
}]
};
var suffixes = endsWith;
var posMap = {
pr: 'PresentTense',
pa: 'PastTense',
gr: 'Gerund',
prt: 'Participle',
ar: 'Actor'
};
var doTransform = function doTransform(str, obj) {
var found = {};
var keys = Object.keys(obj.repl);
for (var i = 0; i < keys.length; i += 1) {
var pos = keys[i];
found[posMap[pos]] = str.replace(obj.reg, obj.repl[pos]);
}
return found;
}; //look at the end of the word for clues
var checkSuffix = function checkSuffix() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var c = str[str.length - 1];
if (suffixes.hasOwnProperty(c) === true) {
for (var r = 0; r < suffixes[c].length; r += 1) {
var reg = suffixes[c][r].reg;
if (reg.test(str) === true) {
return doTransform(str, suffixes[c][r]);
}
}
}
return {};
};
var _01Suffixes = checkSuffix;
//non-specifc, 'hail-mary' transforms from infinitive, into other forms
var hasY = /[bcdfghjklmnpqrstvwxz]y$/;
var generic = {
Gerund: function Gerund(inf) {
if (inf.charAt(inf.length - 1) === 'e') {
return inf.replace(/e$/, 'ing');
}
return inf + 'ing';
},
PresentTense: function PresentTense(inf) {
if (inf.charAt(inf.length - 1) === 's') {
return inf + 'es';
}
if (hasY.test(inf) === true) {
return inf.slice(0, -1) + 'ies';
}
return inf + 's';
},
PastTense: function PastTense(inf) {
if (inf.charAt(inf.length - 1) === 'e') {
return inf + 'd';
}
if (inf.substr(-2) === 'ed') {
return inf;
}
if (hasY.test(inf) === true) {
return inf.slice(0, -1) + 'ied';
}
return inf + 'ed';
}
};
var _02Generic = generic;
//we assume the input word is a proper infinitive
var conjugate = function conjugate() {
var inf = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var world = arguments.length > 1 ? arguments[1] : undefined;
var found = {}; // 1. look at irregulars
//the lexicon doesn't pass this in
if (world && world.irregulars) {
if (world.irregulars.verbs.hasOwnProperty(inf) === true) {
found = Object.assign({}, world.irregulars.verbs[inf]);
}
} //2. rule-based regex
found = Object.assign({}, _01Suffixes(inf), found); //3. generic transformations
//'buzzing'
if (found.Gerund === undefined) {
found.Gerund = _02Generic.Gerund(inf);
} //'buzzed'
if (found.PastTense === undefined) {
found.PastTense = _02Generic.PastTense(inf);
} //'buzzes'
if (found.PresentTense === undefined) {
found.PresentTense = _02Generic.PresentTense(inf);
}
return found;
};
var conjugate_1 = conjugate; // console.log(conjugate('bake'))
//turn 'quick' into 'quickest'
var do_rules = [/ght$/, /nge$/, /ough$/, /ain$/, /uel$/, /[au]ll$/, /ow$/, /oud$/, /...p$/];
var dont_rules = [/ary$/];
var irregulars = {
nice: 'nicest',
late: 'latest',
hard: 'hardest',
inner: 'innermost',
outer: 'outermost',
far: 'furthest',
worse: 'worst',
bad: 'worst',
good: 'best',
big: 'biggest',
large: 'largest'
};
var transforms = [{
reg: /y$/i,
repl: 'iest'
}, {
reg: /([aeiou])t$/i,
repl: '$1ttest'
}, {
reg: /([aeou])de$/i,
repl: '$1dest'
}, {
reg: /nge$/i,
repl: 'ngest'
}, {
reg: /([aeiou])te$/i,
repl: '$1test'
}];
var to_superlative = function to_superlative(str) {
//irregulars
if (irregulars.hasOwnProperty(str)) {
return irregulars[str];
} //known transforms
for (var i = 0; i < transforms.length; i++) {
if (transforms[i].reg.test(str)) {
return str.replace(transforms[i].reg, transforms[i].repl);
}
} //dont-rules
for (var _i = 0; _i < dont_rules.length; _i++) {
if (dont_rules[_i].test(str) === true) {
return null;
}
} //do-rules
for (var _i2 = 0; _i2 < do_rules.length; _i2++) {
if (do_rules[_i2].test(str) === true) {
if (str.charAt(str.length - 1) === 'e') {
return str + 'st';
}
return str + 'est';
}
}
return str + 'est';
};
var toSuperlative = to_superlative;
//turn 'quick' into 'quickly'
var do_rules$1 = [/ght$/, /nge$/, /ough$/, /ain$/, /uel$/, /[au]ll$/, /ow$/, /old$/, /oud$/, /e[ae]p$/];
var dont_rules$1 = [/ary$/, /ous$/];
var irregulars$1 = {
grey: 'greyer',
gray: 'grayer',
green: 'greener',
yellow: 'yellower',
red: 'redder',
good: 'better',
well: 'better',
bad: 'worse',
sad: 'sadder',
big: 'bigger'
};
var transforms$1 = [{
reg: /y$/i,
repl: 'ier'
}, {
reg: /([aeiou])t$/i,
repl: '$1tter'
}, {
reg: /([aeou])de$/i,
repl: '$1der'
}, {
reg: /nge$/i,
repl: 'nger'
}];
var to_comparative = function to_comparative(str) {
//known-irregulars
if (irregulars$1.hasOwnProperty(str)) {
return irregulars$1[str];
} //known-transforms
for (var i = 0; i < transforms$1.length; i++) {
if (transforms$1[i].reg.test(str) === true) {
return str.replace(transforms$1[i].reg, transforms$1[i].repl);
}
} //dont-patterns
for (var _i = 0; _i < dont_rules$1.length; _i++) {
if (dont_rules$1[_i].test(str) === true) {
return null;
}
} //do-patterns
for (var _i2 = 0; _i2 < do_rules$1.length; _i2++) {
if (do_rules$1[_i2].test(str) === true) {
return str + 'er';
}
} //easy-one
if (/e$/.test(str) === true) {
return str + 'r';
}
return str + 'er';
};
var toComparative = to_comparative;
var fns$1 = {
toSuperlative: toSuperlative,
toComparative: toComparative
};
/** conjugate an adjective into other forms */
var conjugate$1 = function conjugate(w) {
var res = {}; // 'greatest'
var sup = fns$1.toSuperlative(w);
if (sup) {
res.Superlative = sup;
} // 'greater'
var comp = fns$1.toComparative(w);
if (comp) {
res.Comparative = comp;
}
return res;
};
var adjectives = conjugate$1;
/** patterns for turning 'bus' to 'buses'*/
var suffixes$1 = {
a: [[/(antenn|formul|nebul|vertebr|vit)a$/i, '$1ae'], [/([ti])a$/i, '$1a']],
e: [[/(kn|l|w)ife$/i, '$1ives'], [/(hive)$/i, '$1s'], [/([m|l])ouse$/i, '$1ice'], [/([m|l])ice$/i, '$1ice']],
f: [[/^(dwar|handkerchie|hoo|scar|whar)f$/i, '$1ves'], [/^((?:ca|e|ha|(?:our|them|your)?se|she|wo)l|lea|loa|shea|thie)f$/i, '$1ves']],
i: [[/(octop|vir)i$/i, '$1i']],
m: [[/([ti])um$/i, '$1a']],
n: [[/^(oxen)$/i, '$1']],
o: [[/(al|ad|at|er|et|ed|ad)o$/i, '$1oes']],
s: [[/(ax|test)is$/i, '$1es'], [/(alias|status)$/i, '$1es'], [/sis$/i, 'ses'], [/(bu)s$/i, '$1ses'], [/(sis)$/i, 'ses'], [/^(?!talis|.*hu)(.*)man$/i, '$1men'], [/(octop|vir|radi|nucle|fung|cact|stimul)us$/i, '$1i']],
x: [[/(matr|vert|ind|cort)(ix|ex)$/i, '$1ices'], [/^(ox)$/i, '$1en']],
y: [[/([^aeiouy]|qu)y$/i, '$1ies']],
z: [[/(quiz)$/i, '$1zes']]
};
var _rules = suffixes$1;
var addE = /(x|ch|sh|s|z)$/;
var trySuffix = function trySuffix(str) {
var c = str[str.length - 1];
if (_rules.hasOwnProperty(c) === true) {
for (var i = 0; i < _rules[c].length; i += 1) {
var reg = _rules[c][i][0];
if (reg.test(str) === true) {
return str.replace(reg, _rules[c][i][1]);
}
}
}
return null;
};
/** Turn a singular noun into a plural
* assume the given string is singular
*/
var pluralize = function pluralize() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var world = arguments.length > 1 ? arguments[1] : undefined;
var irregulars = world.irregulars.nouns; // check irregulars list
if (irregulars.hasOwnProperty(str)) {
return irregulars[str];
} //we have some rules to try-out
var plural = trySuffix(str);
if (plural !== null) {
return plural;
} //like 'church'
if (addE.test(str)) {
return str + 'es';
} // ¯\_(ツ)_/¯
return str + 's';
};
var toPlural = pluralize;
//patterns for turning 'dwarves' to 'dwarf'
var _rules$1 = [[/([^v])ies$/i, '$1y'], [/ises$/i, 'isis'], [/(kn|[^o]l|w)ives$/i, '$1ife'], [/^((?:ca|e|ha|(?:our|them|your)?se|she|wo)l|lea|loa|shea|thie)ves$/i, '$1f'], [/^(dwar|handkerchie|hoo|scar|whar)ves$/i, '$1f'], [/(antenn|formul|nebul|vertebr|vit)ae$/i, '$1a'], [/(octop|vir|radi|nucle|fung|cact|stimul)(i)$/i, '$1us'], [/(buffal|tomat|tornad)(oes)$/i, '$1o'], // [/(analy|diagno|parenthe|progno|synop|the)ses$/i, '$1sis'],
[/(eas)es$/i, '$1e'], //diseases
[/(..[aeiou]s)es$/i, '$1'], //geniouses
[/(vert|ind|cort)(ices)$/i, '$1ex'], [/(matr|append)(ices)$/i, '$1ix'], [/(x|ch|ss|sh|z|o)es$/i, '$1'], [/men$/i, 'man'], [/(n)ews$/i, '$1ews'], [/([ti])a$/i, '$1um'], [/([^aeiouy]|qu)ies$/i, '$1y'], [/(s)eries$/i, '$1eries'], [/(m)ovies$/i, '$1ovie'], [/([m|l])ice$/i, '$1ouse'], [/(cris|ax|test)es$/i, '$1is'], [/(alias|status)es$/i, '$1'], [/(ss)$/i, '$1'], [/(ics)$/i, '$1'], [/s$/i, '']];
var invertObj = function invertObj(obj) {
return Object.keys(obj).reduce(function (h, k) {
h[obj[k]] = k;
return h;
}, {});
};
var toSingular = function toSingular(str, world) {
var irregulars = world.irregulars.nouns;
var invert = invertObj(irregulars); //(not very efficient)
// check irregulars list
if (invert.hasOwnProperty(str)) {
return invert[str];
} // go through our regexes
for (var i = 0; i < _rules$1.length; i++) {
if (_rules$1[i][0].test(str) === true) {
str = str.replace(_rules$1[i][0], _rules$1[i][1]);
return str;
}
}
return str;
};
var toSingular_1 = toSingular;
//rules for turning a verb into infinitive form
var rules = {
Participle: [{
reg: /own$/i,
to: 'ow'
}, {
reg: /(.)un([g|k])$/i,
to: '$1in$2'
}],
Actor: [{
reg: /(er)er$/i,
to: '$1'
}],
PresentTense: [{
reg: /(..)(ies)$/i,
to: '$1y'
}, {
reg: /(tch|sh)es$/i,
to: '$1'
}, {
reg: /(ss|zz)es$/i,
to: '$1'
}, {
reg: /([tzlshicgrvdnkmu])es$/i,
to: '$1e'
}, {
reg: /(n[dtk]|c[kt]|[eo]n|i[nl]|er|a[ytrl])s$/i,
to: '$1'
}, {
reg: /(ow)s$/i,
to: '$1'
}, {
reg: /(op)s$/i,
to: '$1'
}, {
reg: /([eirs])ts$/i,
to: '$1t'
}, {
reg: /(ll)s$/i,
to: '$1'
}, {
reg: /(el)s$/i,
to: '$1'
}, {
reg: /(ip)es$/i,
to: '$1e'
}, {
reg: /ss$/i,
to: 'ss'
}, {
reg: /s$/i,
to: ''
}],
Gerund: [{
//popping -> pop
reg: /(..)(p|d|t|g){2}ing$/i,
to: '$1$2'
}, {
//fuzzing -> fuzz
reg: /(ll|ss|zz)ing$/i,
to: '$1'
}, {
reg: /([^aeiou])ying$/i,
to: '$1y'
}, {
reg: /([^ae]i.)ing$/i,
to: '$1e'
}, {
//eating, reading
reg: /(ea[dklnrtv])ing$/i,
to: '$1'
}, {
//washing -> wash
reg: /(ch|sh)ing$/i,
to: '$1'
}, //soft-e forms:
{
//z : hazing (not buzzing)
reg: /(z)ing$/i,
to: '$1e'
}, {
//a : baking, undulating
reg: /(a[gdkvtc])ing$/i,
to: '$1e'
}, {
//u : conjuring, tubing
reg: /(u[rtcbn])ing$/i,
to: '$1e'
}, {
//o : forboding, poking, hoping, boring (not hooping)
reg: /([^o]o[bdknprv])ing$/i,
to: '$1e'
}, {
//ling : tingling, wrinkling, circling, scrambling, bustling
reg: /([tbckg]l)ing$/i,
//dp
to: '$1e'
}, {
//cing : bouncing, denouncing
reg: /(c|s)ing$/i,
//dp
to: '$1e'
}, // {
// //soft-e :
// reg: /([ua]s|[dr]g|z|o[rlsp]|cre)ing$/i,
// to: '$1e',
// },
{
//fallback
reg: /(..)ing$/i,
to: '$1'
}],
PastTense: [{
reg: /(ued)$/i,
to: 'ue'
}, {
reg: /a([^aeiouy])ed$/i,
to: 'a$1e'
}, {
reg: /([aeiou]zz)ed$/i,
to: '$1'
}, {
reg: /(e|i)lled$/i,
to: '$1ll'
}, {
reg: /(.)(sh|ch)ed$/i,
to: '$1$2'
}, {
reg: /(tl|gl)ed$/i,
to: '$1e'
}, {
reg: /(um?pt?)ed$/i,
to: '$1'
}, {
reg: /(ss)ed$/i,
to: '$1'
}, {
reg: /pped$/i,
to: 'p'
}, {
reg: /tted$/i,
to: 't'
}, {
reg: /(..)gged$/i,
to: '$1g'
}, {
reg: /(..)lked$/i,
to: '$1lk'
}, {
reg: /([^aeiouy][aeiou])ked$/i,
to: '$1ke'
}, {
reg: /(.[aeiou])led$/i,
to: '$1l'
}, {
reg: /(..)(h|ion|n[dt]|ai.|[cs]t|pp|all|ss|tt|int|ail|ld|en|oo.|er|k|pp|w|ou.|rt|ght|rm)ed$/i,
to: '$1$2'
}, {
reg: /(.ut)ed$/i,
to: '$1e'
}, {
reg: /(.pt)ed$/i,
to: '$1'
}, {
reg: /(us)ed$/i,
to: '$1e'
}, {
reg: /(dd)ed$/i,
to: '$1'
}, {
reg: /(..[^aeiouy])ed$/i,
to: '$1e'
}, {
reg: /(..)ied$/i,
to: '$1y'
}, {
reg: /(.o)ed$/i,
to: '$1o'
}, {
reg: /(..i)ed$/i,
to: '$1'
}, {
reg: /(.a[^aeiou])ed$/i,
to: '$1'
}, {
//owed, aced
reg: /([aeiou][^aeiou])ed$/i,
to: '$1e'
}, {
reg: /([rl])ew$/i,
to: '$1ow'
}, {
reg: /([pl])t$/i,
to: '$1t'
}]
};
var _transform = rules;
var guessVerb = {
Gerund: ['ing'],
Actor: ['erer'],
Infinitive: ['ate', 'ize', 'tion', 'rify', 'then', 'ress', 'ify', 'age', 'nce', 'ect', 'ise', 'ine', 'ish', 'ace', 'ash', 'ure', 'tch', 'end', 'ack', 'and', 'ute', 'ade', 'ock', 'ite', 'ase', 'ose', 'use', 'ive', 'int', 'nge', 'lay', 'est', 'ain', 'ant', 'ent', 'eed', 'er', 'le', 'own', 'unk', 'ung', 'en'],
PastTense: ['ed', 'lt', 'nt', 'pt', 'ew', 'ld'],
PresentTense: ['rks', 'cks', 'nks', 'ngs', 'mps', 'tes', 'zes', 'ers', 'les', 'acks', 'ends', 'ands', 'ocks', 'lays', 'eads', 'lls', 'els', 'ils', 'ows', 'nds', 'ays', 'ams', 'ars', 'ops', 'ffs', 'als', 'urs', 'lds', 'ews', 'ips', 'es', 'ts', 'ns']
}; //flip it into a lookup object
guessVerb = Object.keys(guessVerb).reduce(function (h, k) {
guessVerb[k].forEach(function (a) {
return h[a] = k;
});
return h;
}, {});
var _guess = guessVerb;
/** it helps to know what we're conjugating from */
var guessTense = function guessTense(str) {
var three = str.substr(str.length - 3);
if (_guess.hasOwnProperty(three) === true) {
return _guess[three];
}
var two = str.substr(str.length - 2);
if (_guess.hasOwnProperty(two) === true) {
return _guess[two];
}
var one = str.substr(str.length - 1);
if (one === 's') {
return 'PresentTense';
}
return null;
};
var toInfinitive = function toInfinitive(str, world, tense) {
if (!str) {
return '';
} //1. look at known irregulars
if (world.words.hasOwnProperty(str) === true) {
var irregs = world.irregulars.verbs;
var keys = Object.keys(irregs);
for (var i = 0; i < keys.length; i++) {
var forms = Object.keys(irregs[keys[i]]);
for (var o = 0; o < forms.length; o++) {
if (str === irregs[keys[i]][forms[o]]) {
return keys[i];
}
}
}
} // give'r!
tense = tense || guessTense(str);
if (tense && _transform[tense]) {
for (var _i = 0; _i < _transform[tense].length; _i++) {
var rule = _transform[tense][_i];
if (rule.reg.test(str) === true) {
// console.log(rule.reg)
return str.replace(rule.reg, rule.to);
}
}
}
return str;
};
var toInfinitive_1 = toInfinitive;
var irregulars$2 = {
nouns: plurals,
verbs: conjugations_1
}; //these behaviours are configurable & shared across some plugins
var transforms$2 = {
conjugate: conjugate_1,
adjectives: adjectives,
toPlural: toPlural,
toSingular: toSingular_1,
toInfinitive: toInfinitive_1
};
var _isVerbose = false;
/** all configurable linguistic data */
var World = /*#__PURE__*/function () {
function World() {
_classCallCheck(this, World);
// quiet these properties from a console.log
Object.defineProperty(this, 'words', {
enumerable: false,
value: misc$1,
writable: true
});
Object.defineProperty(this, 'hasCompound', {
enumerable: false,
value: {},
writable: true
});
Object.defineProperty(this, 'irregulars', {
enumerable: false,
value: irregulars$2,
writable: true
});
Object.defineProperty(this, 'tags', {
enumerable: false,
value: Object.assign({}, tags),
writable: true
});
Object.defineProperty(this, 'transforms', {
enumerable: false,
value: transforms$2,
writable: true
});
Object.defineProperty(this, 'taggers', {
enumerable: false,
value: [],
writable: true
}); // add our compressed data to lexicon
this.unpackWords(_data); // add our irregulars to lexicon
this.addIrregulars(); // cache our abbreviations for our sentence-parser
Object.defineProperty(this, 'cache', {
enumerable: false,
value: {
abbreviations: this.getByTag('Abbreviation')
}
});
}
/** more logs for debugging */
_createClass(World, [{
key: "verbose",
value: function verbose(bool) {
_isVerbose = bool;
return this;
}
}, {
key: "isVerbose",
value: function isVerbose() {
return _isVerbose;
}
/** get all terms in our lexicon with this tag */
}, {
key: "getByTag",
value: function getByTag(tag) {
var lex = this.words;
var res = {};
var words = Object.keys(lex);
for (var i = 0; i < words.length; i++) {
if (typeof lex[words[i]] === 'string') {
if (lex[words[i]] === tag) {
res[words[i]] = true;
}
} else if (lex[words[i]].some(function (t) {
return t === tag;
})) {
res[words[i]] = true;
}
}
return res;
}
/** augment our lingustic data with new data */
}, {
key: "unpackWords",
value: function unpackWords(lex) {
var tags = Object.keys(lex);
for (var i = 0; i < tags.length; i++) {
var words = Object.keys(efrtUnpack_min(lex[tags[i]]));
for (var w = 0; w < words.length; w++) {
addWords.addWord(words[w], tags[i], this.words); // do some fancier stuff
addWords.addMore(words[w], tags[i], this);
}
}
}
/** put new words into our lexicon, properly */
}, {
key: "addWords",
value: function addWords$1(obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
var word = keys[i].toLowerCase();
addWords.addWord(word, obj[keys[i]], this.words); // do some fancier stuff
addWords.addMore(word, obj[keys[i]], this);
}
}
}, {
key: "addIrregulars",
value: function addIrregulars() {
addIrregulars_1(this);
return this;
}
/** extend the compromise tagset */
}, {
key: "addTags",
value: function addTags(tags) {
tags = Object.assign({}, tags);
this.tags = Object.assign(this.tags, tags); // calculate graph implications for the new tags
this.tags = inference(this.tags);
return this;
}
/** call methods after tagger runs */
}, {
key: "postProcess",
value: function postProcess(fn) {
this.taggers.push(fn);
return this;
}
/** helper method for logging + debugging */
}, {
key: "stats",
value: function stats() {
return {
words: Object.keys(this.words).length,
plurals: Object.keys(this.irregulars.nouns).length,
conjugations: Object.keys(this.irregulars.verbs).length,
compounds: Object.keys(this.hasCompound).length,
postProcessors: this.taggers.length
};
}
}]);
return World;
}(); // ¯\_(:/)_/¯
var clone$1 = function clone(obj) {
return JSON.parse(JSON.stringify(obj));
};
/** produce a deep-copy of all lingustic data */
World.prototype.clone = function () {
var w2 = new World(); // these are simple to copy:
w2.words = Object.assign({}, this.words);
w2.hasCompound = Object.assign({}, this.hasCompound); //these ones are nested:
w2.irregulars = clone$1(this.irregulars);
w2.tags = clone$1(this.tags); // these are functions
w2.transforms = this.transforms;
w2.taggers = this.taggers;
return w2;
};
var World_1 = World;
var _01Utils$1 = createCommonjsModule(function (module, exports) {
/** return the root, first document */
exports.all = function () {
return this.parents()[0] || this;
};
/** return the previous result */
exports.parent = function () {
if (this.from) {
return this.from;
}
return this;
};
/** return a list of all previous results */
exports.parents = function (n) {
var arr = [];
var addParent = function addParent(doc) {
if (doc.from) {
arr.push(doc.from);
addParent(doc.from);
}
};
addParent(this);
arr = arr.reverse();
if (typeof n === 'number') {
return arr[n];
}
return arr;
};
/** deep-copy the document, so that no references remain */
exports.clone = function (doShallow) {
var list = this.list.map(function (ts) {
return ts.clone(doShallow);
});
var tmp = this.buildFrom(list);
return tmp;
};
/** how many seperate terms does the document have? */
exports.wordCount = function () {
return this.list.reduce(function (count, p) {
count += p.wordCount();
return count;
}, 0);
};
exports.wordcount = exports.wordCount;
/** turn on logging for decision-debugging */
// exports.verbose = function(bool) {
// if (bool === undefined) {
// bool = true
// }
// this.world.verbose = bool
// }
});
var _02Accessors = createCommonjsModule(function (module, exports) {
/** use only the first result(s) */
exports.first = function (n) {
if (n === undefined) {
return this.get(0);
}
return this.slice(0, n);
};
/** use only the last result(s) */
exports.last = function (n) {
if (n === undefined) {
return this.get(this.list.length - 1);
}
var end = this.list.length;
return this.slice(end - n, end);
};
/** grab a given subset of the results*/
exports.slice = function (start, end) {
var list = this.list.slice(start, end);
return this.buildFrom(list);
};
/* grab nth result */
exports.eq = function (n) {
var p = this.list[n];
if (p === undefined) {
return this.buildFrom([]);
}
return this.buildFrom([p]);
};
exports.get = exports.eq;
/** grab term[0] for every match */
exports.firstTerms = function () {
return this.match('^.');
};
exports.firstTerm = exports.firstTerms;
/** grab the last term for every match */
exports.lastTerms = function () {
return this.match('.$');
};
exports.lastTerm = exports.lastTerms;
/** return a flat array of term objects */
exports.termList = function (num) {
var arr = []; //'reduce' but faster
for (var i = 0; i < this.list.length; i++) {
var terms = this.list[i].terms();
for (var o = 0; o < terms.length; o++) {
arr.push(terms[o]); //support .termList(4)
if (num !== undefined && arr[num] !== undefined) {
return arr[num];
}
}
}
return arr;
};
/** return a flat array of term text strings */
/* @blab+ */
exports.textList = function (num) {
var arr = []; //'reduce' but faster
for (var i = 0; i < this.list.length; i++) {
var terms = this.list[i].terms();
for (var o = 0; o < terms.length; o++) {
arr.push(terms[o]); //support .termList(4)
if (num !== undefined && arr[num] !== undefined) {
return arr[num];
}
}
}
return arr.map(function (term) { return term.text });
};
/* grab named capture group terms as object */
var getGroups = function getGroups(doc) {
var res = {};
var allGroups = {};
var _loop = function _loop(i) {
var phrase = doc.list[i];
var groups = Object.keys(phrase.groups).map(function (k) {
return phrase.groups[k];
});
for (var j = 0; j < groups.length; j++) {
var _groups$j = groups[j],
group = _groups$j.group,
start = _groups$j.start,
length = _groups$j.length;
if (!allGroups[group]) {
allGroups[group] = [];
}
allGroups[group].push(phrase.buildFrom(start, length));
}
};
for (var i = 0; i < doc.list.length; i++) {
_loop(i);
}
var keys = Object.keys(allGroups);
for (var _i = 0; _i < keys.length; _i++) {
var key = keys[_i];
res[key] = doc.buildFrom(allGroups[key]);
}
return res;
};
var getOneName = function getOneName(doc, name) {
var arr = [];
var _loop2 = function _loop2(i) {
var phrase = doc.list[i];
var keys = Object.keys(phrase.groups);
keys = keys.filter(function (id) {
return phrase.groups[id].group === name;
});
keys.forEach(function (id) {
arr.push(phrase.buildFrom(phrase.groups[id].start, phrase.groups[id].length));
});
};
for (var i = 0; i < doc.list.length; i++) {
_loop2(i);
}
return doc.buildFrom(arr);
};
/** grab named capture group results */
exports.groups = function (target) {
if (target === undefined) {
return getGroups(this);
}
if (typeof target === 'number') {
target = String(target);
}
return getOneName(this, target) || this.buildFrom([]);
};
exports.group = exports.groups;
/** get the full-sentence each phrase belongs to */
exports.sentences = function (n) {
var arr = [];
this.list.forEach(function (p) {
arr.push(p.fullSentence());
});
if (typeof n === 'number') {
return this.buildFrom([arr[n]]);
}
return this.buildFrom(arr);
};
exports.sentence = exports.sentences;
});
// cache the easier conditions up-front
var cacheRequired = function cacheRequired(reg) {
var needTags = [];
var needWords = [];
reg.forEach(function (obj) {
if (obj.optional === true || obj.negative === true) {
return;
}
if (obj.tag !== undefined) {
needTags.push(obj.tag);
}
if (obj.word !== undefined) {
needWords.push(obj.word);
}
});
return {
tags: needTags,
words: needWords
};
};
var failFast$1 = function failFast(doc, regs) {
if (doc._cache && doc._cache.set === true) {
var _cacheRequired = cacheRequired(regs),
words = _cacheRequired.words,
tags = _cacheRequired.tags; //check required words
for (var i = 0; i < words.length; i++) {
if (doc._cache.words[words[i]] === undefined) {
return false;
}
} //check required tags
for (var _i = 0; _i < tags.length; _i++) {
if (doc._cache.tags[tags[_i]] === undefined) {
return false;
}
}
}
return true;
};
var checkCache = failFast$1;
var _03Match = createCommonjsModule(function (module, exports) {
/** return a new Doc, with this one as a parent */
exports.match = function (reg, name) {
//parse-up the input expression
var regs = syntax_1(reg);
if (regs.length === 0) {
return this.buildFrom([]);
} //check our cache, if it exists
if (checkCache(this, regs) === false) {
return this.buildFrom([]);
} //try expression on each phrase
var matches = this.list.reduce(function (arr, p) {
return arr.concat(p.match(regs));
}, []);
if (name !== undefined && name !== null && name !== '') {
return this.buildFrom(matches).groups(name);
}
return this.buildFrom(matches);
};
/** return all results except for this */
exports.not = function (reg) {
//parse-up the input expression
var regs = syntax_1(reg); //if it's empty, return them all!
if (regs.length === 0 || checkCache(this, regs) === false) {
return this;
} //try expression on each phrase
var matches = this.list.reduce(function (arr, p) {
return arr.concat(p.not(regs));
}, []);
return this.buildFrom(matches);
};
/** return only the first match */
exports.matchOne = function (reg) {
var regs = syntax_1(reg); //check our cache, if it exists
if (checkCache(this, regs) === false) {
return this.buildFrom([]);
}
for (var i = 0; i < this.list.length; i++) {
var match = this.list[i].match(regs, true);
return this.buildFrom(match);
}
return this.buildFrom([]);
};
/** return each current phrase, only if it contains this match */
exports["if"] = function (reg) {
var regs = syntax_1(reg); //consult our cache, if it exists
if (checkCache(this, regs) === false) {
return this.buildFrom([]);
}
var found = this.list.filter(function (p) {
return p.has(regs) === true;
});
return this.buildFrom(found);
};
/** Filter-out any current phrases that have this match*/
exports.ifNo = function (reg) {
var regs = syntax_1(reg);
var found = this.list.filter(function (p) {
return p.has(regs) === false;
});
return this.buildFrom(found);
};
/**Return a boolean if this match exists */
// function (reg:string|string [],all:boolean) -> boolean
exports.has = function (reg,all) {
// @blab+
if (typeof reg == 'object' && reg.length) {
for(var i in reg) {
var check=exports.has.call(this,reg[i]);
if (all && !check) return false;
if (!all && check) return true;
}
return all?true:false;
}
var regs = syntax_1(reg); //consult our cache, if it exists
if (checkCache(this, regs) === false) {
return false;
}
return this.list.some(function (p) {
return p.has(regs) === true;
});
};
// @blab; contains similar terms?
exports.hasSimilar = function (reg,thres,all) {
var terms = this.termList();
var count=0;
thres=thres||90;
if (typeof reg=='string') reg=[reg];
for(var i in terms) {
for (var j in reg) {
var check = similar_text(terms[i].text,reg[j],1)>thres;
if (!all && check) return true;
if (check) { count++; break }
}
}
return all?count==res.length:false;
}
/** match any terms after our matches, within the sentence */
exports.lookAhead = function (reg) {
// find everything afterwards, by default
if (!reg) {
reg = '.*';
}
var regs = syntax_1(reg);
var matches = [];
this.list.forEach(function (p) {
matches = matches.concat(p.lookAhead(regs));
});
matches = matches.filter(function (p) {
return p;
});
return this.buildFrom(matches);
};
exports.lookAfter = exports.lookAhead;
/** match any terms before our matches, within the sentence */
exports.lookBehind = function (reg) {
// find everything afterwards, by default
if (!reg) {
reg = '.*';
}
var regs = syntax_1(reg);
var matches = [];
this.list.forEach(function (p) {
matches = matches.concat(p.lookBehind(regs));
});
matches = matches.filter(function (p) {
return p;
});
return this.buildFrom(matches);
};
exports.lookBefore = exports.lookBehind;
/** return all terms before a match, in each phrase */
exports.before = function (reg) {
var regs = syntax_1(reg); //only the phrases we care about
var phrases = this["if"](regs).list;
var befores = phrases.map(function (p) {
var ids = p.terms().map(function (t) {
return t.id;
}); //run the search again
var m = p.match(regs)[0];
var index = ids.indexOf(m.start); //nothing is before a first-term match
if (index === 0 || index === -1) {
return null;
}
return p.buildFrom(p.start, index);
});
befores = befores.filter(function (p) {
return p !== null;
});
return this.buildFrom(befores);
};
/** return all terms after a match, in each phrase */
exports.after = function (reg) {
var regs = syntax_1(reg); //only the phrases we care about
var phrases = this["if"](regs).list;
var befores = phrases.map(function (p) {
var terms = p.terms();
var ids = terms.map(function (t) {
return t.id;
}); //run the search again
var m = p.match(regs)[0];
var index = ids.indexOf(m.start); //skip if nothing is after it
if (index === -1 || !terms[index + m.length]) {
return null;
} //create the new phrase, after our match.
var id = terms[index + m.length].id;
var len = p.length - index - m.length;
return p.buildFrom(id, len);
});
befores = befores.filter(function (p) {
return p !== null;
});
return this.buildFrom(befores);
};
/** return only results with this match afterwards */
exports.hasAfter = function (reg) {
return this.filter(function (doc) {
return doc.lookAfter(reg).found;
});
};
/** return only results with this match before it */
exports.hasBefore = function (reg) {
return this.filter(function (doc) {
return doc.lookBefore(reg).found;
});
};
});
/** apply a tag, or tags to all terms */
var tagTerms = function tagTerms(tag, doc, safe, reason) {
var tagList = [];
if (typeof tag === 'string') {
tagList = tag.split(' ');
} //do indepenent tags for each term:
doc.list.forEach(function (p) {
var terms = p.terms(); // tagSafe - apply only to fitting terms
if (safe === true) {
terms = terms.filter(function (t) {
return t.canBe(tag, doc.world);
});
}
terms.forEach(function (t, i) {
//fancy version:
if (tagList.length > 1) {
if (tagList[i] && tagList[i] !== '.') {
t.tag(tagList[i], reason, doc.world);
}
} else {
//non-fancy version (same tag for all terms)
t.tag(tag, reason, doc.world);
}
});
});
return;
};
var _setTag = tagTerms;
/** Give all terms the given tag */
var tag$1 = function tag(tags, why) {
if (!tags) {
return this;
}
_setTag(tags, this, false, why);
return this;
};
/** Only apply tag to terms if it is consistent with current tags */
var tagSafe$1 = function tagSafe(tags, why) {
if (!tags) {
return this;
}
_setTag(tags, this, true, why);
return this;
};
/** Remove this term from the given terms */
var unTag$1 = function unTag(tags, why) {
var _this = this;
this.list.forEach(function (p) {
p.terms().forEach(function (t) {
return t.unTag(tags, why, _this.world);
});
});
return this;
};
/** return only the terms that can be this tag*/
var canBe$2 = function canBe(tag) {
if (!tag) {
return this;
}
var world = this.world;
var matches = this.list.reduce(function (arr, p) {
return arr.concat(p.canBe(tag, world));
}, []);
return this.buildFrom(matches);
};
var _04Tag = {
tag: tag$1,
tagSafe: tagSafe$1,
unTag: unTag$1,
canBe: canBe$2
};
/* run each phrase through a function, and create a new document */
var map = function map(fn) {
var _this = this;
if (!fn) {
return this;
}
var list = this.list.map(function (p, i) {
var doc = _this.buildFrom([p]);
doc.from = null; //it's not a child/parent
var res = fn(doc, i); // if its a doc, return one result
if (res && res.list && res.list[0]) {
return res.list[0];
}
return res;
}); //remove nulls
list = list.filter(function (x) {
return x;
}); // return an empty response
if (list.length === 0) {
return this.buildFrom(list);
} // if it is not a list of Phrase objects, then don't try to make a Doc object
if (_typeof(list[0]) !== 'object' || list[0].isA !== 'Phrase') {
return list;
}
return this.buildFrom(list);
};
/** run a function on each phrase */
var forEach = function forEach(fn, detachParent) {
var _this2 = this;
if (!fn) {
return this;
}
this.list.forEach(function (p, i) {
var sub = _this2.buildFrom([p]); // if we're doing fancy insertions, we may want to skip updating the parent each time.
if (detachParent === true) {
sub.from = null; //
}
fn(sub, i);
});
return this;
};
/** return only the phrases that return true */
var filter = function filter(fn) {
var _this3 = this;
if (!fn) {
return this;
}
var list = this.list.filter(function (p, i) {
var doc = _this3.buildFrom([p]);
doc.from = null; //it's not a child/parent
return fn(doc, i);
});
return this.buildFrom(list);
};
/** return a document with only the first phrase that matches */
var find = function find(fn) {
var _this4 = this;
if (!fn) {
return this;
}
var phrase = this.list.find(function (p, i) {
var doc = _this4.buildFrom([p]);
doc.from = null; //it's not a child/parent
return fn(doc, i);
});
if (phrase) {
return this.buildFrom([phrase]);
}
return undefined;
};
/** return true or false if there is one matching phrase */
var some = function some(fn) {
var _this5 = this;
if (!fn) {
return this;
}
return this.list.some(function (p, i) {
var doc = _this5.buildFrom([p]);
doc.from = null; //it's not a child/parent
return fn(doc, i);
});
};
/** sample a subset of the results */
var random = function random(n) {
if (!this.found) {
return this;
}
var r = Math.floor(Math.random() * this.list.length);
if (n === undefined) {
var list = [this.list[r]];
return this.buildFrom(list);
} //prevent it from going over the end
if (r + n > this.length) {
r = this.length - n;
r = r < 0 ? 0 : r;
}
return this.slice(r, r + n);
};
/** combine each phrase into a new data-structure */
// exports.reduce = function(fn, h) {
// let list = this.list.reduce((_h, ts) => {
// let doc = this.buildFrom([ts])
// doc.from = null //it's not a child/parent
// return fn(_h, doc)
// }, h)
// return this.buildFrom(list)
// }
var _05Loops = {
map: map,
forEach: forEach,
filter: filter,
find: find,
some: some,
random: random
};
// const tokenize = require('../../01-tokenizer/02-words')
var tokenize = function tokenize(str) {
return str.split(/[ -]/g);
}; // take a list of strings
// look them up in the document
var buildTree = function buildTree(termList) {
var values = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var root = {}; // parse our input
termList.forEach(function (str, i) {
var val = true;
if (values[i] !== undefined) {
val = values[i];
} // some rough normalization
str = (str || '').toLowerCase();
str = str.replace(/[,;.!?]+$/, '');
var arr = tokenize(str).map(function (s) {
return s.trim();
});
root[arr[0]] = root[arr[0]] || {};
if (arr.length === 1) {
root[arr[0]].value = val;
} else {
root[arr[0]].more = root[arr[0]].more || [];
root[arr[0]].more.push({
rest: arr.slice(1),
value: val
});
}
}); // sort by longest-first?
// console.log(JSON.stringify(root, null, 2))
return root;
};
var fastLookup = function fastLookup(termList, values, doc) {
var root = buildTree(termList, values);
var found = []; // each phrase
var _loop = function _loop(i) {
var p = doc.list[i];
var terms = p.terms();
var words = terms.map(function (t) {
return t.reduced;
}); // each word
var _loop2 = function _loop2(w) {
if (root[words[w]] !== undefined) {
// is it a multi-word match?
if (root[words[w]].more !== undefined) {
root[words[w]].more.forEach(function (more) {
// is it too-long?
if (words[w + more.rest.length] === undefined) {
return;
} // compare each subsequent term
var everyTerm = more.rest.every(function (word, r) {
return word === words[w + r + 1];
});
if (everyTerm === true) {
found.push({
id: p.terms()[w].id,
value: more.value,
length: more.rest.length + 1
});
}
});
} // is it a single-word match?
if (root[words[w]].value !== undefined) {
found.push({
id: p.terms()[w].id,
value: root[words[w]].value,
length: 1
});
}
}
};
for (var w = 0; w < words.length; w++) {
_loop2(w);
}
};
for (var i = 0; i < doc.list.length; i++) {
_loop(i);
}
return found;
};
var _lookup = fastLookup;
var _06Lookup = createCommonjsModule(function (module, exports) {
// compare one term and one match
// const doesMatch = function(term, str) {
// if (str === '') {
// return false
// }
// return term.reduced === str || term.implicit === str || term.root === str || term.text.toLowerCase() === str
// }
var isObject = function isObject(obj) {
return obj && Object.prototype.toString.call(obj) === '[object Object]';
};
/** lookup an array of words or phrases */
exports.lookup = function (arr) {
var _this = this;
var values = []; //is it a {key:val} object?
var isObj = isObject(arr);
if (isObj === true) {
arr = Object.keys(arr).map(function (k) {
values.push(arr[k]);
return k;
});
} // support .lookup('foo')
if (typeof arr === 'string') {
arr = [arr];
} //make sure we go fast.
if (this._cache.set !== true) {
this.cache();
}
var found = _lookup(arr, values, this);
var p = this.list[0]; // make object response
if (isObj === true) {
var byVal = {};
found.forEach(function (o) {
byVal[o.value] = byVal[o.value] || [];
byVal[o.value].push(p.buildFrom(o.id, o.length));
});
Object.keys(byVal).forEach(function (k) {
byVal[k] = _this.buildFrom(byVal[k]);
});
return byVal;
} // otherwise, make array response:
found = found.map(function (o) {
return p.buildFrom(o.id, o.length);
});
return this.buildFrom(found);
};
exports.lookUp = exports.lookup;
});
/** freeze the current state of the document, for speed-purposes*/
var cache$1 = function cache(options) {
var _this = this;
options = options || {};
var words = {};
var tags = {};
this._cache.words = words;
this._cache.tags = tags;
this._cache.set = true;
this.list.forEach(function (p, i) {
p.cache = p.cache || {}; //p.terms get cached automatically
var terms = p.terms(); // cache all the terms
terms.forEach(function (t) {
if (words[t.reduced] && !words.hasOwnProperty(t.reduced)) {
return; //skip prototype words
}
words[t.reduced] = words[t.reduced] || [];
words[t.reduced].push(i);
Object.keys(t.tags).forEach(function (tag) {
tags[tag] = tags[tag] || [];
tags[tag].push(i);
}); // cache root-form on Term, too
if (options.root) {
t.setRoot(_this.world);
words[t.root] = true;
}
});
});
return this;
};
/** un-freezes the current state of the document, so it may be transformed */
var uncache = function uncache() {
this._cache = {};
this.list.forEach(function (p) {
p.cache = {};
}); // do parents too?
this.parents().forEach(function (doc) {
doc._cache = {};
doc.list.forEach(function (p) {
p.cache = {};
});
});
return this;
};
var _07Cache = {
cache: cache$1,
uncache: uncache
};
var titleCase$3 = function titleCase(str) {
return str.charAt(0).toUpperCase() + str.substr(1);
};
/** substitute-in new content */
var replaceWith = function replaceWith(replace) {
var _this = this;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (!replace) {
return this["delete"]();
} //support old-style params
if (options === true) {
options = {
keepTags: true
};
}
if (options === false) {
options = {
keepTags: false
};
}
options = options || {}; // clear the cache
this.uncache(); // return this
this.list.forEach(function (p) {
var input = replace; // accept a function for replace
if (typeof replace === 'function') {
input = replace(p);
}
var newPhrases; // accept a Doc object to replace
if (input && _typeof(input) === 'object' && input.isA === 'Doc') {
newPhrases = input.list;
_this.pool().merge(input.pool());
} else if (typeof input === 'string') {
//input is a string
if (options.keepCase !== false && p.terms(0).isTitleCase()) {
input = titleCase$3(input);
}
newPhrases = _01Tokenizer(input, _this.world, _this.pool()); //tag the new phrases
var tmpDoc = _this.buildFrom(newPhrases);
tmpDoc.tagger();
newPhrases = tmpDoc.list;
} else {
return; //don't even bother
} // try to keep its old tags, if appropriate
if (options.keepTags === true) {
var oldTags = p.json({
terms: {
tags: true
}
}).terms;
newPhrases[0].terms().forEach(function (t, i) {
if (oldTags[i]) {
t.tagSafe(oldTags[i].tags, 'keptTag', _this.world);
}
});
}
p.replace(newPhrases[0], _this); //Oneday: support multi-sentence replacements
});
return this;
};
/** search and replace match with new content */
var replace$1 = function replace(match, _replace, options) {
// if there's no 2nd param, use replaceWith
if (_replace === undefined) {
return this.replaceWith(match, options);
}
this.match(match).replaceWith(_replace, options);
return this;
};
var _01Replace = {
replaceWith: replaceWith,
replace: replace$1
};
var _02Insert = createCommonjsModule(function (module, exports) {
// if it's empty, just create the phrase
var makeNew = function makeNew(str, doc) {
var phrase = _01Tokenizer(str, doc.world)[0]; //assume it's one sentence, for now
var tmpDoc = doc.buildFrom([phrase]);
tmpDoc.tagger();
doc.list = tmpDoc.list;
return doc;
};
/** add these new terms to the end*/
exports.append = function (str) {
var _this = this;
if (!str) {
return this;
} // if it's empty, just create the phrase
if (!this.found) {
return makeNew(str, this);
} // clear the cache
this.uncache(); //add it to end of every phrase
this.list.forEach(function (p) {
//build it
var phrase = _01Tokenizer(str, _this.world, _this.pool())[0]; //assume it's one sentence, for now
//tag it
var tmpDoc = _this.buildFrom([phrase]);
tmpDoc.tagger(); // push it onto the end
p.append(phrase, _this);
});
return this;
};
exports.insertAfter = exports.append;
exports.insertAt = exports.append;
/** add these new terms to the front*/
exports.prepend = function (str) {
var _this2 = this;
if (!str) {
return this;
} // if it's empty, just create the phrase
if (!this.found) {
return makeNew(str, this);
} // clear the cache
this.uncache(); //add it to start of every phrase
this.list.forEach(function (p) {
//build it
var phrase = _01Tokenizer(str, _this2.world, _this2.pool())[0]; //assume it's one sentence, for now
//tag it
var tmpDoc = _this2.buildFrom([phrase]);
tmpDoc.tagger(); // add it to the start
p.prepend(phrase, _this2);
});
return this;
};
exports.insertBefore = exports.prepend;
/** add these new things to the end*/
exports.concat = function () {
// clear the cache
this.uncache();
var list = this.list.slice(0); //repeat for any number of params
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i]; //support a fresh string
if (typeof arg === 'string') {
var arr = _01Tokenizer(arg, this.world); //TODO: phrase.tagger()?
list = list.concat(arr);
} else if (arg.isA === 'Doc') {
list = list.concat(arg.list);
} else if (arg.isA === 'Phrase') {
list.push(arg);
}
}
return this.buildFrom(list);
};
/** fully remove these terms from the document */
exports["delete"] = function (match) {
var _this3 = this;
// clear the cache
this.uncache();
var toRemove = this;
if (match) {
toRemove = this.match(match);
}
toRemove.list.forEach(function (phrase) {
return phrase["delete"](_this3);
});
return this;
}; // aliases
exports.remove = exports["delete"];
});
var shouldTrim = {
clean: true,
reduced: true,
root: true
};
/** return the document as text */
var text$1 = function text(options) {
var _this = this;
options = options || {}; //are we showing every phrase?
var showFull = false;
if (this.parents().length === 0) {
showFull = true;
} // cache roots, if necessary
if (options === 'root' || _typeof(options) === 'object' && options.root) {
this.list.forEach(function (p) {
p.terms().forEach(function (t) {
if (t.root === null) {
t.setRoot(_this.world);
}
});
});
}
var txt = this.list.reduce(function (str, p, i) {
var trimPre = !showFull && i === 0;
var trimPost = !showFull && i === _this.list.length - 1;
return str + p.text(options, trimPre, trimPost);
}, ''); // clumsy final trim of leading/trailing whitespace
if (shouldTrim[options] === true || options.reduced === true || options.clean === true || options.root === true) {
txt = txt.trim();
}
return txt;
};
var _01Text = {
text: text$1
};
// get all character startings in doc
var termOffsets = function termOffsets(doc) {
var elapsed = 0;
var index = 0;
var offsets = {};
doc.termList().forEach(function (term) {
offsets[term.id] = {
index: index,
start: elapsed + term.pre.length,
length: term.text.length
};
elapsed += term.pre.length + term.text.length + term.post.length;
index += 1;
});
return offsets;
};
var calcOffset = function calcOffset(doc, result, options) {
// calculate offsets for each term
var offsets = termOffsets(doc.all()); // add index values
if (options.terms.index || options.index) {
result.forEach(function (o) {
o.terms.forEach(function (t) {
t.index = offsets[t.id].index;
});
o.index = o.terms[0].index;
});
} // add offset values
if (options.terms.offset || options.offset) {
result.forEach(function (o) {
o.terms.forEach(function (t) {
t.offset = offsets[t.id] || {};
}); // let len = o.terms.reduce((n, t, i) => {
// n += t.offset.length || 0
// //add whitespace, too
// console.log(t.post)
// return n
// }, 0)
// The offset information for the entire doc starts at (or just before)
// the first term, and is as long as the whole text. The code originally
// copied the entire offset value from terms[0], but since we're now
// overriding 2 of the three fields, it's cleaner to just create an all-
// new object and not pretend it's "just" the same as terms[0].
o.offset = {
index: o.terms[0].offset.index,
start: o.terms[0].offset.start - o.text.indexOf(o.terms[0].text),
length: o.text.length
};
});
}
};
var _offset = calcOffset;
var _02Json = createCommonjsModule(function (module, exports) {
var jsonDefaults = {
text: true,
terms: true,
trim: true
}; //some options have dependents
var setOptions = function setOptions(options) {
options = Object.assign({}, jsonDefaults, options);
if (options.unique) {
options.reduced = true;
} //offset calculation requires these options to be on
if (options.offset) {
options.text = true;
if (!options.terms || options.terms === true) {
options.terms = {};
}
options.terms.offset = true;
}
if (options.index || options.terms.index) {
options.terms = options.terms === true ? {} : options.terms;
options.terms.id = true;
}
return options;
};
/** pull out desired metadata from the document */
exports.json = function () {
var _this = this;
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
//support json(3) format
if (typeof options === 'number' && this.list[options]) {
return this.list[options].json(jsonDefaults);
}
options = setOptions(options); // cache root strings beforehand, if necessary
if (options.root === true) {
this.list.forEach(function (p) {
p.terms().forEach(function (t) {
if (t.root === null) {
t.setRoot(_this.world);
}
});
});
}
var result = this.list.map(function (p) {
return p.json(options, _this.world);
}); // add offset and index data for each term
if (options.terms.offset || options.offset || options.terms.index || options.index) {
_offset(this, result, options);
} // add frequency #s
if (options.frequency || options.freq || options.count) {
var obj = {};
this.list.forEach(function (p) {
var str = p.text('reduced');
obj[str] = obj[str] || 0;
obj[str] += 1;
});
this.list.forEach(function (p, i) {
result[i].count = obj[p.text('reduced')];
});
} // remove duplicates
if (options.unique) {
var already = {};
result = result.filter(function (o) {
if (already[o.reduced] === true) {
return false;
}
already[o.reduced] = true;
return true;
});
}
return result;
}; //aliases
exports.data = exports.json;
});
var _debug = createCommonjsModule(function (module) {
// https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color
var reset = '\x1b[0m';
var padEnd = function padEnd(str, width) {
str = str.toString();
while (str.length < width) {
str += ' ';
}
return str;
};
function isClientSide() {
return typeof window !== 'undefined' && window.document;
} // some nice colors for client-side debug
var css = {
green: '#7f9c6c',
red: '#914045',
blue: '#6699cc',
magenta: '#6D5685',
cyan: '#2D85A8',
yellow: '#e6d7b3',
black: '#303b50'
};
var logClientSide = function logClientSide(doc) {
var tagset = doc.world.tags;
doc.list.forEach(function (p) {
console.log('\n%c"' + p.text() + '"', 'color: #e6d7b3;');
var terms = p.terms();
terms.forEach(function (t) {
var tags = Object.keys(t.tags);
var text = t.text || '-';
if (t.implicit) {
text = '[' + t.implicit + ']';
}
var word = "'" + text + "'";
word = padEnd(word, 8);
var found = tags.find(function (tag) {
return tagset[tag] && tagset[tag].color;
});
var color = 'steelblue';
if (tagset[found]) {
color = tagset[found].color;
color = css[color];
}
console.log(" ".concat(word, " - %c").concat(tags.join(', ')), "color: ".concat(color || 'steelblue', ";"));
});
});
}; //cheaper than requiring chalk
var cli = {
green: function green(str) {
return '\x1b[32m' + str + reset;
},
red: function red(str) {
return '\x1b[31m' + str + reset;
},
blue: function blue(str) {
return '\x1b[34m' + str + reset;
},
magenta: function magenta(str) {
return '\x1b[35m' + str + reset;
},
cyan: function cyan(str) {
return '\x1b[36m' + str + reset;
},
yellow: function yellow(str) {
return '\x1b[33m' + str + reset;
},
black: function black(str) {
return '\x1b[30m' + str + reset;
}
};
var tagString = function tagString(tags, world) {
tags = tags.map(function (tag) {
if (!world.tags.hasOwnProperty(tag)) {
return tag;
}
var c = world.tags[tag].color || 'blue';
return cli[c](tag);
});
return tags.join(', ');
}; //output some helpful stuff to the console
var debug = function debug(doc) {
if (isClientSide()) {
logClientSide(doc);
return doc;
}
console.log(cli.blue('====='));
doc.list.forEach(function (p) {
console.log(cli.blue(' -----'));
var terms = p.terms();
terms.forEach(function (t) {
var tags = Object.keys(t.tags);
var text = t.text || '-';
if (t.implicit) {
text = '[' + t.implicit + ']';
}
{
text = cli.yellow(text);
}
var word = "'" + text + "'";
word = padEnd(word, 18);
var str = cli.blue(' ') + word + ' - ' + tagString(tags, doc.world);
console.log(str);
});
});
console.log('');
return doc;
};
module.exports = debug;
});
var topk = function topk(doc) {
var list = doc.json({
text: false,
terms: false,
reduced: true
}); // combine them
var obj = {};
list.forEach(function (o) {
if (!obj[o.reduced]) {
o.count = 0;
obj[o.reduced] = o;
}
obj[o.reduced].count += 1;
});
var arr = Object.keys(obj).map(function (k) {
return obj[k];
}); // sort them
arr.sort(function (a, b) {
if (a.count > b.count) {
return -1;
} else if (a.count < b.count) {
return 1;
}
return 0;
});
return arr;
};
var _topk = topk;
/** pretty-print the current document and its tags */
var debug_1 = function debug_1() {
_debug(this);
return this;
};
/** some named output formats */
var out = function out(method) {
if (method === 'text') {
return this.text();
}
if (method === 'normal') {
return this.text('normal');
}
if (method === 'json') {
return this.json();
}
if (method === 'offset' || method === 'offsets') {
return this.json({
offset: true
});
}
if (method === 'array') {
return this.json({
terms: false
}).map(function (obj) {
return obj.text;
});
}
if (method === 'freq' || method === 'frequency') {
return _topk(this);
}
if (method === 'terms') {
var list = [];
this.json({
text: false,
terms: {
text: true
}
}).forEach(function (obj) {
var terms = obj.terms.map(function (t) {
return t.text;
});
terms = terms.filter(function (t) {
return t;
});
list = list.concat(terms);
});
return list;
}
if (method === 'tags') {
return this.list.map(function (p) {
return p.terms().reduce(function (h, t) {
h[t.clean || t.implicit] = Object.keys(t.tags);
return h;
}, {});
});
}
if (method === 'debug') {
_debug(this);
return this;
}
return this.text();
};
var _03Out = {
debug: debug_1,
out: out
};
var methods$2 = {
/** alphabetical order */
alpha: function alpha(a, b) {
var left = a.text('clean');
var right = b.text('clean');
if (left < right) {
return -1;
}
if (left > right) {
return 1;
}
return 0;
},
/** count the # of characters of each match */
length: function length(a, b) {
var left = a.text().trim().length;
var right = b.text().trim().length;
if (left < right) {
return 1;
}
if (left > right) {
return -1;
}
return 0;
},
/** count the # of terms in each match */
wordCount: function wordCount(a, b) {
var left = a.wordCount();
var right = b.wordCount();
if (left < right) {
return 1;
}
if (left > right) {
return -1;
}
return 0;
}
};
/** sort by # of duplicates in the document*/
var byFreq = function byFreq(doc) {
var counts = {};
var options = {
"case": true,
punctuation: false,
whitespace: true,
unicode: true
};
doc.list.forEach(function (p) {
var str = p.text(options);
counts[str] = counts[str] || 0;
counts[str] += 1;
}); // sort by freq
doc.list.sort(function (a, b) {
var left = counts[a.text(options)];
var right = counts[b.text(options)];
if (left < right) {
return 1;
}
if (left > right) {
return -1;
}
return 0;
});
return doc;
}; // order results 'chronologically', or document-order
var sortSequential = function sortSequential(doc) {
var order = {};
doc.json({
terms: {
offset: true
}
}).forEach(function (o) {
order[o.terms[0].id] = o.terms[0].offset.start;
});
doc.list = doc.list.sort(function (a, b) {
if (order[a.start] > order[b.start]) {
return 1;
} else if (order[a.start] < order[b.start]) {
return -1;
}
return 0;
});
return doc;
}; //aliases
methods$2.alphabetical = methods$2.alpha;
methods$2.wordcount = methods$2.wordCount; // aliases for sequential ordering
var seqNames = {
index: true,
sequence: true,
seq: true,
sequential: true,
chron: true,
chronological: true
};
/** re-arrange the order of the matches (in place) */
var sort = function sort(input) {
input = input || 'alpha'; //do this one up-front
if (input === 'freq' || input === 'frequency' || input === 'topk') {
return byFreq(this);
}
if (seqNames.hasOwnProperty(input)) {
return sortSequential(this);
}
input = methods$2[input] || input; // apply sort method on each phrase
if (typeof input === 'function') {
this.list = this.list.sort(input);
return this;
}
return this;
};
/** reverse the order of the matches, but not the words */
var reverse = function reverse() {
var list = [].concat(this.list);
list = list.reverse();
return this.buildFrom(list);
};
/** remove any duplicate matches */
var unique$4 = function unique() {
var list = [].concat(this.list);
var obj = {};
list = list.filter(function (p) {
var str = p.text('reduced').trim();
if (obj.hasOwnProperty(str) === true) {
return false;
}
obj[str] = true;
return true;
});
return this.buildFrom(list);
};
var _01Sort = {
sort: sort,
reverse: reverse,
unique: unique$4
};
var isPunct = /[\[\]{}⟨⟩:,،、‒–—―…‹›«»‐\-;\/⁄·*\•^†‡°¡¿※№÷׺ª%‰=‱¶§~|‖¦©℗®℠™¤₳฿]/g;
var quotes = /['‘’“”"′″‴]+/g;
var methods$3 = {
// cleanup newlines and extra spaces
whitespace: function whitespace(doc) {
var termArr = doc.list.map(function (ts) {
return ts.terms();
});
termArr.forEach(function (terms, o) {
terms.forEach(function (t, i) {
// keep dashes between words
if (t.hasDash() === true) {
t.post = ' - ';
return;
} // remove existing spaces
t.pre = t.pre.replace(/\s/g, '');
t.post = t.post.replace(/\s/g, ''); //last word? ensure there's a next sentence.
if (terms.length - 1 === i && !termArr[o + 1]) {
return;
} // no extra spaces for contractions
if (t.implicit && Boolean(t.text) === true) {
return;
} // no extra spaces for hyphenated words
if (t.hasHyphen() === true) {
return;
}
t.post += ' ';
});
});
},
punctuation: function punctuation(termList) {
termList.forEach(function (t) {
// space between hyphenated words
if (t.hasHyphen() === true) {
t.post = ' ';
}
t.pre = t.pre.replace(isPunct, '');
t.post = t.post.replace(isPunct, ''); // elipses
t.post = t.post.replace(/\.\.\./, ''); // only allow one exclamation
if (/!/.test(t.post) === true) {
t.post = t.post.replace(/!/g, '');
t.post = '!' + t.post;
} // only allow one question mark
if (/\?/.test(t.post) === true) {
t.post = t.post.replace(/[\?!]*/, '');
t.post = '?' + t.post;
}
});
},
unicode: function unicode(termList) {
termList.forEach(function (t) {
if (t.isImplicit() === true) {
return;
}
t.text = unicode_1(t.text);
});
},
quotations: function quotations(termList) {
termList.forEach(function (t) {
t.post = t.post.replace(quotes, '');
t.pre = t.pre.replace(quotes, '');
});
},
adverbs: function adverbs(doc) {
doc.match('#Adverb').not('(not|nary|seldom|never|barely|almost|basically|so)').remove();
},
// remove the '.' from 'Mrs.' (safely)
abbreviations: function abbreviations(doc) {
doc.list.forEach(function (ts) {
var terms = ts.terms();
terms.forEach(function (t, i) {
if (t.tags.Abbreviation === true && terms[i + 1]) {
t.post = t.post.replace(/^\./, '');
}
});
});
}
};
var _methods = methods$3;
var defaults = {
// light
whitespace: true,
unicode: true,
punctuation: true,
emoji: true,
acronyms: true,
abbreviations: true,
// medium
"case": false,
contractions: false,
parentheses: false,
quotations: false,
adverbs: false,
// heavy (loose legibility)
possessives: false,
verbs: false,
nouns: false,
honorifics: false // pronouns: true,
};
var mapping$1 = {
light: {},
medium: {
"case": true,
contractions: true,
parentheses: true,
quotations: true,
adverbs: true
}
};
mapping$1.heavy = Object.assign({}, mapping$1.medium, {
possessives: true,
verbs: true,
nouns: true,
honorifics: true
});
/** common ways to clean-up the document, and reduce noise */
var normalize = function normalize(options) {
options = options || {}; // support named forms
if (typeof options === 'string') {
options = mapping$1[options] || {};
} // set defaults
options = Object.assign({}, defaults, options); // clear the cache
this.uncache();
var termList = this.termList(); // lowercase things
if (options["case"]) {
this.toLowerCase();
} //whitespace
if (options.whitespace) {
_methods.whitespace(this);
} // unicode: é -> e
if (options.unicode) {
_methods.unicode(termList);
} //punctuation - keep sentence punctation, quotes, parenths
if (options.punctuation) {
_methods.punctuation(termList);
} // remove ':)'
if (options.emoji) {
this.remove('(#Emoji|#Emoticon)');
} // 'f.b.i.' -> 'FBI'
if (options.acronyms) {
this.acronyms().strip(); // .toUpperCase()
} // remove period from abbreviations
if (options.abbreviations) {
_methods.abbreviations(this);
} // --Medium methods--
// `isn't` -> 'is not'
if (options.contraction || options.contractions) {
this.contractions().expand();
} // '(word)' -> 'word'
if (options.parentheses) {
this.parentheses().unwrap();
} // remove "" punctuation
if (options.quotations || options.quotes) {
_methods.quotations(termList);
} // remove any un-necessary adverbs
if (options.adverbs) {
_methods.adverbs(this);
} // --Heavy methods--
// `cory hart's -> cory hart'
if (options.possessive || options.possessives) {
this.possessives().strip();
} // 'he walked' -> 'he walk'
if (options.verbs) {
this.verbs().toInfinitive();
} // 'three dogs' -> 'three dog'
if (options.nouns || options.plurals) {
this.nouns().toSingular();
} // remove 'Mr.' from 'Mr John Smith'
if (options.honorifics) {
this.remove('#Honorific');
}
return this;
};
var _02Normalize = {
normalize: normalize
};
var _03Split = createCommonjsModule(function (module, exports) {
/** return a Document with three parts for every match
* seperate everything before the word, as a new phrase
*/
exports.splitOn = function (reg) {
// if there's no match, split parent, instead
if (!reg) {
var parent = this.parent();
return parent.splitOn(this);
} //start looking for a match..
var regs = syntax_1(reg);
var matches = [];
this.list.forEach(function (p) {
var foundEm = p.match(regs); //no match here, add full sentence
if (foundEm.length === 0) {
matches.push(p);
return;
} // we found something here.
var carry = p;
foundEm.forEach(function (found) {
var parts = carry.splitOn(found); // add em in
if (parts.before) {
matches.push(parts.before);
}
if (parts.match) {
matches.push(parts.match);
} // start matching now on the end
carry = parts.after;
}); // add that last part
if (carry) {
matches.push(carry);
}
});
return this.buildFrom(matches);
};
/** return a Document with two parts for every match
* seperate everything after the word, as a new phrase
*/
exports.splitAfter = function (reg) {
// if there's no match, split parent, instead
if (!reg) {
var parent = this.parent();
return parent.splitAfter(this);
} // start looking for our matches
var regs = syntax_1(reg);
var matches = [];
this.list.forEach(function (p) {
var foundEm = p.match(regs); //no match here, add full sentence
if (foundEm.length === 0) {
matches.push(p);
return;
} // we found something here.
var carry = p;
foundEm.forEach(function (found) {
var parts = carry.splitOn(found); // add em in
if (parts.before && parts.match) {
// merge these two together
parts.before.length += parts.match.length;
matches.push(parts.before);
} else if (parts.match) {
matches.push(parts.match);
} // start matching now on the end
carry = parts.after;
}); // add that last part
if (carry) {
matches.push(carry);
}
});
return this.buildFrom(matches);
};
exports.split = exports.splitAfter; //i guess?
/** return a Document with two parts for every match */
exports.splitBefore = function (reg) {
// if there's no match, split parent, instead
if (!reg) {
var parent = this.parent();
return parent.splitBefore(this);
} //start looking for a match..
var regs = syntax_1(reg);
var matches = [];
this.list.forEach(function (p) {
var foundEm = p.match(regs); //no match here, add full sentence
if (foundEm.length === 0) {
matches.push(p);
return;
} // we found something here.
var carry = p;
foundEm.forEach(function (found) {
var parts = carry.splitOn(found); // add before part in
if (parts.before) {
matches.push(parts.before);
} // merge match+after
if (parts.match && parts.after) {
parts.match.length += parts.after.length;
} // start matching now on the end
carry = parts.match;
}); // add that last part
if (carry) {
matches.push(carry);
}
});
return this.buildFrom(matches);
};
/** split a document into labeled sections */
exports.segment = function (regs, options) {
regs = regs || {};
options = options || {
text: true
};
var doc = this;
var keys = Object.keys(regs); // split em
keys.forEach(function (k) {
doc = doc.splitOn(k);
}); //add labels for each section
doc.list.forEach(function (p) {
for (var i = 0; i < keys.length; i += 1) {
if (p.has(keys[i])) {
p.segment = regs[keys[i]];
return;
}
}
});
return doc.list.map(function (p) {
var res = p.json(options);
res.segment = p.segment || null;
return res;
});
};
});
var eachTerm = function eachTerm(doc, fn) {
var world = doc.world;
doc.list.forEach(function (p) {
p.terms().forEach(function (t) {
return t[fn](world);
});
});
return doc;
};
/** turn every letter of every term to lower-cse */
var toLowerCase = function toLowerCase() {
return eachTerm(this, 'toLowerCase');
};
/** turn every letter of every term to upper case */
var toUpperCase = function toUpperCase() {
return eachTerm(this, 'toUpperCase');
};
/** upper-case the first letter of each term */
var toTitleCase = function toTitleCase() {
return eachTerm(this, 'toTitleCase');
};
/** remove whitespace and title-case each term */
var toCamelCase = function toCamelCase() {
this.list.forEach(function (p) {
//remove whitespace
var terms = p.terms();
terms.forEach(function (t, i) {
if (i !== 0) {
t.toTitleCase();
}
if (i !== terms.length - 1) {
t.post = '';
}
});
}); // this.tag('#CamelCase', 'toCamelCase')
return this;
};
var _04Case = {
toLowerCase: toLowerCase,
toUpperCase: toUpperCase,
toTitleCase: toTitleCase,
toCamelCase: toCamelCase
};
var _05Whitespace = createCommonjsModule(function (module, exports) {
/** add this punctuation or whitespace before each match: */
exports.pre = function (str, concat) {
if (str === undefined) {
return this.list[0].terms(0).pre;
}
this.list.forEach(function (p) {
var term = p.terms(0);
if (concat === true) {
term.pre += str;
} else {
term.pre = str;
}
});
return this;
};
/** add this punctuation or whitespace after each match: */
exports.post = function (str, concat) {
// return array of post strings
if (str === undefined) {
return this.list.map(function (p) {
var terms = p.terms();
var term = terms[terms.length - 1];
return term.post;
});
} // set post string on all ends
this.list.forEach(function (p) {
var terms = p.terms();
var term = terms[terms.length - 1];
if (concat === true) {
term.post += str;
} else {
term.post = str;
}
});
return this;
};
/** remove start and end whitespace */
exports.trim = function () {
this.list = this.list.map(function (p) {
return p.trim();
});
return this;
};
/** connect words with hyphen, and remove whitespace */
exports.hyphenate = function () {
this.list.forEach(function (p) {
var terms = p.terms(); //remove whitespace
terms.forEach(function (t, i) {
if (i !== 0) {
t.pre = '';
}
if (terms[i + 1]) {
t.post = '-';
}
});
});
return this;
};
/** remove hyphens between words, and set whitespace */
exports.dehyphenate = function () {
var hasHyphen = /(-||—)/;
this.list.forEach(function (p) {
var terms = p.terms(); //remove whitespace
terms.forEach(function (t) {
if (hasHyphen.test(t.post)) {
t.post = ' ';
}
});
});
return this;
};
exports.deHyphenate = exports.dehyphenate;
/** add quotations around these matches */
exports.toQuotations = function (start, end) {
start = start || "\"";
end = end || "\"";
this.list.forEach(function (p) {
var terms = p.terms();
terms[0].pre = start + terms[0].pre;
var last = terms[terms.length - 1];
last.post = end + last.post;
});
return this;
};
exports.toQuotation = exports.toQuotations;
/** add brackets around these matches */
exports.toParentheses = function (start, end) {
start = start || "(";
end = end || ")";
this.list.forEach(function (p) {
var terms = p.terms();
terms[0].pre = start + terms[0].pre;
var last = terms[terms.length - 1];
last.post = end + last.post;
});
return this;
};
});
/** make all phrases into one phrase */
var join = function join(str) {
// clear the cache
this.uncache(); // make one large phrase - 'main'
var main = this.list[0];
var before = main.length;
var removed = {};
for (var i = 1; i < this.list.length; i++) {
var p = this.list[i];
removed[p.start] = true;
var term = main.lastTerm(); // add whitespace between them
if (str) {
term.post += str;
} // main -> p
term.next = p.start; // main <- p
p.terms(0).prev = term.id;
main.length += p.length;
main.cache = {};
} // parents are bigger than than their children.
// when we increase a child, we increase their parent too.
var increase = main.length - before;
this.parents().forEach(function (doc) {
// increase length on each effected phrase
doc.list.forEach(function (p) {
var terms = p.terms();
for (var _i = 0; _i < terms.length; _i++) {
if (terms[_i].id === main.start) {
p.length += increase;
break;
}
}
p.cache = {};
}); // remove redundant phrases now
doc.list = doc.list.filter(function (p) {
return removed[p.start] !== true;
});
}); // return one major phrase
return this.buildFrom([main]);
};
var _06Join = {
join: join
};
var postPunct = /[,\)"';:\-–—\.…]/; // const irregulars = {
// 'will not': `won't`,
// 'i am': `i'm`,
// }
var setContraction = function setContraction(m, suffix) {
if (!m.found) {
return;
}
var terms = m.termList(); //avoid any problematic punctuation
for (var i = 0; i < terms.length - 1; i++) {
var t = terms[i];
if (postPunct.test(t.post)) {
return;
}
} // set them as implict
terms.forEach(function (t) {
t.implicit = t.clean;
}); // perform the contraction
terms[0].text += suffix; // clean-up the others
terms.slice(1).forEach(function (t) {
t.text = '';
});
for (var _i = 0; _i < terms.length - 1; _i++) {
var _t = terms[_i];
_t.post = _t.post.replace(/ /, '');
}
};
/** turn 'i am' into i'm */
var contract = function contract() {
var doc = this.not('@hasContraction'); // we are -> we're
var m = doc.match('(we|they|you) are');
setContraction(m, "'re"); // they will -> they'll
m = doc.match('(he|she|they|it|we|you) will');
setContraction(m, "'ll"); // she is -> she's
m = doc.match('(he|she|they|it|we) is');
setContraction(m, "'s"); // spencer is -> spencer's
m = doc.match('#Person is');
setContraction(m, "'s"); // spencer would -> spencer'd
m = doc.match('#Person would');
setContraction(m, "'d"); // would not -> wouldn't
m = doc.match('(is|was|had|would|should|could|do|does|have|has|can) not');
setContraction(m, "n't"); // i have -> i've
m = doc.match('(i|we|they) have');
setContraction(m, "'ve"); // would have -> would've
m = doc.match('(would|should|could) have');
setContraction(m, "'ve"); // i am -> i'm
m = doc.match('i am');
setContraction(m, "'m"); // going to -> gonna
m = doc.match('going to');
return this;
};
var _07Contract = {
contract: contract
};
var methods$4 = Object.assign({}, _01Utils$1, _02Accessors, _03Match, _04Tag, _05Loops, _06Lookup, _07Cache, _01Replace, _02Insert, _01Text, _02Json, _03Out, _01Sort, _02Normalize, _03Split, _04Case, _05Whitespace, _06Join, _07Contract);
var methods$5 = {}; // allow helper methods like .adjectives() and .adverbs()
var arr = [['terms', '.'], ['hyphenated', '@hasHyphen .'], ['adjectives', '#Adjective'], ['hashTags', '#HashTag'], ['emails', '#Email'], ['emoji', '#Emoji'], ['emoticons', '#Emoticon'], ['atMentions', '#AtMention'], ['urls', '#Url'], ['adverbs', '#Adverb'], ['pronouns', '#Pronoun'], ['conjunctions', '#Conjunction'], ['prepositions', '#Preposition']];
arr.forEach(function (a) {
methods$5[a[0]] = function (n) {
var m = this.match(a[1]);
if (typeof n === 'number') {
m = m.get(n);
}
return m;
};
}); // aliases
methods$5.emojis = methods$5.emoji;
methods$5.atmentions = methods$5.atMentions;
methods$5.words = methods$5.terms;
/** return anything tagged as a phone number */
methods$5.phoneNumbers = function (n) {
var m = this.splitAfter('@hasComma');
m = m.match('#PhoneNumber+');
if (typeof n === 'number') {
m = m.get(n);
}
return m;
};
/** Deprecated: please use compromise-numbers plugin */
methods$5.money = function (n) {
var m = this.match('#Money #Currency?');
if (typeof n === 'number') {
m = m.get(n);
}
return m;
};
/** return all cities, countries, addresses, and regions */
methods$5.places = function (n) {
// don't split 'paris, france'
var keep = this.match('(#City && @hasComma) (#Region|#Country)'); // but split the other commas
var m = this.not(keep).splitAfter('@hasComma'); // combine them back together
m = m.concat(keep);
m.sort('index');
m = m.match('#Place+');
if (typeof n === 'number') {
m = m.get(n);
}
return m;
};
/** return all schools, businesses and institutions */
methods$5.organizations = function (n) {
var m = this.clauses();
m = m.match('#Organization+');
if (typeof n === 'number') {
m = m.get(n);
}
return m;
}; //combine them with .topics() method
methods$5.entities = function (n) {
var r = this.clauses(); // Find people, places, and organizations
var yup = r.people();
yup = yup.concat(r.places());
yup = yup.concat(r.organizations());
var ignore = ['someone', 'man', 'woman', 'mother', 'brother', 'sister', 'father'];
yup = yup.not(ignore); //return them to normal ordering
yup.sort('sequence'); // yup.unique() //? not sure
if (typeof n === 'number') {
yup = yup.get(n);
}
return yup;
}; //aliases
methods$5.things = methods$5.entities;
methods$5.topics = methods$5.entities;
var _simple = methods$5;
var underOver = /^(under|over)-?/;
/** match a word-sequence, like 'super bowl' in the lexicon */
var tryMultiple = function tryMultiple(terms, t, world) {
var lex = world.words; //try a two-word version
var txt = terms[t].reduced + ' ' + terms[t + 1].reduced;
if (lex[txt] !== undefined && lex.hasOwnProperty(txt) === true) {
terms[t].tag(lex[txt], 'lexicon-two', world);
terms[t + 1].tag(lex[txt], 'lexicon-two', world);
return 1;
} //try a three-word version?
if (t + 2 < terms.length) {
txt += ' ' + terms[t + 2].reduced;
if (lex[txt] !== undefined && lex.hasOwnProperty(txt) === true) {
terms[t].tag(lex[txt], 'lexicon-three', world);
terms[t + 1].tag(lex[txt], 'lexicon-three', world);
terms[t + 2].tag(lex[txt], 'lexicon-three', world);
return 2;
}
} //try a four-word version?
if (t + 3 < terms.length) {
txt += ' ' + terms[t + 3].reduced;
if (lex[txt] !== undefined && lex.hasOwnProperty(txt) === true) {
terms[t].tag(lex[txt], 'lexicon-four', world);
terms[t + 1].tag(lex[txt], 'lexicon-four', world);
terms[t + 2].tag(lex[txt], 'lexicon-four', world);
terms[t + 3].tag(lex[txt], 'lexicon-four', world);
return 3;
}
}
return 0;
};
/** look at each word in our list of known-words */
var checkLexicon = function checkLexicon(terms, world) {
var lex = world.words;
var hasCompound = world.hasCompound; // use reduced?
//go through each term, and check the lexicon
for (var t = 0; t < terms.length; t += 1) {
var str = terms[t].clean; //is it the start of a compound word, like 'super bowl'?
if (hasCompound[str] === true && t + 1 < terms.length) {
var foundWords = tryMultiple(terms, t, world);
if (foundWords > 0) {
t += foundWords; //skip any already-found words
continue;
}
} //try one-word lexicon
if (lex[str] !== undefined && lex.hasOwnProperty(str) === true) {
terms[t].tag(lex[str], 'lexicon', world);
continue;
} // look at reduced version of term, too
if (str !== terms[t].reduced && lex.hasOwnProperty(terms[t].reduced) === true) {
terms[t].tag(lex[terms[t].reduced], 'lexicon', world);
continue;
} // prefix strip: try to match 'take' for 'undertake'
if (underOver.test(str) === true) {
var noPrefix = str.replace(underOver, '');
if (lex.hasOwnProperty(noPrefix) === true) {
terms[t].tag(lex[noPrefix], 'noprefix-lexicon', world);
}
}
}
return terms;
};
var _01Lexicon = checkLexicon;
var apostrophes = /[\'`´]$/;
var perSec = /^(m|k|cm|km|m)\/(s|h|hr)$/; // '5 k/m'
//
var checkPunctuation = function checkPunctuation(terms, i, world) {
var term = terms[i]; //check hyphenation
// if (term.post.indexOf('-') !== -1 && terms[i + 1] && terms[i + 1].pre === '') {
// term.tag('Hyphenated', 'has-hyphen', world)
// }
// support 'head-over'
// if (term.hasHyphen() === true) {
// console.log(term.tags)
// }
// console.log(term.hasHyphen(), term.text)
//an end-tick (trailing apostrophe) - flanders', or Carlos'
if (apostrophes.test(term.text)) {
if (!apostrophes.test(term.pre) && !apostrophes.test(term.post) && term.clean.length > 2) {
var endChar = term.clean[term.clean.length - 2]; //flanders'
if (endChar === 's') {
term.tag(['Possessive', 'Noun'], 'end-tick', world);
return;
} //chillin'
if (endChar === 'n') {
term.tag(['Gerund'], 'chillin', world);
}
}
} // '5 km/s'
if (perSec.test(term.text)) {
term.tag('Unit', 'per-sec', world);
} // 'NASA' is, but not 'i REALLY love it.'
// if (term.tags.Noun === true && isAcronym(term, world)) {
// term.tag('Acronym', 'acronym-step', world)
// term.tag('Noun', 'acronym-infer', world)
// } else if (!oneLetterWord.hasOwnProperty(term.text) && oneLetterAcronym.test(term.text)) {
// term.tag('Acronym', 'one-letter-acronym', world)
// term.tag('Noun', 'one-letter-infer', world)
// }
};
var _02Punctuation$1 = checkPunctuation;
//these are regexes applied to t.text, instead of t.clean
// order matters.
var startsWith = [//web tags
[/^[\w\.]+@[\w\.]+\.[a-z]{2,3}$/, 'Email'], //not fancy
[/^#[a-z0-9_\u00C0-\u00FF]{2,}$/, 'HashTag'], [/^@\w{2,}$/, 'AtMention'], [/^(https?:\/\/|www\.)\w+\.[a-z]{2,3}/, 'Url'], //with http/www
[/^[\w./]+\.(com|net|gov|org|ly|edu|info|biz|ru|jp|de|in|uk|br)/, 'Url'], //http://mostpopularwebsites.net/top-level-domain
//dates/times
[/^[012]?[0-9](:[0-5][0-9])(:[0-5][0-9])$/, 'Time'], //4:32:32
[/^[012]?[0-9](:[0-5][0-9])?(:[0-5][0-9])? ?(am|pm)$/, 'Time'], //4pm
[/^[012]?[0-9](:[0-5][0-9])(:[0-5][0-9])? ?(am|pm)?$/, 'Time'], //4:00pm
[/^[PMCE]ST$/, 'Time'], //PST, time zone abbrevs
[/^utc ?[+-]?[0-9]+?$/, 'Time'], //UTC 8+
[/^[a-z0-9]*? o\'?clock$/, 'Time'], //3 oclock
[/^[0-9]{1,4}-[0-9]{1,2}-[0-9]{1,4}$/, 'Date'], // 03-02-89
[/^[0-9]{1,4}\/[0-9]{1,2}\/[0-9]{1,4}$/, 'Date'], // 03/02/89
[/^[0-9]{1,4}-[a-z]{2,9}-[0-9]{1,4}$/i, 'Date'], // 03-March-89
//names
[/^ma?c\'.*/, 'LastName'], //mc'adams
[/^o\'[drlkn].*/, 'LastName'], //o'douggan
[/^ma?cd[aeiou]/, 'LastName'], //macdonell - Last patterns https://en.wikipedia.org/wiki/List_of_family_name_affixes
//slang things
[/^(lol)+[sz]$/, 'Expression'], //lol
[/^woo+a*?h?$/, 'Expression'], //whoaa, wooo
[/^(un|de|re)\\-[a-z\u00C0-\u00FF]{2}/, 'Verb'], // [/^(over|under)[a-z]{2,}/, 'Adjective'],
[/^[0-9]{1,4}\.[0-9]{1,2}\.[0-9]{1,4}$/, 'Date'], // 03-02-89
//phone numbers
[/^[0-9]{3}-[0-9]{4}$/, 'PhoneNumber'], //589-3809
[/^(\+?[0-9][ -])?[0-9]{3}[ -]?[0-9]{3}-[0-9]{4}$/, 'PhoneNumber'], //632-589-3809
//money
// currency regex
// /[\$\xA2-\xA5\u058F\u060B\u09F2\u09F3\u09FB\u0AF1\u0BF9\u0E3F\u17DB\u20A0-\u20BD\uA838\uFDFC\uFE69\uFF04\uFFE0\uFFE1\uFFE5\uFFE6]
//like $5.30
[/^[-+]?[\$\xA2-\xA5\u058F\u060B\u09F2\u09F3\u09FB\u0AF1\u0BF9\u0E3F\u17DB\u20A0-\u20BD\uA838\uFDFC\uFE69\uFF04\uFFE0\uFFE1\uFFE5\uFFE6][-+]?[0-9]+(,[0-9]{3})*(\.[0-9]+)?(k|m|b|bn)?\+?$/, ['Money', 'Value']], //like 5.30$
[/^[-+]?[0-9]+(,[0-9]{3})*(\.[0-9]+)?[\$\xA2-\xA5\u058F\u060B\u09F2\u09F3\u09FB\u0AF1\u0BF9\u0E3F\u17DB\u20A0-\u20BD\uA838\uFDFC\uFE69\uFF04\uFFE0\uFFE1\uFFE5\uFFE6]\+?$/, ['Money', 'Value']], //like 400usd
[/^[-+]?[0-9]([0-9,.])+?(usd|eur|jpy|gbp|cad|aud|chf|cny|hkd|nzd|kr|rub)$/i, ['Money', 'Value']], //numbers
// 50 | -50 | 3.23 | 5,999.0 | 10+
[/^[-+]?[0-9]+(,[0-9]{3})*(\.[0-9]+)?\+?$/, ['Cardinal', 'NumericValue']], [/^[-+]?[0-9]+(,[0-9]{3})*(\.[0-9]+)?(st|nd|rd|th)$/, ['Ordinal', 'NumericValue']], // .73th
[/^\.[0-9]+\+?$/, ['Cardinal', 'NumericValue']], //percent
[/^[-+]?[0-9]+(,[0-9]{3})*(\.[0-9]+)?%\+?$/, ['Percent', 'Cardinal', 'NumericValue']], //7% ..
[/^\.[0-9]+%$/, ['Percent', 'Cardinal', 'NumericValue']], //.7% ..
//fraction
[/^[0-9]{1,4}\/[0-9]{1,4}$/, 'Fraction'], //3/2ths
//range
[/^[0-9.]{1,2}[-][0-9]{1,2}$/, ['Value', 'NumberRange']], //7-8
[/^[0-9.]{1,4}(st|nd|rd|th)?[-][0-9\.]{1,4}(st|nd|rd|th)?$/, 'NumberRange'], //5-7
//with unit
[/^[0-9.]+([a-z]{1,4})$/, 'Value'] //like 5tbsp
//ordinal
// [/^[0-9][0-9,.]*(st|nd|rd|r?th)$/, ['NumericValue', 'Ordinal']], //like 5th
// [/^[0-9]+(st|nd|rd|th)$/, 'Ordinal'], //like 5th
];
var romanNumeral = /^[IVXLCDM]{2,}$/;
var romanNumValid = /^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/; // https://stackoverflow.com/a/267405/168877
//try each of the ^regexes in our list
var checkRegex = function checkRegex(term, world) {
var str = term.text; // do them all!
for (var r = 0; r < startsWith.length; r += 1) {
if (startsWith[r][0].test(str) === true) {
term.tagSafe(startsWith[r][1], 'prefix #' + r, world);
break;
}
} // do some more!
//roman numberals - XVII
if (term.text.length >= 2 && romanNumeral.test(str) && romanNumValid.test(str)) {
term.tag('RomanNumeral', 'xvii', world);
}
};
var _03Prefixes = checkRegex;
//regex suffix patterns and their most common parts of speech,
//built using wordnet, by spencer kelly.
//this mapping shrinks-down the uglified build
var Adj = 'Adjective';
var Inf = 'Infinitive';
var Pres = 'PresentTense';
var Sing = 'Singular';
var Past = 'PastTense';
var Adverb = 'Adverb';
var Exp = 'Expression';
var Actor = 'Actor';
var Verb = 'Verb';
var Noun = 'Noun';
var Last = 'LastName'; //the order here matters.
//regexes indexed by mandated last-character
var endsWith$1 = {
a: [[/.[aeiou]na$/, Noun], [/.[oau][wvl]ska$/, Last], //polish (female)
[/.[^aeiou]ica$/, Sing], [/^([hyj]a)+$/, Exp] //hahah
],
c: [[/.[^aeiou]ic$/, Adj]],
d: [//==-ed==
//double-consonant
[/[aeiou](pp|ll|ss|ff|gg|tt|rr|bb|nn|mm)ed$/, Past], //popped, planned
//double-vowel
[/.[aeo]{2}[bdgmnprvz]ed$/, Past], //beeped, mooned, veered
//-hed
[/.[aeiou][sg]hed$/, Past], //stashed, sighed
//-rd
[/.[aeiou]red$/, Past], //stored
[/.[aeiou]r?ried$/, Past], //buried
//-led
[/.[bcdgtr]led$/, Past], //startled, rumbled
[/.[aoui]f?led$/, Past], //impaled, stifled
//-sed
[/.[iao]sed$/, Past], //franchised
[/[aeiou]n?[cs]ed$/, Past], //laced, lanced
//-med
[/[aeiou][rl]?[mnf]ed$/, Past], //warmed, attained, engulfed
//-ked
[/[aeiou][ns]?c?ked$/, Past], //hooked, masked
//-ged
[/[aeiou][nl]?ged$/, Past], //engaged
//-ted
[/.[tdbwxz]ed$/, Past], //bribed, boxed
[/[^aeiou][aeiou][tvx]ed$/, Past], //boxed
//-ied
[/.[cdlmnprstv]ied$/, Past], //rallied
[/[^aeiou]ard$/, Sing], //card
[/[aeiou][^aeiou]id$/, Adj], [/.[vrl]id$/, Adj]],
e: [[/.[lnr]ize$/, Inf], [/.[^aeiou]ise$/, Inf], [/.[aeiou]te$/, Inf], [/.[^aeiou][ai]ble$/, Adj], [/.[^aeiou]eable$/, Adj], [/.[ts]ive$/, Adj]],
h: [[/.[^aeiouf]ish$/, Adj], [/.v[iy]ch$/, Last], //east-europe
[/^ug?h+$/, Exp], //uhh
[/^uh[ -]?oh$/, Exp] //uhoh
],
i: [[/.[oau][wvl]ski$/, Last] //polish (male)
],
k: [[/^(k){2}$/, Exp] //kkkk
],
l: [[/.[gl]ial$/, Adj], [/.[^aeiou]ful$/, Adj], [/.[nrtumcd]al$/, Adj], [/.[^aeiou][ei]al$/, Adj]],
m: [[/.[^aeiou]ium$/, Sing], [/[^aeiou]ism$/, Sing], [/^h*u*m+$/, Exp], //mmmmmmm / ummmm / huuuuuummmmmm
[/^\d+ ?[ap]m$/, 'Date']],
n: [[/.[lsrnpb]ian$/, Adj], [/[^aeiou]ician$/, Actor], [/[aeiou][ktrp]in$/, 'Gerund'] // 'cookin', 'hootin'
],
o: [[/^no+$/, Exp], //noooo
[/^(yo)+$/, Exp], //yoyo
[/^woo+[pt]?$/, Exp] //woo
],
r: [[/.[bdfklmst]ler$/, 'Noun'], [/.[ilk]er$/, 'Comparative'], [/[aeiou][pns]er$/, Sing], [/[^i]fer$/, Inf], [/.[^aeiou][ao]pher$/, Actor]],
t: [[/.[di]est$/, 'Superlative'], [/.[icldtgrv]ent$/, Adj], [/[aeiou].*ist$/, Adj], [/^[a-z]et$/, Verb]],
s: [[/.[rln]ates$/, Pres], [/.[^z]ens$/, Verb], [/.[lstrn]us$/, Sing], [/.[aeiou]sks$/, Pres], //masks
[/.[aeiou]kes$/, Pres], //bakes
[/[aeiou][^aeiou]is$/, Sing], [/[a-z]\'s$/, Noun], [/^yes+$/, Exp] //yessss
],
v: [[/.[^aeiou][ai][kln]ov$/, Last] //east-europe
],
y: [[/.[cts]hy$/, Adj], [/.[st]ty$/, Adj], [/.[gk]y$/, Adj], [/.[tnl]ary$/, Adj], [/.[oe]ry$/, Sing], [/[rdntkbhs]ly$/, Adverb], [/...lly$/, Adverb], [/[bszmp]{2}y$/, Adj], [/.(gg|bb|zz)ly$/, Adj], [/.[aeiou]my$/, Adj], [/[ea]{2}zy$/, Adj], [/.[^aeiou]ity$/, Sing]]
};
//just a foolish lookup of known suffixes
var Adj$1 = 'Adjective';
var Inf$1 = 'Infinitive';
var Pres$1 = 'PresentTense';
var Sing$1 = 'Singular';
var Past$1 = 'PastTense';
var Avb = 'Adverb';
var Plrl = 'Plural';
var Actor$1 = 'Actor';
var Vb = 'Verb';
var Noun$1 = 'Noun';
var Last$1 = 'LastName';
var Modal = 'Modal';
var Place = 'Place'; // find any issues - https://observablehq.com/@spencermountain/suffix-word-lookup
var suffixMap = [null, //0
null, //1
{
//2-letter
ea: Sing$1,
ia: Noun$1,
ic: Adj$1,
ly: Avb,
"'n": Vb,
"'t": Vb
}, {
//3-letter
oed: Past$1,
ued: Past$1,
xed: Past$1,
' so': Avb,
"'ll": Modal,
"'re": 'Copula',
azy: Adj$1,
end: Vb,
ped: Past$1,
ffy: Adj$1,
ify: Inf$1,
ing: 'Gerund',
//likely to be converted to Adj after lexicon pass
ize: Inf$1,
lar: Adj$1,
mum: Adj$1,
nes: Pres$1,
nny: Adj$1,
oid: Adj$1,
ous: Adj$1,
que: Adj$1,
rmy: Adj$1,
rol: Sing$1,
sis: Sing$1,
zes: Pres$1
}, {
//4-letter
amed: Past$1,
aped: Past$1,
ched: Past$1,
lked: Past$1,
nded: Past$1,
cted: Past$1,
dged: Past$1,
akis: Last$1,
//greek
cede: Inf$1,
chuk: Last$1,
//east-europe
czyk: Last$1,
//polish (male)
ects: Pres$1,
ends: Vb,
enko: Last$1,
//east-europe
ette: Sing$1,
fies: Pres$1,
fore: Avb,
gate: Inf$1,
gone: Adj$1,
ices: Plrl,
ints: Plrl,
ines: Plrl,
ions: Plrl,
less: Avb,
llen: Adj$1,
made: Adj$1,
nsen: Last$1,
//norway
oses: Pres$1,
ould: Modal,
some: Adj$1,
sson: Last$1,
//swedish male
tage: Inf$1,
teen: 'Value',
tion: Sing$1,
tive: Adj$1,
tors: Noun$1,
vice: Sing$1
}, {
//5-letter
tized: Past$1,
urned: Past$1,
eased: Past$1,
ances: Plrl,
bound: Adj$1,
ettes: Plrl,
fully: Avb,
ishes: Pres$1,
ities: Plrl,
marek: Last$1,
//polish (male)
nssen: Last$1,
//norway
ology: Noun$1,
ports: Plrl,
rough: Adj$1,
tches: Pres$1,
tieth: 'Ordinal',
tures: Plrl,
wards: Avb,
where: Avb
}, {
//6-letter
auskas: Last$1,
//lithuania
keeper: Actor$1,
logist: Actor$1,
teenth: 'Value'
}, {
//7-letter
opoulos: Last$1,
//greek
borough: Place,
//Hillsborough
sdottir: Last$1 //swedish female
}];
var endRegexs = function endRegexs(term, world) {
var str = term.clean;
var _char = str[str.length - 1];
if (endsWith$1.hasOwnProperty(_char) === true) {
var regs = endsWith$1[_char];
for (var r = 0; r < regs.length; r += 1) {
if (regs[r][0].test(str) === true) {
term.tagSafe(regs[r][1], "endReg ".concat(_char, " #").concat(r), world);
break;
}
}
}
}; //sweep-through all suffixes
var knownSuffixes = function knownSuffixes(term, world) {
var len = term.clean.length;
var max = 7;
if (len <= max) {
max = len - 1;
}
for (var i = max; i > 1; i -= 1) {
var str = term.clean.substr(len - i, len);
if (suffixMap[str.length].hasOwnProperty(str) === true) {
var tag = suffixMap[str.length][str];
term.tagSafe(tag, 'suffix -' + str, world);
break;
}
}
}; //all-the-way-down!
var checkRegex$1 = function checkRegex(term, world) {
knownSuffixes(term, world);
endRegexs(term, world);
};
var _04Suffixes = checkRegex$1;
//just some of the most common emoticons
//faster than
//http://stackoverflow.com/questions/28077049/regex-matching-emoticons
var emoticons = {
':(': true,
':)': true,
':P': true,
':p': true,
':O': true,
':3': true,
':|': true,
':/': true,
':\\': true,
':$': true,
':*': true,
':@': true,
':-(': true,
':-)': true,
':-P': true,
':-p': true,
':-O': true,
':-3': true,
':-|': true,
':-/': true,
':-\\': true,
':-$': true,
':-*': true,
':-@': true,
':^(': true,
':^)': true,
':^P': true,
':^p': true,
':^O': true,
':^3': true,
':^|': true,
':^/': true,
':^\\': true,
':^$': true,
':^*': true,
':^@': true,
'):': true,
'(:': true,
'$:': true,
'*:': true,
')-:': true,
'(-:': true,
'$-:': true,
'*-:': true,
')^:': true,
'(^:': true,
'$^:': true,
'*^:': true,
'<3': true,
'</3': true,
'<\\3': true
};
var emojiReg = /^(\u00a9|\u00ae|[\u2319-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/; //for us, there's three types -
// * ;) - emoticons
// * 🌵 - unicode emoji
// * :smiling_face: - asci-represented emoji
//test for forms like ':woman_tone2::ear_of_rice:'
//https://github.com/Kikobeats/emojis-keywords/blob/master/index.js
var isCommaEmoji = function isCommaEmoji(raw) {
if (raw.charAt(0) === ':') {
//end comma can be last or second-last ':haircut_tone3:‍♀️'
if (raw.match(/:.?$/) === null) {
return false;
} //ensure no spaces
if (raw.match(' ')) {
return false;
} //reasonably sized
if (raw.length > 35) {
return false;
}
return true;
}
return false;
}; //check against emoticon whitelist
var isEmoticon = function isEmoticon(str) {
str = str.replace(/^[:;]/, ':'); //normalize the 'eyes'
return emoticons.hasOwnProperty(str);
};
var tagEmoji = function tagEmoji(term, world) {
var raw = term.pre + term.text + term.post;
raw = raw.trim(); //dont double-up on ending periods
raw = raw.replace(/[.!?,]$/, ''); //test for :keyword: emojis
if (isCommaEmoji(raw) === true) {
term.tag('Emoji', 'comma-emoji', world);
term.text = raw;
term.pre = term.pre.replace(':', '');
term.post = term.post.replace(':', '');
} //test for unicode emojis
if (term.text.match(emojiReg)) {
term.tag('Emoji', 'unicode-emoji', world);
term.text = raw;
} //test for emoticon ':)' emojis
if (isEmoticon(raw) === true) {
term.tag('Emoticon', 'emoticon-emoji', world);
term.text = raw;
}
};
var _05Emoji = tagEmoji;
var steps = {
lexicon: _01Lexicon,
punctuation: _02Punctuation$1,
regex: _03Prefixes,
suffix: _04Suffixes,
emoji: _05Emoji
}; //'lookups' look at a term by itself
var lookups = function lookups(doc, terms) {
var world = doc.world; //our list of known-words
steps.lexicon(terms, world); //try these other methods
for (var i = 0; i < terms.length; i += 1) {
var term = terms[i]; //or maybe some helpful punctuation
steps.punctuation(terms, i, world); //mostly prefix checks
steps.regex(term, world); //maybe we can guess
steps.suffix(term, world); //emoji and emoticons
steps.emoji(term, world);
}
return doc;
};
var _01Init = lookups;
//markov-like stats about co-occurance, for hints about unknown terms
//basically, a little-bit better than the noun-fallback
//just top n-grams from nlp tags, generated from nlp-corpus
//after this word, here's what happens usually
var afterThisWord = {
i: 'Verb',
//44% //i walk..
first: 'Noun',
//50% //first principles..
it: 'Verb',
//33%
there: 'Verb',
//35%
not: 'Verb',
//33%
because: 'Noun',
//31%
"if": 'Noun',
//32%
but: 'Noun',
//26%
who: 'Verb',
//40%
"this": 'Noun',
//37%
his: 'Noun',
//48%
when: 'Noun',
//33%
you: 'Verb',
//35%
very: 'Adjective',
// 39%
old: 'Noun',
//51%
never: 'Verb',
//42%
before: 'Noun' //28%
}; //in advance of this word, this is what happens usually
var beforeThisWord = {
there: 'Verb',
//23% // be there
me: 'Verb',
//31% //see me
man: 'Adjective',
// 80% //quiet man
only: 'Verb',
//27% //sees only
him: 'Verb',
//32% //show him
were: 'Noun',
//48% //we were
took: 'Noun',
//38% //he took
himself: 'Verb',
//31% //see himself
went: 'Noun',
//43% //he went
who: 'Noun',
//47% //person who
jr: 'Person'
}; //following this POS, this is likely
var afterThisPOS = {
Adjective: 'Noun',
//36% //blue dress
Possessive: 'Noun',
//41% //his song
Determiner: 'Noun',
//47%
Adverb: 'Verb',
//20%
Pronoun: 'Verb',
//40%
Value: 'Noun',
//47%
Ordinal: 'Noun',
//53%
Modal: 'Verb',
//35%
Superlative: 'Noun',
//43%
Demonym: 'Noun',
//38%
Honorific: 'Person' //
}; //in advance of this POS, this is likely
var beforeThisPOS = {
Copula: 'Noun',
//44% //spencer is
PastTense: 'Noun',
//33% //spencer walked
Conjunction: 'Noun',
//36%
Modal: 'Noun',
//38%
Pluperfect: 'Noun',
//40%
PerfectTense: 'Verb' //32%
};
var markov = {
beforeThisWord: beforeThisWord,
afterThisWord: afterThisWord,
beforeThisPos: beforeThisPOS,
afterThisPos: afterThisPOS
};
var afterKeys = Object.keys(markov.afterThisPos);
var beforeKeys = Object.keys(markov.beforeThisPos);
var checkNeighbours = function checkNeighbours(terms, world) {
var _loop = function _loop(i) {
var term = terms[i]; //do we still need a tag?
if (term.isKnown() === true) {
return "continue";
} //ok, this term needs a tag.
//look at previous word for clues..
var lastTerm = terms[i - 1];
if (lastTerm) {
// 'foobar term'
if (markov.afterThisWord.hasOwnProperty(lastTerm.clean) === true) {
var tag = markov.afterThisWord[lastTerm.clean];
term.tag(tag, 'after-' + lastTerm.clean, world);
return "continue";
} // 'Tag term'
// (look at previous POS tags for clues..)
var foundTag = afterKeys.find(function (tag) {
return lastTerm.tags[tag];
});
if (foundTag !== undefined) {
var _tag = markov.afterThisPos[foundTag];
term.tag(_tag, 'after-' + foundTag, world);
return "continue";
}
} //look at next word for clues..
var nextTerm = terms[i + 1];
if (nextTerm) {
// 'term foobar'
if (markov.beforeThisWord.hasOwnProperty(nextTerm.clean) === true) {
var _tag2 = markov.beforeThisWord[nextTerm.clean];
term.tag(_tag2, 'before-' + nextTerm.clean, world);
return "continue";
} // 'term Tag'
// (look at next POS tags for clues..)
var _foundTag = beforeKeys.find(function (tag) {
return nextTerm.tags[tag];
});
if (_foundTag !== undefined) {
var _tag3 = markov.beforeThisPos[_foundTag];
term.tag(_tag3, 'before-' + _foundTag, world);
return "continue";
}
}
};
for (var i = 0; i < terms.length; i += 1) {
var _ret = _loop(i);
if (_ret === "continue") continue;
}
};
var _01Neighbours = checkNeighbours;
var titleCase$4 = /^[A-Z][a-z'\u00C0-\u00FF]/;
var hasNumber = /[0-9]/;
/** look for any grammar signals based on capital/lowercase */
var checkCase = function checkCase(doc) {
var world = doc.world;
doc.list.forEach(function (p) {
var terms = p.terms();
for (var i = 1; i < terms.length; i++) {
var term = terms[i];
if (titleCase$4.test(term.text) === true && hasNumber.test(term.text) === false) {
term.tag('ProperNoun', 'titlecase-noun', world);
}
}
});
};
var _02Case = checkCase;
var hasPrefix = /^(re|un)-?[a-z\u00C0-\u00FF]/;
var prefix = /^(re|un)-?/;
/** check 'rewatch' in lexicon as 'watch' */
var checkPrefix = function checkPrefix(terms, world) {
var lex = world.words;
terms.forEach(function (term) {
// skip if we have a good tag already
if (term.isKnown() === true) {
return;
} //does it start with 'un|re'
if (hasPrefix.test(term.clean) === true) {
// look for the root word in the lexicon:
var stem = term.clean.replace(prefix, '');
if (stem && stem.length > 3 && lex[stem] !== undefined && lex.hasOwnProperty(stem) === true) {
term.tag(lex[stem], 'stem-' + stem, world);
}
}
});
};
var _03Stem = checkPrefix;
//similar to plural/singularize rules, but not the same
var isPlural = [/(^v)ies$/i, /ises$/i, /ives$/i, /(antenn|formul|nebul|vertebr|vit)ae$/i, /(octop|vir|radi|nucle|fung|cact|stimul)i$/i, /(buffal|tomat|tornad)oes$/i, /(analy|ba|diagno|parenthe|progno|synop|the)ses$/i, /(vert|ind|cort)ices$/i, /(matr|append)ices$/i, /(x|ch|ss|sh|s|z|o)es$/i, /is$/i, /men$/i, /news$/i, /.tia$/i, /(^f)ves$/i, /(lr)ves$/i, /(^aeiouy|qu)ies$/i, /(m|l)ice$/i, /(cris|ax|test)es$/i, /(alias|status)es$/i, /ics$/i]; //similar to plural/singularize rules, but not the same
var isSingular = [/(ax|test)is$/i, /(octop|vir|radi|nucle|fung|cact|stimul)us$/i, /(octop|vir)i$/i, /(rl)f$/i, /(alias|status)$/i, /(bu)s$/i, /(al|ad|at|er|et|ed|ad)o$/i, /(ti)um$/i, /(ti)a$/i, /sis$/i, /(?:(^f)fe|(lr)f)$/i, /hive$/i, /s[aeiou]+ns$/i, // sans, siens
/(^aeiouy|qu)y$/i, /(x|ch|ss|sh|z)$/i, /(matr|vert|ind|cort)(ix|ex)$/i, /(m|l)ouse$/i, /(m|l)ice$/i, /(antenn|formul|nebul|vertebr|vit)a$/i, /.sis$/i, /^(?!talis|.*hu)(.*)man$/i];
var isPlural_1 = {
isSingular: isSingular,
isPlural: isPlural
};
var noPlurals = ['Uncountable', 'Pronoun', 'Place', 'Value', 'Person', 'Month', 'WeekDay', 'Holiday'];
var notPlural = [/ss$/, /sis$/, /[^aeiou][uo]s$/, /'s$/];
var notSingular = [/i$/, /ae$/];
/** turn nouns into singular/plural */
var checkPlural = function checkPlural(t, world) {
if (t.tags.Noun && !t.tags.Acronym) {
var str = t.clean; //skip existing tags, fast
if (t.tags.Singular || t.tags.Plural) {
return;
} //too short
if (str.length <= 3) {
t.tag('Singular', 'short-singular', world);
return;
} //is it impossible to be plural?
if (noPlurals.find(function (tag) {
return t.tags[tag];
})) {
return;
} // isPlural suffix rules
if (isPlural_1.isPlural.find(function (reg) {
return reg.test(str);
})) {
t.tag('Plural', 'plural-rules', world);
return;
} // isSingular suffix rules
if (isPlural_1.isSingular.find(function (reg) {
return reg.test(str);
})) {
t.tag('Singular', 'singular-rules', world);
return;
} // finally, fallback 'looks plural' rules..
if (/s$/.test(str) === true) {
//avoid anything too sketchy to be plural
if (notPlural.find(function (reg) {
return reg.test(str);
})) {
return;
}
t.tag('Plural', 'plural-fallback', world);
return;
} //avoid anything too sketchy to be singular
if (notSingular.find(function (reg) {
return reg.test(str);
})) {
return;
}
t.tag('Singular', 'singular-fallback', world);
}
};
var _04Plurals = checkPlural;
//nouns that also signal the title of an unknown organization
//todo remove/normalize plural forms
var orgWords = ['academy', 'administration', 'agence', 'agences', 'agencies', 'agency', 'airlines', 'airways', 'army', 'assoc', 'associates', 'association', 'assurance', 'authority', 'autorite', 'aviation', 'bank', 'banque', 'board', 'boys', 'brands', 'brewery', 'brotherhood', 'brothers', 'building society', 'bureau', 'cafe', 'caisse', 'capital', 'care', 'cathedral', 'center', 'central bank', 'centre', 'chemicals', 'choir', 'chronicle', 'church', 'circus', 'clinic', 'clinique', 'club', 'co', 'coalition', 'coffee', 'collective', 'college', 'commission', 'committee', 'communications', 'community', 'company', 'comprehensive', 'computers', 'confederation', 'conference', 'conseil', 'consulting', 'containers', 'corporation', 'corps', 'corp', 'council', 'crew', 'daily news', 'data', 'departement', 'department', 'department store', 'departments', 'design', 'development', 'directorate', 'division', 'drilling', 'education', 'eglise', 'electric', 'electricity', 'energy', 'ensemble', 'enterprise', 'enterprises', 'entertainment', 'estate', 'etat', 'evening news', 'faculty', 'federation', 'financial', 'fm', 'foundation', 'fund', 'gas', 'gazette', 'girls', 'government', 'group', 'guild', 'health authority', 'herald', 'holdings', 'hospital', 'hotel', 'hotels', 'inc', 'industries', 'institut', 'institute', 'institute of technology', 'institutes', 'insurance', 'international', 'interstate', 'investment', 'investments', 'investors', 'journal', 'laboratory', 'labs', // 'law',
'liberation army', 'limited', 'local authority', 'local health authority', 'machines', 'magazine', 'management', 'marine', 'marketing', 'markets', 'media', 'memorial', 'mercantile exchange', 'ministere', 'ministry', 'military', 'mobile', 'motor', 'motors', 'musee', 'museum', // 'network',
'news', 'news service', 'observatory', 'office', 'oil', 'optical', 'orchestra', 'organization', 'partners', 'partnership', // 'party',
"people's party", 'petrol', 'petroleum', 'pharmacare', 'pharmaceutical', 'pharmaceuticals', 'pizza', 'plc', 'police', 'polytechnic', 'post', 'power', 'press', 'productions', 'quartet', 'radio', 'regional authority', 'regional health authority', 'reserve', 'resources', 'restaurant', 'restaurants', 'savings', 'school', 'securities', 'service', 'services', 'social club', 'societe', 'society', 'sons', 'standard', 'state police', 'state university', 'stock exchange', 'subcommittee', 'syndicat', 'systems', 'telecommunications', 'telegraph', 'television', 'times', 'tribunal', 'tv', 'union', 'university', 'utilities', 'workers'];
var organizations = orgWords.reduce(function (h, str) {
h[str] = 'Noun';
return h;
}, {});
var maybeOrg = function maybeOrg(t) {
//must be a noun
if (!t.tags.Noun) {
return false;
} //can't be these things
if (t.tags.Pronoun || t.tags.Comma || t.tags.Possessive) {
return false;
} //must be one of these
if (t.tags.Organization || t.tags.Acronym || t.tags.Place || t.titleCase()) {
return true;
}
return false;
};
var tagOrgs = function tagOrgs(terms, world) {
for (var i = 0; i < terms.length; i += 1) {
var t = terms[i];
if (organizations[t.clean] !== undefined && organizations.hasOwnProperty(t.clean) === true) {
// look-backward - eg. 'Toronto University'
var lastTerm = terms[i - 1];
if (lastTerm !== undefined && maybeOrg(lastTerm) === true) {
lastTerm.tagSafe('Organization', 'org-word-1', world);
t.tagSafe('Organization', 'org-word-2', world);
continue;
} //look-forward - eg. University of Toronto
var nextTerm = terms[i + 1];
if (nextTerm !== undefined && nextTerm.clean === 'of') {
if (terms[i + 2] && maybeOrg(terms[i + 2])) {
t.tagSafe('Organization', 'org-of-word-1', world);
nextTerm.tagSafe('Organization', 'org-of-word-2', world);
terms[i + 2].tagSafe('Organization', 'org-of-word-3', world);
continue;
}
}
}
}
};
var _05Organizations = tagOrgs;
var oneLetterAcronym$1 = /^[A-Z]('s|,)?$/;
var periodSeperated = /([A-Z]\.){2}[A-Z]?/i;
var oneLetterWord = {
I: true,
A: true
};
var isAcronym$2 = function isAcronym(term, world) {
var str = term.reduced; // a known acronym like fbi
if (term.tags.Acronym) {
return true;
} // if (term.tags.Adverb || term.tags.Verb || term.tags.Value || term.tags.Plural) {
// return false
// }
// known-words, like 'PIZZA' is not an acronym.
if (world.words[str]) {
return false;
}
return term.isAcronym();
}; // F.B.I., NBC, - but not 'NO COLLUSION'
var checkAcronym = function checkAcronym(terms, world) {
terms.forEach(function (term) {
//these are not acronyms
if (term.tags.RomanNumeral === true) {
return;
} //period-ones F.D.B.
if (periodSeperated.test(term.text) === true) {
term.tag('Acronym', 'period-acronym', world);
} //non-period ones are harder
if (term.isUpperCase() && isAcronym$2(term, world)) {
term.tag('Acronym', 'acronym-step', world);
term.tag('Noun', 'acronym-infer', world);
} else if (!oneLetterWord.hasOwnProperty(term.text) && oneLetterAcronym$1.test(term.text)) {
term.tag('Acronym', 'one-letter-acronym', world);
term.tag('Noun', 'one-letter-infer', world);
} //if it's a organization,
if (term.tags.Organization && term.text.length <= 3) {
term.tag('Acronym', 'acronym-org', world);
}
if (term.tags.Organization && term.isUpperCase() && term.text.length <= 6) {
term.tag('Acronym', 'acronym-org-case', world);
}
});
};
var _06Acronyms = checkAcronym;
var step = {
neighbours: _01Neighbours,
"case": _02Case,
stem: _03Stem,
plural: _04Plurals,
organizations: _05Organizations,
acronyms: _06Acronyms
}; //
var fallbacks = function fallbacks(doc, terms) {
var world = doc.world; // if it's empty, consult it's neighbours, first
step.neighbours(terms, world); // is there a case-sensitive clue?
step["case"](doc); // check 'rewatch' as 'watch'
step.stem(terms, world); // ... fallback to a noun!
terms.forEach(function (t) {
if (t.isKnown() === false) {
t.tag('Noun', 'noun-fallback', doc.world);
}
}); // turn 'Foo University' into an Org
step.organizations(terms, world); //turn 'FBD' into an acronym
step.acronyms(terms, world); //are the nouns singular or plural?
terms.forEach(function (t) {
step.plural(t, doc.world);
});
return doc;
};
var _02Fallbacks = fallbacks;
var hasNegative = /n't$/;
var irregulars$3 = {
"won't": ['will', 'not'],
wont: ['will', 'not'],
"can't": ['can', 'not'],
cant: ['can', 'not'],
cannot: ['can', 'not'],
"shan't": ['should', 'not'],
dont: ['do', 'not'],
dun: ['do', 'not'] // "ain't" is ambiguous for is/was
}; // either 'is not' or 'are not'
var doAint = function doAint(term, phrase) {
var terms = phrase.terms();
var index = terms.indexOf(term);
var before = terms.slice(0, index); //look for the preceding noun
var noun = before.find(function (t) {
return t.tags.Noun;
});
if (noun && noun.tags.Plural) {
return ['are', 'not'];
}
return ['is', 'not'];
};
var checkNegative = function checkNegative(term, phrase) {
//check named-ones
if (irregulars$3.hasOwnProperty(term.clean) === true) {
return irregulars$3[term.clean];
} //this word needs it's own logic:
if (term.clean === "ain't" || term.clean === 'aint') {
return doAint(term, phrase);
} //try it normally
if (hasNegative.test(term.clean) === true) {
var main = term.clean.replace(hasNegative, '');
return [main, 'not'];
}
return null;
};
var _01Negative = checkNegative;
var contraction = /([a-z\u00C0-\u00FF]+)[\u0027\u0060\u00B4\u2018\u2019\u201A\u201B\u2032\u2035\u2039\u203A]([a-z]{1,2})$/i; //these ones don't seem to be ambiguous
var easy = {
ll: 'will',
ve: 'have',
re: 'are',
m: 'am',
"n't": 'not'
}; //
var checkApostrophe = function checkApostrophe(term) {
var parts = term.text.match(contraction);
if (parts === null) {
return null;
}
if (easy.hasOwnProperty(parts[2])) {
return [parts[1], easy[parts[2]]];
}
return null;
};
var _02Simple = checkApostrophe;
var irregulars$4 = {
wanna: ['want', 'to'],
gonna: ['going', 'to'],
im: ['i', 'am'],
alot: ['a', 'lot'],
ive: ['i', 'have'],
imma: ['I', 'will'],
"where'd": ['where', 'did'],
whered: ['where', 'did'],
"when'd": ['when', 'did'],
whend: ['when', 'did'],
// "how'd": ['how', 'did'], //'how would?'
// "what'd": ['what', 'did'], //'what would?'
howd: ['how', 'did'],
whatd: ['what', 'did'],
// "let's": ['let', 'us'], //too weird
//multiple word contractions
dunno: ['do', 'not', 'know'],
brb: ['be', 'right', 'back'],
gtg: ['got', 'to', 'go'],
irl: ['in', 'real', 'life'],
tbh: ['to', 'be', 'honest'],
imo: ['in', 'my', 'opinion'],
til: ['today', 'i', 'learned'],
rn: ['right', 'now'],
twas: ['it', 'was'],
'@': ['at']
}; //
var checkIrregulars = function checkIrregulars(term) {
//check white-list
if (irregulars$4.hasOwnProperty(term.clean)) {
return irregulars$4[term.clean];
}
return null;
};
var _03Irregulars = checkIrregulars;
var hasApostropheS = /([a-z\u00C0-\u00FF]+)[\u0027\u0060\u00B4\u2018\u2019\u201A\u201B\u2032\u2035\u2039\u203A]s$/i;
var banList = {
that: true,
there: true
};
var isPossessive = function isPossessive(term, pool) {
// if we already know it
if (term.tags.Possessive) {
return true;
} //a pronoun can't be possessive - "he's house"
if (term.tags.Pronoun || term.tags.QuestionWord) {
return false;
}
if (banList.hasOwnProperty(term.reduced)) {
return false;
} //if end of sentence, it is possessive - "was spencer's"
var nextTerm = pool.get(term.next);
if (!nextTerm) {
return true;
} //a gerund suggests 'is walking'
if (nextTerm.tags.Verb) {
//fix 'jamie's bite'
if (nextTerm.tags.Infinitive) {
return true;
} //fix 'spencer's runs'
if (nextTerm.tags.PresentTense) {
return true;
}
return false;
} //spencer's house
if (nextTerm.tags.Noun) {
return true;
} //rocket's red glare
var twoTerm = pool.get(nextTerm.next);
if (twoTerm && twoTerm.tags.Noun && !twoTerm.tags.Pronoun) {
return true;
} //othwerwise, an adjective suggests 'is good'
if (nextTerm.tags.Adjective || nextTerm.tags.Adverb || nextTerm.tags.Verb) {
return false;
}
return false;
};
var isHas = function isHas(term, phrase) {
var terms = phrase.terms();
var index = terms.indexOf(term);
var after = terms.slice(index + 1, index + 3); //look for a past-tense verb
return after.find(function (t) {
return t.tags.PastTense;
});
};
var checkPossessive = function checkPossessive(term, phrase, world) {
//the rest of 's
var found = term.text.match(hasApostropheS);
if (found !== null) {
//spencer's thing vs spencer-is
if (isPossessive(term, phrase.pool) === true) {
term.tag('#Possessive', 'isPossessive', world);
return null;
} //'spencer is'
if (found !== null) {
if (isHas(term, phrase)) {
return [found[1], 'has'];
}
return [found[1], 'is'];
}
}
return null;
};
var _04Possessive = checkPossessive;
var hasPerfect = /[a-z\u00C0-\u00FF]'d$/;
var useDid = {
how: true,
what: true
};
/** split `i'd` into 'i had', or 'i would' */
var checkPerfect = function checkPerfect(term, phrase) {
if (hasPerfect.test(term.clean)) {
var root = term.clean.replace(/'d$/, ''); //look at the next few words
var terms = phrase.terms();
var index = terms.indexOf(term);
var after = terms.slice(index + 1, index + 4); //is it before a past-tense verb? - 'i'd walked'
for (var i = 0; i < after.length; i++) {
var t = after[i];
if (t.tags.Verb) {
if (t.tags.PastTense) {
return [root, 'had'];
} //what'd you see
if (useDid[root] === true) {
return [root, 'did'];
}
return [root, 'would'];
}
} //otherwise, 'i'd walk'
return [root, 'would'];
}
return null;
};
var _05PerfectTense = checkPerfect;
var isRange = /^([0-9]+)[-–—]([0-9]+)$/i; //split '2-4' into '2 to 4'
var checkRange = function checkRange(term) {
if (term.tags.PhoneNumber === true) {
return null;
}
var parts = term.text.match(isRange);
if (parts !== null) {
return [parts[1], 'to', parts[2]];
}
return null;
};
var _06Ranges = checkRange;
var contraction$1 = /^(l|c|d|j|m|n|qu|s|t)[\u0027\u0060\u00B4\u2018\u2019\u201A\u201B\u2032\u2035\u2039\u203A]([a-z\u00C0-\u00FF]+)$/i; // basic support for ungendered french contractions
// not perfect, but better than nothing, to support matching on french text.
var french = {
l: 'le',
// l'amour
c: 'ce',
// c'est
d: 'de',
// d'amerique
j: 'je',
// j'aime
m: 'me',
// m'appelle
n: 'ne',
// n'est
qu: 'que',
// qu'il
s: 'se',
// s'appelle
t: 'tu' // t'aime
};
var checkFrench = function checkFrench(term) {
var parts = term.text.match(contraction$1);
if (parts === null || french.hasOwnProperty(parts[1]) === false) {
return null;
}
var arr = [french[parts[1]], parts[2]];
if (arr[0] && arr[1]) {
return arr;
}
return null;
};
var _07French = checkFrench;
var isNumber = /^[0-9]+$/;
var createPhrase = function createPhrase(found, doc) {
//create phrase from ['would', 'not']
var phrase = _01Tokenizer(found.join(' '), doc.world, doc.pool())[0]; //tag it
var terms = phrase.terms();
_01Lexicon(terms, doc.world); //make these terms implicit
terms.forEach(function (t) {
t.implicit = t.text;
t.text = '';
t.clean = ''; // remove whitespace for implicit terms
t.pre = '';
t.post = ''; // tag number-ranges
if (isNumber.test(t.implicit)) {
t.tags.Number = true;
t.tags.Cardinal = true;
}
});
return phrase;
};
var contractions = function contractions(doc) {
var world = doc.world;
doc.list.forEach(function (p) {
var terms = p.terms();
for (var i = 0; i < terms.length; i += 1) {
var term = terms[i];
var found = _01Negative(term, p);
found = found || _02Simple(term);
found = found || _03Irregulars(term);
found = found || _04Possessive(term, p, world);
found = found || _05PerfectTense(term, p);
found = found || _06Ranges(term);
found = found || _07French(term); //add them in
if (found !== null) {
var newPhrase = createPhrase(found, doc); // keep tag NumberRange, if we had it
if (p.has('#NumberRange') === true) {
doc.buildFrom([newPhrase]).tag('NumberRange');
} //set text as contraction
var firstTerm = newPhrase.terms(0);
firstTerm.text = term.text; //grab sub-phrase to remove
var match = p.buildFrom(term.id, 1, doc.pool());
match.replace(newPhrase, doc, true);
}
}
});
return doc;
};
var _03Contractions = contractions;
var hasWord = function hasWord(doc, word) {
var arr = doc._cache.words[word] || [];
arr = arr.map(function (i) {
return doc.list[i];
});
return doc.buildFrom(arr);
};
var hasTag = function hasTag(doc, tag) {
var arr = doc._cache.tags[tag] || [];
arr = arr.map(function (i) {
return doc.list[i];
});
return doc.buildFrom(arr);
}; //mostly pos-corections here
var miscCorrection = function miscCorrection(doc) {
//exactly like
var m = hasWord(doc, 'like');
m.match('#Adverb like').notIf('(really|generally|typically|usually|sometimes|often) [like]').tag('Adverb', 'adverb-like'); //the orange.
m = hasTag(doc, 'Adjective');
m.match('#Determiner #Adjective$').notIf('(#Comparative|#Superlative)').terms(1).tag('Noun', 'the-adj-1'); // Firstname x (dangerous)
m = hasTag(doc, 'FirstName');
m.match('#FirstName (#Noun|@titleCase)').ifNo('^#Possessive').ifNo('#Pronoun').ifNo('@hasComma .').lastTerm().tag('#LastName', 'firstname-noun'); //three trains / one train
m = hasTag(doc, 'Value');
m = m.match('#Value #PresentTense');
if (m.found) {
if (m.has('(one|1)') === true) {
m.terms(1).tag('Singular', 'one-presentTense');
} else {
m.terms(1).tag('Plural', 'value-presentTense');
}
} // well i've been...
doc.match('^(well|so|okay)').tag('Expression', 'well-'); //been walking
m = hasTag(doc, 'Gerund');
m.match("(be|been) (#Adverb|not)+? #Gerund").not('#Verb$').tag('Auxiliary', 'be-walking'); // directive verb - 'use reverse'
doc.match('(try|use|attempt|build|make) #Verb').ifNo('(@hasComma|#Negative|#Copula|will|be)').lastTerm().tag('#Noun', 'do-verb'); //possessives
//'her match' vs 'let her match'
m = hasTag(doc, 'Possessive');
m = m.match('#Possessive [#Infinitive]', 0);
if (!m.lookBehind('(let|made|make|force|ask)').found) {
m.tag('Noun', 'her-match');
}
return doc;
};
var fixMisc = miscCorrection;
var unique$5 = function unique(arr) {
var obj = {};
for (var i = 0; i < arr.length; i++) {
obj[arr[i]] = true;
}
return Object.keys(obj);
};
var _unique = unique$5;
// order matters
var list = [// ==== Mutliple tags ====
{
match: 'too much',
tag: 'Adverb Adjective',
reason: 'bit-4'
}, // u r cool
{
match: 'u r',
tag: 'Pronoun Copula',
reason: 'u r'
}, //sometimes adverbs - 'pretty good','well above'
{
match: '#Copula (pretty|dead|full|well) (#Adjective|#Noun)',
tag: '#Copula #Adverb #Adjective',
reason: 'sometimes-adverb'
}, //walking is cool
{
match: '[#Gerund] #Adverb? not? #Copula',
group: 0,
tag: 'Activity',
reason: 'gerund-copula'
}, //walking should be fun
{
match: '[#Gerund] #Modal',
group: 0,
tag: 'Activity',
reason: 'gerund-modal'
}, //swear-words as non-expression POS
{
match: 'holy (shit|fuck|hell)',
tag: 'Expression',
reason: 'swears-expression'
}, //Aircraft designer
{
match: '#Noun #Actor',
tag: 'Actor',
reason: 'thing-doer'
}, {
match: '#Conjunction [u]',
group: 0,
tag: 'Pronoun',
reason: 'u-pronoun-2'
}, //'u' as pronoun
{
match: '[u] #Verb',
group: 0,
tag: 'Pronoun',
reason: 'u-pronoun-1'
}, // ==== Determiners ====
{
match: '#Noun [(who|whom)]',
group: 0,
tag: 'Determiner',
reason: 'captain-who'
}, //that car goes
{
match: 'that #Noun [#Verb]',
group: 0,
tag: 'Determiner',
reason: 'that-determiner'
}, {
match: 'a bit much',
tag: 'Determiner Adverb Adjective',
reason: 'bit-3'
}, // ==== Propositions ====
//all students
{
match: '#Verb #Adverb? #Noun [(that|which)]',
group: 0,
tag: 'Preposition',
reason: 'that-prep'
}, //work, which has been done.
{
match: '@hasComma [which] (#Pronoun|#Verb)',
group: 0,
tag: 'Preposition',
reason: 'which-copula'
},
{
match: 'just [like]',
group: 0,
tag: 'Preposition',
reason: 'like-preposition'
}, //folks like her
{
match: '#Noun [like] #Noun',
group: 0,
tag: 'Preposition',
reason: 'noun-like'
}, //fix for busted-up phrasalVerbs
{
match: '#Noun [#Particle]',
group: 0,
tag: 'Preposition',
reason: 'repair-noPhrasal'
}, // ==== Conditions ====
// had he survived,
{
match: '[had] #Noun+ #PastTense',
group: 0,
tag: 'Condition',
reason: 'had-he'
}, // were he to survive
{
match: '[were] #Noun+ to #Infinitive',
group: 0,
tag: 'Condition',
reason: 'were-he'
}, // ==== Questions ====
//the word 'how'
{
match: '^how',
tag: 'QuestionWord',
reason: 'how-question'
}, {
match: '[how] (#Determiner|#Copula|#Modal|#PastTense)',
group: 0,
tag: 'QuestionWord',
reason: 'how-is'
}, // //the word 'which'
{
match: '^which',
tag: 'QuestionWord',
reason: 'which-question'
}, {
match: '[which] . (#Noun)+ #Pronoun',
group: 0,
tag: 'QuestionWord',
reason: 'which-question2'
}, // { match: 'which', tag: 'QuestionWord', reason: 'which-question3' },
// ==== Conjunctions ====
{
match: '[so] #Noun',
group: 0,
tag: 'Conjunction',
reason: 'so-conj'
}, //how he is driving
{
match: '[(who|what|where|why|how|when)] #Noun #Copula #Adverb? (#Verb|#Adjective)',
group: 0,
tag: 'Conjunction',
reason: 'how-he-is-x'
},
{
match: '[(who|what|where|why|how|when)] #Noun #Adverb? #Infinitive not? #Gerund',
group: 0,
tag: 'Conjunction',
reason: 'when i go fishing'
},
{ /*@blab+*/
match: '^[(who|what|where|why|how|when)] #Adjective? #Verb #Pronoun',
group: 0,
tag: 'Adverb',
reason: 'where-question'
},
{ /*@blab+*/
match: 'the way',
tag: 'Noun',
reason: 'fix-1'
},
];
var _01Misc = list;
//Dates: 'june' or 'may'
var dates = '(april|june|may|jan|august|eve)';
var list$1 = [// ==== Holiday ====
{
match: '#Holiday (day|eve)',
tag: 'Holiday',
reason: 'holiday-day'
}, // the captain who
// ==== WeekDay ====
// sun the 5th
{
match: '[sun] the #Ordinal',
tag: 'WeekDay',
reason: 'sun-the-5th'
}, //sun feb 2
{
match: '[sun] #Date',
group: 0,
tag: 'WeekDay',
reason: 'sun-feb'
}, //1pm next sun
{
match: '#Date (on|this|next|last|during)? [sun]',
group: 0,
tag: 'WeekDay',
reason: '1pm-sun'
}, //this sat
{
match: "(in|by|before|during|on|until|after|of|within|all) [sat]",
group: 0,
tag: 'WeekDay',
reason: 'sat'
}, //sat november
{
match: '[sat] #Date',
group: 0,
tag: 'WeekDay',
reason: 'sat-feb'
}, // ==== Month ====
//all march
{
match: "#Preposition [(march|may)]",
group: 0,
tag: 'Month',
reason: 'in-month'
}, //this march
{
match: "this [(march|may)]",
group: 0,
tag: 'Month',
reason: 'this-month'
}, {
match: "next [(march|may)]",
group: 0,
tag: 'Month',
reason: 'this-month'
}, {
match: "last [(march|may)]",
group: 0,
tag: 'Month',
reason: 'this-month'
}, // march 5th
{
match: "[(march|may)] the? #Value",
group: 0,
tag: 'Month',
reason: 'march-5th'
}, // 5th of march
{
match: "#Value of? [(march|may)]",
group: 0,
tag: 'Month',
reason: '5th-of-march'
}, // march and feb
{
match: "[(march|may)] .? #Date",
group: 0,
tag: 'Month',
reason: 'march-and-feb'
}, // feb to march
{
match: "#Date .? [(march|may)]",
group: 0,
tag: 'Month',
reason: 'feb-and-march'
}, //quickly march
{
match: "#Adverb [(march|may)]",
group: 0,
tag: 'Verb',
reason: 'quickly-march'
}, //march quickly
{
match: "[(march|may)] #Adverb",
group: 0,
tag: 'Verb',
reason: 'march-quickly'
}, //5th of March
{
match: '#Value of #Month',
tag: 'Date',
reason: 'value-of-month'
}, //5 March
{
match: '#Cardinal #Month',
tag: 'Date',
reason: 'cardinal-month'
}, //march 5 to 7
{
match: '#Month #Value to #Value',
tag: 'Date',
reason: 'value-to-value'
}, //march the 12th
{
match: '#Month the #Value',
tag: 'Date',
reason: 'month-the-value'
}, //june 7
{
match: '(#WeekDay|#Month) #Value',
tag: 'Date',
reason: 'date-value'
}, //7 june
{
match: '#Value (#WeekDay|#Month)',
tag: 'Date',
reason: 'value-date'
}, //may twenty five
{
match: '(#TextValue && #Date) #TextValue',
tag: 'Date',
reason: 'textvalue-date'
}, // in june
{
match: "in [".concat(dates, "]"),
group: 0,
tag: 'Date',
reason: 'in-june'
}, {
match: "during [".concat(dates, "]"),
group: 0,
tag: 'Date',
reason: 'in-june'
}, {
match: "on [".concat(dates, "]"),
group: 0,
tag: 'Date',
reason: 'in-june'
}, {
match: "by [".concat(dates, "]"),
group: 0,
tag: 'Date',
reason: 'in-june'
}, {
match: "before [".concat(dates, "]"),
group: 0,
tag: 'Date',
reason: 'in-june'
}, {
match: "#Date [".concat(dates, "]"),
group: 0,
tag: 'Date',
reason: 'in-june'
}, // june 1992
{
match: "".concat(dates, " #Value"),
tag: 'Date',
reason: 'june-5th'
}, {
match: "".concat(dates, " #Date"),
tag: 'Date',
reason: 'june-5th'
}, // June Smith
{
match: "".concat(dates, " #ProperNoun"),
tag: 'Person',
reason: 'june-smith',
safe: true
}, // june m. Cooper
{
match: "".concat(dates, " #Acronym? (#ProperNoun && !#Month)"),
tag: 'Person',
reason: 'june-smith-jr'
}, // 'second'
{
match: "#Cardinal [second]",
tag: 'Unit',
reason: 'one-second'
}];
var _02Dates = list$1;
var _03Noun = [// ==== Plural ====
//there are reasons
{
match: 'there (are|were) #Adjective? [#PresentTense]',
group: 0,
tag: 'Plural',
reason: 'there-are'
}, // ==== Singular ====
//the sun
{
match: '#Determiner [sun]',
group: 0,
tag: 'Singular',
reason: 'the-sun'
}, //did a 900, paid a 20
{
match: '#Verb (a|an) [#Value]',
group: 0,
tag: 'Singular',
reason: 'did-a-value'
}, //'the can'
{
match: '#Determiner [(can|will|may)]',
group: 0,
tag: 'Singular',
reason: 'the can'
}, // ==== Possessive ====
//spencer kelly's
{
match: '#FirstName #Acronym? (#Possessive && #LastName)',
tag: 'Possessive',
reason: 'name-poss'
}, //Super Corp's fundraiser
{
match: '#Organization+ #Possessive',
tag: 'Possessive',
reason: 'org-possessive'
}, //Los Angeles's fundraiser
{
match: '#Place+ #Possessive',
tag: 'Possessive',
reason: 'place-possessive'
}, // assign all tasks
{
match: '#Verb (all|every|each|most|some|no) [#PresentTense]',
group: 0,
tag: 'Noun',
reason: 'all-presentTense'
}, //big dreams, critical thinking
{
match: '(#Adjective && !all) [#PresentTense]',
group: 0,
tag: 'Noun',
reason: 'adj-presentTense'
}, //his fine
{
match: '(his|her|its) [#Adjective]',
group: 0,
tag: 'Noun',
reason: 'his-fine'
}, //some pressing issues
{
match: 'some [#Verb] #Plural',
group: 0,
tag: 'Noun',
reason: 'determiner6'
}, //'more' is not always an adverb
{
match: 'more #Noun',
tag: 'Noun',
reason: 'more-noun'
}, {
match: '(#Noun && @hasComma) #Noun (and|or) [#PresentTense]',
group: 0,
tag: 'Noun',
reason: 'noun-list'
}, //3 feet
{
match: '(right|rights) of .',
tag: 'Noun',
reason: 'right-of'
}, // a bit
{
match: 'a [bit]',
group: 0,
tag: 'Noun',
reason: 'bit-2'
}, //running-a-show
{
match: '#Gerund #Determiner [#Infinitive]',
group: 0,
tag: 'Noun',
reason: 'running-a-show'
}, //the-only-reason
{
match: '#Determiner #Adverb [#Infinitive]',
group: 0,
tag: 'Noun',
reason: 'the-reason'
}, //the nice swim
{
match: '(the|this|those|these) #Adjective [#Verb]',
group: 0,
tag: 'Noun',
reason: 'the-adj-verb'
}, // the truly nice swim
{
match: '(the|this|those|these) #Adverb #Adjective [#Verb]',
group: 0,
tag: 'Noun',
reason: 'determiner4'
}, //the orange is
{
match: '#Determiner [#Adjective] (#Copula|#PastTense|#Auxiliary)',
group: 0,
tag: 'Noun',
reason: 'the-adj-2'
}, // a stream runs
{
match: '(the|this|a|an) [#Infinitive] #Adverb? #Verb',
group: 0,
tag: 'Noun',
reason: 'determiner5'
}, //the test string
{
match: '#Determiner [#Infinitive] #Noun',
group: 0,
tag: 'Noun',
reason: 'determiner7'
}, //by a bear.
{
match: '#Determiner #Adjective [#Infinitive]$',
group: 0,
tag: 'Noun',
reason: 'a-inf'
}, //the wait to vote
{
match: '(the|this) [#Verb] #Preposition .',
group: 0,
tag: 'Noun',
reason: 'determiner1'
}, //a sense of
{
match: '#Determiner [#Verb] of',
group: 0,
tag: 'Noun',
reason: 'the-verb-of'
}, //the threat of force
{
match: '#Determiner #Noun of [#Verb]',
group: 0,
tag: 'Noun',
reason: 'noun-of-noun'
}, //the western line
{
match: '#Determiner [(western|eastern|northern|southern|central)] #Noun',
group: 0,
tag: 'Noun',
reason: 'western-line'
}, //her polling
{
match: '#Possessive [#Gerund]',
group: 0,
tag: 'Noun',
reason: 'her-polling'
}, //her fines
{
match: '(his|her|its) [#PresentTense]',
group: 0,
tag: 'Noun',
reason: 'its-polling'
}, //linear algebra
{
match: '(#Determiner|#Value) [(linear|binary|mobile|lexical|technical|computer|scientific|formal)] #Noun',
group: 0,
tag: 'Noun',
reason: 'technical-noun'
}, // walk the walk
{
match: '(the|those|these) #Adjective? [#Infinitive]',
group: 0,
tag: 'Noun',
reason: 'det-inf'
}, {
match: '(the|those|these) #Adjective? [#PresentTense]',
group: 0,
tag: 'Noun',
reason: 'det-pres'
}, {
match: '(the|those|these) #Adjective? [#PastTense]',
group: 0,
tag: 'Noun',
reason: 'det-past'
}, //air-flow
{
match: '(#Noun && @hasHyphen) #Verb',
tag: 'Noun',
reason: 'hyphen-verb'
}, //is no walk
{
match: 'is no [#Verb]',
group: 0,
tag: 'Noun',
reason: 'is-no-verb'
}, //different views than
{
match: '[#Verb] than',
group: 0,
tag: 'Noun',
reason: 'correction'
}, // goes to sleep
{
match: '(go|goes|went) to [#Infinitive]',
group: 0,
tag: 'Noun',
reason: 'goes-to-verb'
}, //a great run
{
match: '(a|an) #Adjective [(#Infinitive|#PresentTense)]',
tag: 'Noun',
reason: 'a|an2'
}, //a tv show
{
match: '(a|an) #Noun [#Infinitive]',
group: 0,
tag: 'Noun',
reason: 'a-noun-inf'
}, //do so
{
match: 'do [so]',
group: 0,
tag: 'Noun',
reason: 'so-noun'
}, //is mark hughes
{
match: '#Copula [#Infinitive] #Noun',
group: 0,
tag: 'Noun',
reason: 'is-pres-noun'
}, //
// { match: '[#Infinitive] #Copula', group: 0, tag: 'Noun', reason: 'inf-copula' },
//a close
{
match: '#Determiner #Adverb? [close]',
group: 0,
tag: 'Adjective',
reason: 'a-close'
}, // what the hell
{
match: '#Determiner [(shit|damn|hell)]',
group: 0,
tag: 'Noun',
reason: 'swears-noun'
}];
var adjectives$1 = '(misty|rusty|dusty|rich|randy)';
var list$2 = [// all fell apart
{
match: '[all] #Determiner? #Noun',
group: 0,
tag: 'Adjective',
reason: 'all-noun'
}, // very rusty
{
match: "#Adverb [".concat(adjectives$1, "]"),
group: 0,
tag: 'Adjective',
reason: 'really-rich'
}, // rusty smith
{
match: "".concat(adjectives$1, " #Person"),
tag: 'Person',
reason: 'randy-smith'
}, // rusty a. smith
{
match: "".concat(adjectives$1, " #Acronym? #ProperNoun"),
tag: 'Person',
reason: 'rusty-smith'
}, //sometimes not-adverbs
{
match: '#Copula [(just|alone)]$',
group: 0,
tag: 'Adjective',
reason: 'not-adverb'
}, //jack is guarded
{
match: '#Singular is #Adverb? [#PastTense$]',
group: 0,
tag: 'Adjective',
reason: 'is-filled'
}, // smoked poutine is
{
match: '[#PastTense] #Singular is',
group: 0,
tag: 'Adjective',
reason: 'smoked-poutine'
}, // baked onions are
{
match: '[#PastTense] #Plural are',
group: 0,
tag: 'Adjective',
reason: 'baked-onions'
}, //a staggering cost
{
match: '(a|an) [#Gerund]',
group: 0,
tag: 'Adjective',
reason: 'a|an'
}, // is f*ed up
{
match: '#Copula [fucked up?]',
tag: 'Adjective',
reason: 'swears-adjective'
}, //jack seems guarded
{
match: '#Singular (seems|appears) #Adverb? [#PastTense$]',
group: 0,
tag: 'Adjective',
reason: 'seems-filled'
}];
var _04Adjective = list$2;
var _05Adverb = [//still good
{
match: '[still] #Adjective',
group: 0,
tag: 'Adverb',
reason: 'still-advb'
}, //still make
{
match: '[still] #Verb',
group: 0,
tag: 'Adverb',
reason: 'still-verb'
}, // so hot
{
match: '[so] #Adjective',
group: 0,
tag: 'Adverb',
reason: 'so-adv'
}, // all singing
{
match: '[all] #Verb',
group: 0,
tag: 'Adverb',
reason: 'all-verb'
}, // sing like an angel
{
match: '#Verb [like]',
group: 0,
tag: 'Adverb',
reason: 'verb-like'
}, //barely even walk
{
match: '(barely|hardly) even',
tag: 'Adverb',
reason: 'barely-even'
}, //cheering hard - dropped -ly's
{
match: '#PresentTense [(hard|quick|long|bright|slow)]',
group: 0,
tag: 'Adverb',
reason: 'lazy-ly'
}, // much appreciated
{
match: '[much] #Adjective',
group: 0,
tag: 'Adverb',
reason: 'bit-1'
}];
var _06Value = [// ==== PhoneNumber ====
//1 800 ...
{
match: '1 #Value #PhoneNumber',
tag: 'PhoneNumber',
reason: '1-800-Value'
}, //(454) 232-9873
{
match: '#NumericValue #PhoneNumber',
tag: 'PhoneNumber',
reason: '(800) PhoneNumber'
}, // ==== Currency ====
// chinese yuan
{
match: '#Demonym #Currency',
tag: 'Currency',
reason: 'demonym-currency'
}, // ==== Ordinal ====
{
match: '[second] #Noun',
group: 0,
tag: 'Ordinal',
reason: 'second-noun'
}, // ==== Unit ====
//5 yan
{
match: '#Value+ [#Currency]',
group: 0,
tag: 'Unit',
reason: '5-yan'
}, {
match: '#Value [(foot|feet)]',
group: 0,
tag: 'Unit',
reason: 'foot-unit'
}, //minus 7
{
match: '(minus|negative) #Value',
tag: 'Value',
reason: 'minus-value'
}, //5 kg.
{
match: '#Value [#Abbreviation]',
group: 0,
tag: 'Unit',
reason: 'value-abbr'
}, {
match: '#Value [k]',
group: 0,
tag: 'Unit',
reason: 'value-k'
}, {
match: '#Unit an hour',
tag: 'Unit',
reason: 'unit-an-hour'
}, //seven point five
{
match: '#Value (point|decimal) #Value',
tag: 'Value',
reason: 'value-point-value'
}, // ten bucks
{
match: '(#Value|a) [(buck|bucks|grand)]',
group: 0,
tag: 'Currency',
reason: 'value-bucks'
}, //quarter million
{
match: '#Determiner [(half|quarter)] #Ordinal',
group: 0,
tag: 'Value',
reason: 'half-ordinal'
}, {
match: 'a #Value',
tag: 'Value',
reason: 'a-value'
}, // ==== Money ====
{
match: '[#Value+] #Currency',
group: 0,
tag: 'Money',
reason: '15 usd'
}, // thousand and two
{
match: "(hundred|thousand|million|billion|trillion|quadrillion)+ and #Value",
tag: 'Value',
reason: 'magnitude-and-value'
}, //'a/an' can mean 1 - "a hour"
{
match: '!once [(a|an)] (#Duration|hundred|thousand|million|billion|trillion)',
group: 0,
tag: 'Value',
reason: 'a-is-one'
}];
var verbs$1 = '(pat|wade|ollie|will|rob|buck|bob|mark|jack)';
var list$3 = [// ==== Tense ====
//he left
{
match: '#Noun #Adverb? [left]',
group: 0,
tag: 'PastTense',
reason: 'left-verb'
}, //this rocks
{
match: '(this|that) [#Plural]',
group: 0,
tag: 'PresentTense',
reason: 'this-verbs'
}, // ==== Auxiliary ====
//was walking
{
match: "[#Copula (#Adverb|not)+?] (#Gerund|#PastTense)",
group: 0,
tag: 'Auxiliary',
reason: 'copula-walking'
}, //support a splattering of auxillaries before a verb
{
match: "[(has|had) (#Adverb|not)+?] #PastTense",
group: 0,
tag: 'Auxiliary',
reason: 'had-walked'
}, //would walk
{
match: "[#Adverb+? (#Modal|did)+ (#Adverb|not)+?] #Verb",
group: 0,
tag: 'Auxiliary',
reason: 'modal-verb'
}, //would have had
{
match: "[#Modal (#Adverb|not)+? have (#Adverb|not)+? had (#Adverb|not)+?] #Verb",
group: 0,
tag: 'Auxiliary',
reason: 'would-have'
}, //would be walking
{
match: "#Modal (#Adverb|not)+? be (#Adverb|not)+? #Verb",
group: 0,
tag: 'Auxiliary',
reason: 'would-be'
}, //had been walking
{
match: "(#Modal|had|has) (#Adverb|not)+? been (#Adverb|not)+? #Verb",
group: 0,
tag: 'Auxiliary',
reason: 'had-been'
}, //was walking
{
match: "[#Copula (#Adverb|not)+?] (#Gerund|#PastTense)",
group: 0,
tag: 'Auxiliary',
reason: 'copula-walking'
}, //support a splattering of auxillaries before a verb
{
match: "[(has|had) (#Adverb|not)+?] #PastTense",
group: 0,
tag: 'Auxiliary',
reason: 'had-walked'
}, // will walk
{
match: '[(do|does|will|have|had)] (not|#Adverb)? #Verb',
group: 0,
tag: 'Auxiliary',
reason: 'have-had'
}, // about to go
{
match: '[about to] #Adverb? #Verb',
group: 0,
tag: ['Auxiliary', 'Verb'],
reason: 'about-to'
}, //would be walking
{
match: "#Modal (#Adverb|not)+? be (#Adverb|not)+? #Verb",
group: 0,
tag: 'Auxiliary',
reason: 'would-be'
}, //would have had
{
match: "[#Modal (#Adverb|not)+? have (#Adverb|not)+? had (#Adverb|not)+?] #Verb",
group: 0,
tag: 'Auxiliary',
reason: 'would-have'
}, //had been walking
{
match: "(#Modal|had|has) (#Adverb|not)+? been (#Adverb|not)+? #Verb",
group: 0,
tag: 'Auxiliary',
reason: 'had-been'
}, // was being driven
{
match: '[(be|being|been)] #Participle',
group: 0,
tag: 'Auxiliary',
reason: 'being-foo'
}, // ==== Phrasal ====
//'foo-up'
{
match: '(#Verb && @hasHyphen) up',
group: 0,
tag: 'PhrasalVerb',
reason: 'foo-up'
}, {
match: '(#Verb && @hasHyphen) off',
group: 0,
tag: 'PhrasalVerb',
reason: 'foo-off'
}, {
match: '(#Verb && @hasHyphen) over',
group: 0,
tag: 'PhrasalVerb',
reason: 'foo-over'
}, {
match: '(#Verb && @hasHyphen) out',
group: 0,
tag: 'PhrasalVerb',
reason: 'foo-out'
}, //fall over
{
match: '#PhrasalVerb [#PhrasalVerb]',
group: 0,
tag: 'Particle',
reason: 'phrasal-particle'
}, // ==== Copula ====
//will be running (not copula)
{
match: '[will #Adverb? not? #Adverb? be] #Gerund',
group: 0,
tag: 'Copula',
reason: 'will-be-copula'
}, //for more complex forms, just tag 'be'
{
match: 'will #Adverb? not? #Adverb? [be] #Adjective',
group: 0,
tag: 'Copula',
reason: 'be-copula'
}, // ==== Infinitive ====
//march to
{
match: '[march] (up|down|back|to|toward)',
group: 0,
tag: 'Infinitive',
reason: 'march-to'
}, //must march
{
match: '#Modal [march]',
group: 0,
tag: 'Infinitive',
reason: 'must-march'
}, //let him glue
{
match: '(let|make|made) (him|her|it|#Person|#Place|#Organization)+ [#Singular] (a|an|the|it)',
group: 0,
tag: 'Infinitive',
reason: 'let-him-glue'
}, //he quickly foo
{
match: '#Noun #Adverb [#Noun]',
group: 0,
tag: 'Verb',
reason: 'quickly-foo'
}, //will secure our
{
match: 'will [#Adjective]',
group: 0,
tag: 'Verb',
reason: 'will-adj'
}, //he disguised the thing
{
match: '#Pronoun [#Adjective] #Determiner #Adjective? #Noun',
group: 0,
tag: 'Verb',
reason: 'he-adj-the'
}, //is eager to go
{
match: '#Copula [#Adjective to] #Verb',
group: 0,
tag: 'Verb',
reason: 'adj-to'
}, // open the door
{
match: '[open] #Determiner',
group: 0,
tag: 'Infinitive',
reason: 'open-the'
}, // would wade
{
match: "#Modal [".concat(verbs$1, "]"),
group: 0,
tag: 'Verb',
reason: 'would-mark'
}, {
match: "#Adverb [".concat(verbs$1, "]"),
group: 0,
tag: 'Verb',
reason: 'really-mark'
}, // wade smith
{
match: "".concat(verbs$1, " #Person"),
tag: 'Person',
reason: 'rob-smith'
}, // wade m. Cooper
{
match: "".concat(verbs$1, " #Acronym? #ProperNoun"),
tag: 'Person',
reason: 'rob-a-smith'
}, // damn them
{
match: '[shit] (#Determiner|#Possessive|them)',
group: 0,
tag: 'Verb',
reason: 'swear1-verb'
}, {
match: '[damn] (#Determiner|#Possessive|them)',
group: 0,
tag: 'Verb',
reason: 'swear2-verb'
}, {
match: '[fuck] (#Determiner|#Possessive|them)',
group: 0,
tag: 'Verb',
reason: 'swear3-verb'
}];
var _07Verbs = list$3;
var places = '(paris|alexandria|houston|kobe|salvador|sydney)';
var list$4 = [// ==== Region ====
//West Norforlk
{
match: '(west|north|south|east|western|northern|southern|eastern)+ #Place',
tag: 'Region',
reason: 'west-norfolk'
}, //some us-state acronyms (exlude: al, in, la, mo, hi, me, md, ok..)
{
match: '#City [(al|ak|az|ar|ca|ct|dc|fl|ga|id|il|nv|nh|nj|ny|oh|or|pa|sc|tn|tx|ut|vt|pr)]',
group: 0,
tag: 'Region',
reason: 'us-state'
}, //Foo District
{
match: '#ProperNoun+ (district|region|province|county|prefecture|municipality|territory|burough|reservation)',
tag: 'Region',
reason: 'foo-district'
}, //District of Foo
{
match: '(district|region|province|municipality|territory|burough|state) of #ProperNoun',
tag: 'Region',
reason: 'district-of-Foo'
}, // in Foo California
{
match: 'in [#ProperNoun] #Place',
group: 0,
tag: 'Place',
reason: 'propernoun-place'
}, // ==== Address ====
{
match: '#Value #Noun (st|street|rd|road|crescent|cr|way|tr|terrace|avenue|ave)',
tag: 'Address',
reason: 'address-st'
}, // in houston
{
match: "in [".concat(places, "]"),
group: 0,
tag: 'Place',
reason: 'in-paris'
}, {
match: "near [".concat(places, "]"),
group: 0,
tag: 'Place',
reason: 'near-paris'
}, {
match: "at [".concat(places, "]"),
group: 0,
tag: 'Place',
reason: 'at-paris'
}, {
match: "from [".concat(places, "]"),
group: 0,
tag: 'Place',
reason: 'from-paris'
}, {
match: "to [".concat(places, "]"),
group: 0,
tag: 'Place',
reason: 'to-paris'
}, {
match: "#Place [".concat(places, "]"),
group: 0,
tag: 'Place',
reason: 'tokyo-paris'
}, // houston texas
{
match: "[".concat(places, "] #Place"),
group: 0,
tag: 'Place',
reason: 'paris-france'
}];
var _08Place = list$4;
var _09Org = [//John & Joe's
{
match: '#Noun (&|n) #Noun',
tag: 'Organization',
reason: 'Noun-&-Noun'
}, // teachers union of Ontario
{
match: '#Organization of the? #ProperNoun',
tag: 'Organization',
reason: 'org-of-place',
safe: true
}, //walmart USA
{
match: '#Organization #Country',
tag: 'Organization',
reason: 'org-country'
}, //organization
{
match: '#ProperNoun #Organization',
tag: 'Organization',
reason: 'titlecase-org'
}, //FitBit Inc
{
match: '#ProperNoun (ltd|co|inc|dept|assn|bros)',
tag: 'Organization',
reason: 'org-abbrv'
}, // the OCED
{
match: 'the [#Acronym]',
group: 0,
tag: 'Organization',
reason: 'the-acronym',
safe: true
}, // global trade union
{
match: '(world|global|international|national|#Demonym) #Organization',
tag: 'Organization',
reason: 'global-org'
}, // schools
{
match: '#Noun+ (public|private) school',
tag: 'School',
reason: 'noun-public-school'
}];
var nouns$1 = '(rose|robin|dawn|ray|holly|bill|joy|viola|penny|sky|violet|daisy|melody|kelvin|hope|mercedes|olive|jewel|faith|van|charity|miles|lily|summer|dolly|rod|dick|cliff|lane|reed|kitty|art|jean|trinity)';
var months = '(january|april|may|june|jan|sep)'; //summer|autumn
var list$5 = [// ==== Honorific ====
{
match: '[(1st|2nd|first|second)] #Honorific',
group: 0,
tag: 'Honorific',
reason: 'ordinal-honorific'
}, {
match: '[(private|general|major|corporal|lord|lady|secretary|premier)] #Honorific? #Person',
group: 0,
tag: 'Honorific',
reason: 'ambg-honorifics'
}, // ==== FirstNames ====
//is foo Smith
{
match: '#Copula [(#Noun|#PresentTense)] #LastName',
group: 0,
tag: 'FirstName',
reason: 'copula-noun-lastname'
}, //pope francis
{
match: '(lady|queen|sister) #ProperNoun',
tag: 'FemaleName',
reason: 'lady-titlecase',
safe: true
}, {
match: '(king|pope|father) #ProperNoun',
tag: 'MaleName',
reason: 'pope-titlecase',
safe: true
}, //ambiguous-but-common firstnames
{
match: '[(will|may|april|june|said|rob|wade|ray|rusty|drew|miles|jack|chuck|randy|jan|pat|cliff|bill)] #LastName',
group: 0,
tag: 'FirstName',
reason: 'maybe-lastname'
}, // ==== Nickname ====
// Dwayne 'the rock' Johnson
{
match: '#FirstName [#Determiner #Noun] #LastName',
group: 0,
tag: 'NickName',
reason: 'first-noun-last'
}, //my buddy
{
match: '#Possessive [#FirstName]',
group: 0,
tag: 'Person',
reason: 'possessive-name'
}, {
match: '#Acronym #ProperNoun',
tag: 'Person',
reason: 'acronym-titlecase',
safe: true
}, //ludwig van beethovan
{
match: '#Person (jr|sr|md)',
tag: 'Person',
reason: 'person-honorific'
}, //peter II
{
match: '#Person #Person the? #RomanNumeral',
tag: 'Person',
reason: 'roman-numeral'
}, //'Professor Fink', 'General McCarthy'
{
match: '#FirstName [/^[^aiurck]$/]',
group: 0,
tag: ['Acronym', 'Person'],
reason: 'john-e'
}, //Doctor john smith jr
//general pearson
{
match: '#Honorific #Person',
tag: 'Person',
reason: 'honorific-person'
}, //remove single 'mr'
{
match: '#Honorific #Acronym',
tag: 'Person',
reason: 'Honorific-TitleCase'
}, //j.k Rowling
{
match: '#Noun van der? #Noun',
tag: 'Person',
reason: 'von der noun',
safe: true
}, //king of spain
{
match: '(king|queen|prince|saint|lady) of? #Noun',
tag: 'Person',
reason: 'king-of-noun',
safe: true
}, //Foo U Ford
{
match: '[#ProperNoun] #Person',
group: 0,
tag: 'Person',
reason: 'proper-person',
safe: true
}, // al sharpton
{
match: 'al (#Person|#ProperNoun)',
tag: 'Person',
reason: 'al-borlen',
safe: true
}, //ferdinand de almar
{
match: '#FirstName de #Noun',
tag: 'Person',
reason: 'bill-de-noun'
}, //Osama bin Laden
{
match: '#FirstName (bin|al) #Noun',
tag: 'Person',
reason: 'bill-al-noun'
}, //John L. Foo
{
match: '#FirstName #Acronym #ProperNoun',
tag: 'Person',
reason: 'bill-acronym-title'
}, //Andrew Lloyd Webber
{
match: '#FirstName #FirstName #ProperNoun',
tag: 'Person',
reason: 'bill-firstname-title'
}, //Mr Foo
{
match: '#Honorific #FirstName? #ProperNoun',
tag: 'Person',
reason: 'dr-john-Title'
}, //peter the great
{
match: '#FirstName the #Adjective',
tag: 'Person',
reason: 'name-the-great'
}, //very common-but-ambiguous lastnames
{
match: '#FirstName (green|white|brown|hall|young|king|hill|cook|gray|price)',
tag: 'Person',
reason: 'bill-green'
}, // faith smith
{
match: "".concat(nouns$1, " #Person"),
tag: 'Person',
reason: 'ray-smith',
safe: true
}, // faith m. Smith
{
match: "".concat(nouns$1, " #Acronym? #ProperNoun"),
tag: 'Person',
reason: 'ray-a-smith',
safe: true
}, //give to april
{
match: "#Infinitive #Determiner? #Adjective? #Noun? (to|for) [".concat(months, "]"),
group: 0,
tag: 'Person',
reason: 'ambig-person'
}, // remind june
{
match: "#Infinitive [".concat(months, "]"),
group: 0,
tag: 'Person',
reason: 'infinitive-person'
}, // may waits for
{
match: "[".concat(months, "] #PresentTense for"),
group: 0,
tag: 'Person',
reason: 'ambig-active-for'
}, // may waits to
{
match: "[".concat(months, "] #PresentTense to"),
group: 0,
tag: 'Person',
reason: 'ambig-active-to'
}, // april will
{
match: "[".concat(months, "] #Modal"),
group: 0,
tag: 'Person',
reason: 'ambig-modal'
}, // would april
{
match: "#Modal [".concat(months, "]"),
group: 0,
tag: 'Person',
reason: 'modal-ambig'
}, // it is may
{
match: "#Copula [".concat(months, "]"),
group: 0,
tag: 'Person',
reason: 'is-may'
}, // may is
{
match: "[".concat(months, "] #Copula"),
group: 0,
tag: 'Person',
reason: 'may-is'
}, // with april
{
match: "that [".concat(months, "]"),
group: 0,
tag: 'Person',
reason: 'that-month'
}, // with april
{
match: "with [".concat(months, "]"),
group: 0,
tag: 'Person',
reason: 'with-month'
}, // for april
{
match: "for [".concat(months, "]"),
group: 0,
tag: 'Person',
reason: 'for-month'
}, // this april
{
match: "this [".concat(months, "]"),
group: 0,
tag: 'Month',
reason: 'this-may'
}, //maybe not 'this'
// next april
{
match: "next [".concat(months, "]"),
group: 0,
tag: 'Month',
reason: 'next-may'
}, // last april
{
match: "last [".concat(months, "]"),
group: 0,
tag: 'Month',
reason: 'last-may'
}, // wednesday april
{
match: "#Date [".concat(months, "]"),
group: 0,
tag: 'Month',
reason: 'date-may'
}, // may 5th
{
match: "[".concat(months, "] the? #Value"),
group: 0,
tag: 'Month',
reason: 'may-5th'
}, // 5th of may
{
match: "#Value of [".concat(months, "]"),
group: 0,
tag: 'Month',
reason: '5th-of-may'
}, // dick van dyke
{
match: '#ProperNoun (van|al|bin) #ProperNoun',
tag: 'Person',
reason: 'title-van-title',
safe: true
}, //jose de Sucre
{
match: '#ProperNoun (de|du) la? #ProperNoun',
tag: 'Person',
reason: 'title-de-title',
safe: true
}, //Jani K. Smith
{
match: '#Singular #Acronym #LastName',
tag: '#Person',
reason: 'title-acro-noun',
safe: true
}, //John Foo
{
match: '#FirstName (#Noun && #ProperNoun) #ProperNoun?',
tag: 'Person',
reason: 'firstname-titlecase'
}, //Joe K. Sombrero
{
match: '#FirstName #Acronym #Noun',
tag: 'Person',
reason: 'n-acro-noun',
safe: true
}];
var _10People = list$5;
var matches = [];
matches = matches.concat(_01Misc);
matches = matches.concat(_02Dates);
matches = matches.concat(_03Noun);
matches = matches.concat(_04Adjective);
matches = matches.concat(_05Adverb);
matches = matches.concat(_06Value);
matches = matches.concat(_07Verbs);
matches = matches.concat(_08Place);
matches = matches.concat(_09Org);
matches = matches.concat(_10People); // cache the easier conditions up-front
var cacheRequired$1 = function cacheRequired(reg) {
var needTags = [];
var needWords = [];
reg.forEach(function (obj) {
if (obj.optional === true || obj.negative === true) {
return;
}
if (obj.tag !== undefined) {
needTags.push(obj.tag);
}
if (obj.word !== undefined) {
needWords.push(obj.word);
}
});
return {
tags: _unique(needTags),
words: _unique(needWords)
};
};
var allLists = function allLists(m) {
var more = [];
var lists = m.reg.filter(function (r) {
return r.oneOf !== undefined;
});
if (lists.length === 1) {
var i = m.reg.findIndex(function (r) {
return r.oneOf !== undefined;
});
Object.keys(m.reg[i].oneOf).forEach(function (w) {
var newM = Object.assign({}, m);
newM.reg = newM.reg.slice(0);
newM.reg[i] = Object.assign({}, newM.reg[i]);
newM.reg[i].word = w;
delete newM.reg[i].operator;
delete newM.reg[i].oneOf;
newM.reason += '-' + w;
more.push(newM);
});
}
return more;
}; // parse them
var all = [];
matches.forEach(function (m) {
m.reg = syntax_1(m.match);
var enumerated = allLists(m);
if (enumerated.length > 0) {
all = all.concat(enumerated);
} else {
all.push(m);
}
});
all.forEach(function (m) {
m.required = cacheRequired$1(m.reg);
return m;
});
var matches_1 = all;
var hasEvery = function hasEvery(chances) {
if (chances.length === 0) {
return [];
}
var obj = {};
chances.forEach(function (arr) {
arr = _unique(arr);
for (var i = 0; i < arr.length; i++) {
obj[arr[i]] = obj[arr[i]] || 0;
obj[arr[i]] += 1;
}
});
var res = Object.keys(obj);
res = res.filter(function (k) {
return obj[k] === chances.length;
});
res = res.map(function (num) {
return Number(num);
});
return res;
};
var runner = function runner(doc) {
//find phrases to try for each match
matches_1.forEach(function (m) {
var allChances = [];
m.required.words.forEach(function (w) {
allChances.push(doc._cache.words[w] || []);
});
m.required.tags.forEach(function (tag) {
allChances.push(doc._cache.tags[tag] || []);
});
var worthIt = hasEvery(allChances);
if (worthIt.length === 0) {
return;
}
var phrases = worthIt.map(function (index) {
return doc.list[index];
});
var tryDoc = doc.buildFrom(phrases); // phrases getting tagged
var match = tryDoc.match(m.reg, m.group);
if (match.found) {
if (m.safe === true) {
match.tagSafe(m.tag, m.reason);
} else {
match.tag(m.tag, m.reason);
}
}
});
};
var runner_1 = runner; // console.log(hasEvery([[1, 2, 2, 3], [2, 3], []]))
// misc: 40ms
//sequence of match-tag statements to correct mis-tags
var corrections = function corrections(doc) {
runner_1(doc);
fixMisc(doc);
return doc;
};
var _04Correction = corrections;
/** POS-tag all terms in this document */
var tagger = function tagger(doc) {
var terms = doc.termList(); // check against any known-words
doc = _01Init(doc, terms); // everything has gotta be something. ¯\_(:/)_/¯
doc = _02Fallbacks(doc, terms); // support "didn't" & "spencer's"
doc = _03Contractions(doc); //set our cache, to speed things up
doc.cache(); // wiggle-around the results, so they make more sense
doc = _04Correction(doc); // remove our cache, as it's invalidated now
doc.uncache(); // run any user-given tagger functions
doc.world.taggers.forEach(function (fn) {
fn(doc);
});
return doc;
};
var _02Tagger = tagger;
var addMethod = function addMethod(Doc) {
/** */
var Abbreviations = /*#__PURE__*/function (_Doc) {
_inherits(Abbreviations, _Doc);
var _super = _createSuper(Abbreviations);
function Abbreviations() {
_classCallCheck(this, Abbreviations);
return _super.apply(this, arguments);
}
_createClass(Abbreviations, [{
key: "stripPeriods",
value: function stripPeriods() {
this.termList().forEach(function (t) {
if (t.tags.Abbreviation === true && t.next) {
t.post = t.post.replace(/^\./, '');
}
var str = t.text.replace(/\./, '');
t.set(str);
});
return this;
}
}, {
key: "addPeriods",
value: function addPeriods() {
this.termList().forEach(function (t) {
t.post = t.post.replace(/^\./, '');
t.post = '.' + t.post;
});
return this;
}
}]);
return Abbreviations;
}(Doc);
Abbreviations.prototype.unwrap = Abbreviations.prototype.stripPeriods;
Doc.prototype.abbreviations = function (n) {
var match = this.match('#Abbreviation');
if (typeof n === 'number') {
match = match.get(n);
}
return new Abbreviations(match.list, this, this.world);
};
return Doc;
};
var Abbreviations = addMethod;
var hasPeriod = /\./;
var addMethod$1 = function addMethod(Doc) {
/** */
var Acronyms = /*#__PURE__*/function (_Doc) {
_inherits(Acronyms, _Doc);
var _super = _createSuper(Acronyms);
function Acronyms() {
_classCallCheck(this, Acronyms);
return _super.apply(this, arguments);
}
_createClass(Acronyms, [{
key: "stripPeriods",
value: function stripPeriods() {
this.termList().forEach(function (t) {
var str = t.text.replace(/\./g, '');
t.set(str);
});
return this;
}
}, {
key: "addPeriods",
value: function addPeriods() {
this.termList().forEach(function (t) {
var str = t.text.replace(/\./g, '');
str = str.split('').join('.'); // don't add a end-period if there's a sentence-end one
if (hasPeriod.test(t.post) === false) {
str += '.';
}
t.set(str);
});
return this;
}
}]);
return Acronyms;
}(Doc);
Acronyms.prototype.unwrap = Acronyms.prototype.stripPeriods;
Acronyms.prototype.strip = Acronyms.prototype.stripPeriods;
Doc.prototype.acronyms = function (n) {
var match = this.match('#Acronym');
if (typeof n === 'number') {
match = match.get(n);
}
return new Acronyms(match.list, this, this.world);
};
return Doc;
};
var Acronyms = addMethod$1;
var addMethod$2 = function addMethod(Doc) {
/** split into approximate sub-sentence phrases */
Doc.prototype.clauses = function (n) {
// an awkward way to disambiguate a comma use
var commas = this["if"]('@hasComma').notIf('@hasComma @hasComma') //fun, cool...
.notIf('@hasComma . .? (and|or) .') //cool, and fun
.notIf('(#City && @hasComma) #Country') //'toronto, canada'
.notIf('(#Date && @hasComma) #Year') //'july 6, 1992'
.notIf('@hasComma (too|also)$') //at end of sentence
.match('@hasComma');
var found = this.splitAfter(commas);
var quotes = found.quotations();
found = found.splitOn(quotes);
var parentheses = found.parentheses();
found = found.splitOn(parentheses); // it is cool and it is ..
var conjunctions = found["if"]('#Copula #Adjective #Conjunction (#Pronoun|#Determiner) #Verb').match('#Conjunction');
found = found.splitBefore(conjunctions); // if it is this then that
var condition = found["if"]('if .{2,9} then .').match('then');
found = found.splitBefore(condition); // misc clause partitions
found = found.splitBefore('as well as .');
found = found.splitBefore('such as .');
found = found.splitBefore('in addition to .'); // semicolons, dashes
found = found.splitAfter('@hasSemicolon');
found = found.splitAfter('@hasDash'); // passive voice verb - '.. which was robbed is empty'
// let passive = found.match('#Noun (which|that) (was|is) #Adverb? #PastTense #Adverb?')
// if (passive.found) {
// found = found.splitAfter(passive)
// }
// //which the boy robbed
// passive = found.match('#Noun (which|that) the? #Noun+ #Adverb? #PastTense #Adverb?')
// if (passive.found) {
// found = found.splitAfter(passive)
// }
// does there appear to have relative/subordinate clause still?
var tooLong = found.filter(function (d) {
return d.wordCount() > 5 && d.match('#Verb+').length >= 2;
});
if (tooLong.found) {
var m = tooLong.splitAfter('#Noun .* #Verb .* #Noun+');
found = found.splitOn(m.eq(0));
}
if (typeof n === 'number') {
found = found.get(n);
}
return new Doc(found.list, this, this.world);
};
return Doc;
};
var Clauses = addMethod$2;
var addMethod$3 = function addMethod(Doc) {
/** */
var Contractions = /*#__PURE__*/function (_Doc) {
_inherits(Contractions, _Doc);
var _super = _createSuper(Contractions);
function Contractions(list, from, world) {
var _this;
_classCallCheck(this, Contractions);
_this = _super.call(this, list, from, world);
_this.contracted = null;
return _this;
}
/** turn didn't into 'did not' */
_createClass(Contractions, [{
key: "expand",
value: function expand() {
this.list.forEach(function (p) {
var terms = p.terms(); //change the case?
var isTitlecase = terms[0].isTitleCase();
terms.forEach(function (t, i) {
//use the implicit text
t.set(t.implicit || t.text);
t.implicit = undefined; //add whitespace
if (i < terms.length - 1 && t.post === '') {
t.post += ' ';
}
}); //set titlecase
if (isTitlecase) {
terms[0].toTitleCase();
}
});
return this;
}
}]);
return Contractions;
}(Doc); //find contractable, expanded-contractions
// const findExpanded = r => {
// let remain = r.not('#Contraction')
// let m = remain.match('(#Noun|#QuestionWord) (#Copula|did|do|have|had|could|would|will)')
// m.concat(remain.match('(they|we|you|i) have'))
// m.concat(remain.match('i am'))
// m.concat(remain.match('(#Copula|#Modal|do|does|have|has|can|will) not'))
// return m
// }
Doc.prototype.contractions = function (n) {
//find currently-contracted
var found = this.match('@hasContraction+'); //(may want to split these up)
//todo: split consecutive contractions
if (typeof n === 'number') {
found = found.get(n);
}
return new Contractions(found.list, this, this.world);
}; //aliases
Doc.prototype.expanded = Doc.prototype.isExpanded;
Doc.prototype.contracted = Doc.prototype.isContracted;
return Doc;
};
var Contractions = addMethod$3;
var addMethod$4 = function addMethod(Doc) {
//pull it apart..
var parse = function parse(doc) {
var things = doc.splitAfter('@hasComma').splitOn('(and|or) not?').not('(and|or) not?');
var beforeLast = doc.match('[.] (and|or)', 0);
return {
things: things,
conjunction: doc.match('(and|or) not?'),
beforeLast: beforeLast,
hasOxford: beforeLast.has('@hasComma')
};
};
/** cool, fun, and nice */
var Lists = /*#__PURE__*/function (_Doc) {
_inherits(Lists, _Doc);
var _super = _createSuper(Lists);
function Lists() {
_classCallCheck(this, Lists);
return _super.apply(this, arguments);
}
_createClass(Lists, [{
key: "conjunctions",
/** coordinating conjunction */
value: function conjunctions() {
return this.match('(and|or)');
}
/** split-up by list object */
}, {
key: "parts",
value: function parts() {
return this.splitAfter('@hasComma').splitOn('(and|or) not?');
}
/** remove the conjunction */
}, {
key: "items",
value: function items() {
return parse(this).things;
}
/** add a new unit to the list */
}, {
key: "add",
value: function add(str) {
this.forEach(function (p) {
var beforeLast = parse(p).beforeLast;
beforeLast.append(str); //add a comma to it
beforeLast.termList(0).addPunctuation(',');
});
return this;
}
/** remove any matching unit from the list */
}, {
key: "remove",
value: function remove(match) {
return this.items()["if"](match).remove();
}
/** return only lists that use a serial comma */
}, {
key: "hasOxfordComma",
value: function hasOxfordComma() {
return this.filter(function (doc) {
return parse(doc).hasOxford;
});
}
}, {
key: "addOxfordComma",
value: function addOxfordComma() {
var items = this.items();
var needsComma = items.eq(items.length - 2);
if (needsComma.found && needsComma.has('@hasComma') === false) {
needsComma.post(', ');
}
return this;
}
}, {
key: "removeOxfordComma",
value: function removeOxfordComma() {
var items = this.items();
var needsComma = items.eq(items.length - 2);
if (needsComma.found && needsComma.has('@hasComma') === true) {
needsComma.post(' ');
}
return this;
}
}]);
return Lists;
}(Doc); // aliases
Lists.prototype.things = Lists.prototype.items;
Doc.prototype.lists = function (n) {
var m = this["if"]('@hasComma+ .? (and|or) not? .'); // person-list
var nounList = m.match('(#Noun|#Adjective|#Determiner|#Article)+ #Conjunction not? (#Article|#Determiner)? #Adjective? #Noun+')["if"]('#Noun');
var adjList = m.match('(#Adjective|#Adverb)+ #Conjunction not? #Adverb? #Adjective+');
var verbList = m.match('(#Verb|#Adverb)+ #Conjunction not? #Adverb? #Verb+');
var result = nounList.concat(adjList);
result = result.concat(verbList);
result = result["if"]('@hasComma');
if (typeof n === 'number') {
result = m.get(n);
}
return new Lists(result.list, this, this.world);
};
return Doc;
};
var Lists = addMethod$4;
var noPlural = '(#Pronoun|#Place|#Value|#Person|#Uncountable|#Month|#WeekDay|#Holiday|#Possessive)'; //certain words can't be plural, like 'peace'
var hasPlural = function hasPlural(doc) {
if (doc.has('#Plural') === true) {
return true;
} // these can't be plural
if (doc.has(noPlural) === true) {
return false;
}
return true;
};
var hasPlural_1 = hasPlural;
var irregulars$5 = {
hour: 'an',
heir: 'an',
heirloom: 'an',
honest: 'an',
honour: 'an',
honor: 'an',
uber: 'an' //german u
}; //pronounced letters of acronyms that get a 'an'
var an_acronyms = {
a: true,
e: true,
f: true,
h: true,
i: true,
l: true,
m: true,
n: true,
o: true,
r: true,
s: true,
x: true
}; //'a' regexes
var a_regexs = [/^onc?e/i, //'wu' sound of 'o'
/^u[bcfhjkqrstn][aeiou]/i, // 'yu' sound for hard 'u'
/^eul/i];
var makeArticle = function makeArticle(doc) {
//no 'the john smith', but 'a london hotel'
if (doc.has('#Person') || doc.has('#Place')) {
return '';
} //no a/an if it's plural
if (doc.has('#Plural')) {
return 'the';
}
var str = doc.text('normal').trim(); //explicit irregular forms
if (irregulars$5.hasOwnProperty(str)) {
return irregulars$5[str];
} //spelled-out acronyms
var firstLetter = str.substr(0, 1);
if (doc.has('^@isAcronym') && an_acronyms.hasOwnProperty(firstLetter)) {
return 'an';
} //'a' regexes
for (var i = 0; i < a_regexs.length; i++) {
if (a_regexs[i].test(str)) {
return 'a';
}
} //basic vowel-startings
if (/^[aeiou]/i.test(str)) {
return 'an';
}
return 'a';
};
var getArticle = makeArticle;
//similar to plural/singularize rules, but not the same
var isPlural$1 = [/(antenn|formul|nebul|vertebr|vit)ae$/i, /(octop|vir|radi|nucle|fung|cact|stimul)i$/i, /men$/i, /.tia$/i, /(m|l)ice$/i]; //similar to plural/singularize rules, but not the same
var isSingular$1 = [/(ax|test)is$/i, /(octop|vir|radi|nucle|fung|cact|stimul)us$/i, /(octop|vir)i$/i, /(rl)f$/i, /(alias|status)$/i, /(bu)s$/i, /(al|ad|at|er|et|ed|ad)o$/i, /(ti)um$/i, /(ti)a$/i, /sis$/i, /(?:(^f)fe|(lr)f)$/i, /hive$/i, /(^aeiouy|qu)y$/i, /(x|ch|ss|sh|z)$/i, /(matr|vert|ind|cort)(ix|ex)$/i, /(m|l)ouse$/i, /(m|l)ice$/i, /(antenn|formul|nebul|vertebr|vit)a$/i, /.sis$/i, /^(?!talis|.*hu)(.*)man$/i];
var _rules$2 = {
isSingular: isSingular$1,
isPlural: isPlural$1
};
var endS = /s$/; // double-check this term, if it is not plural, or singular.
// (this is a partial copy of ./tagger/fallbacks/plural)
// fallback plural if it ends in an 's'.
var isPlural$2 = function isPlural(str) {
// isSingular suffix rules
if (_rules$2.isSingular.find(function (reg) {
return reg.test(str);
})) {
return false;
} // does it end in an s?
if (endS.test(str) === true) {
return true;
} // is it a plural like 'fungi'?
if (_rules$2.isPlural.find(function (reg) {
return reg.test(str);
})) {
return true;
}
return null;
};
var isPlural_1$1 = isPlural$2;
var exceptions = {
he: 'his',
she: 'hers',
they: 'theirs',
we: 'ours',
i: 'mine',
you: 'yours',
her: 'hers',
their: 'theirs',
our: 'ours',
my: 'mine',
your: 'yours'
}; // turn "David" to "David's"
var toPossessive = function toPossessive(doc) {
var str = doc.text('text').trim(); // exceptions
if (exceptions.hasOwnProperty(str)) {
doc.replaceWith(exceptions[str], true);
doc.tag('Possessive', 'toPossessive');
return;
} // flanders'
if (/s$/.test(str)) {
str += "'";
doc.replaceWith(str, true);
doc.tag('Possessive', 'toPossessive');
return;
} //normal form:
str += "'s";
doc.replaceWith(str, true);
doc.tag('Possessive', 'toPossessive');
return;
};
var toPossessive_1 = toPossessive;
// .nouns() supports some noun-phrase-ish groupings
// pull these apart, if necessary
var parse$1 = function parse(doc) {
var res = {
main: doc
}; //support 'mayor of chicago' as one noun-phrase
if (doc.has('#Noun (of|by|for) .')) {
var m = doc.splitAfter('[#Noun+]', 0);
res.main = m.eq(0);
res.post = m.eq(1);
}
return res;
};
var parse_1 = parse$1;
var methods$6 = {
/** overload the original json with noun information */
json: function json(options) {
var n = null;
if (typeof options === 'number') {
n = options;
options = null;
}
options = options || {
text: true,
normal: true,
trim: true,
terms: true
};
var res = [];
this.forEach(function (doc) {
var json = doc.json(options)[0];
json.article = getArticle(doc);
res.push(json);
});
if (n !== null) {
return res[n];
}
return res;
},
/** get all adjectives describing this noun*/
adjectives: function adjectives() {
var list = this.lookAhead('^(that|who|which)? (was|is|will)? be? #Adverb? #Adjective+');
list = list.concat(this.lookBehind('#Adjective+ #Adverb?$'));
list = list.match('#Adjective');
return list.sort('index');
},
isPlural: function isPlural() {
return this["if"]('#Plural'); //assume tagger has run?
},
hasPlural: function hasPlural() {
return this.filter(function (d) {
return hasPlural_1(d);
});
},
toPlural: function toPlural(agree) {
var _this = this;
var toPlural = this.world.transforms.toPlural;
this.forEach(function (doc) {
if (doc.has('#Plural') || hasPlural_1(doc) === false) {
return;
} // double-check it isn't an un-tagged plural
var main = parse_1(doc).main;
var str = main.text('reduced');
if (!main.has('#Singular') && isPlural_1$1(str) === true) {
return;
}
str = toPlural(str, _this.world);
main.replace(str).tag('#Plural'); // 'an apple' -> 'apples'
if (agree) {
var an = main.lookBefore('(an|a) #Adjective?$').not('#Adjective');
if (an.found === true) {
an.remove();
}
}
});
return this;
},
toSingular: function toSingular(agree) {
var _this2 = this;
var toSingular = this.world.transforms.toSingular;
this.forEach(function (doc) {
if (doc.has('^#Singular+$') || hasPlural_1(doc) === false) {
return;
} // double-check it isn't an un-tagged plural
var main = parse_1(doc).main;
var str = main.text('reduced');
if (!main.has('#Plural') && isPlural_1$1(str) !== true) {
return;
}
str = toSingular(str, _this2.world);
main.replace(str).tag('#Singular'); // add an article
if (agree) {
// 'apples' -> 'an apple'
var start = doc;
var adj = doc.lookBefore('#Adjective');
if (adj.found) {
start = adj;
}
var article = getArticle(start);
start.insertBefore(article);
}
});
return this;
},
toPossessive: function toPossessive() {
this.forEach(function (d) {
toPossessive_1(d);
});
return this;
}
};
var methods_1 = methods$6;
var addMethod$5 = function addMethod(Doc) {
/** */
var Nouns = /*#__PURE__*/function (_Doc) {
_inherits(Nouns, _Doc);
var _super = _createSuper(Nouns);
function Nouns() {
_classCallCheck(this, Nouns);
return _super.apply(this, arguments);
}
return Nouns;
}(Doc); // add-in our methods
Object.assign(Nouns.prototype, methods_1);
Doc.prototype.nouns = function (n) {
// don't split 'paris, france'
var keep = this.match('(#City && @hasComma) (#Region|#Country)'); // but split the other commas
var m = this.not(keep).splitAfter('@hasComma'); // combine them back together
m = m.concat(keep);
m = m.match('#Noun+ (of|by)? the? #Noun+?'); //nouns that we don't want in these results, for weird reasons
m = m.not('#Pronoun');
m = m.not('(there|these)');
m = m.not('(#Month|#WeekDay)'); //allow Durations, Holidays
// //allow possessives like "spencer's", but not generic ones like,
m = m.not('(my|our|your|their|her|his)');
m = m.not('(of|for|by|the)$');
if (typeof n === 'number') {
m = m.get(n);
}
return new Nouns(m.list, this, this.world);
};
return Doc;
};
var Nouns = addMethod$5;
var open = /\(/;
var close = /\)/;
var addMethod$6 = function addMethod(Doc) {
/** anything between (these things) */
var Parentheses = /*#__PURE__*/function (_Doc) {
_inherits(Parentheses, _Doc);
var _super = _createSuper(Parentheses);
function Parentheses() {
_classCallCheck(this, Parentheses);
return _super.apply(this, arguments);
}
_createClass(Parentheses, [{
key: "unwrap",
/** remove the parentheses characters */
value: function unwrap() {
this.list.forEach(function (p) {
var first = p.terms(0);
first.pre = first.pre.replace(open, '');
var last = p.lastTerm();
last.post = last.post.replace(close, '');
});
return this;
}
}]);
return Parentheses;
}(Doc);
Doc.prototype.parentheses = function (n) {
var list = [];
this.list.forEach(function (p) {
var terms = p.terms(); //look for opening brackets
for (var i = 0; i < terms.length; i += 1) {
var t = terms[i];
if (open.test(t.pre)) {
//look for the closing bracket..
for (var o = i; o < terms.length; o += 1) {
if (close.test(terms[o].post)) {
var len = o - i + 1;
list.push(p.buildFrom(t.id, len));
i = o;
break;
}
}
}
}
}); //support nth result
if (typeof n === 'number') {
if (list[n]) {
list = [list[n]];
} else {
list = [];
}
return new Parentheses(list, this, this.world);
}
return new Parentheses(list, this, this.world);
};
return Doc;
};
var Parentheses = addMethod$6;
var addMethod$7 = function addMethod(Doc) {
/** */
var Possessives = /*#__PURE__*/function (_Doc) {
_inherits(Possessives, _Doc);
var _super = _createSuper(Possessives);
function Possessives(list, from, world) {
var _this;
_classCallCheck(this, Possessives);
_this = _super.call(this, list, from, world);
_this.contracted = null;
return _this;
}
/** turn didn't into 'did not' */
_createClass(Possessives, [{
key: "strip",
value: function strip() {
this.list.forEach(function (p) {
var terms = p.terms();
terms.forEach(function (t) {
var str = t.text.replace(/'s$/, '');
t.set(str || t.text);
});
});
return this;
}
}]);
return Possessives;
}(Doc); //find contractable, expanded-contractions
// const findExpanded = r => {
// let remain = r.not('#Contraction')
// let m = remain.match('(#Noun|#QuestionWord) (#Copula|did|do|have|had|could|would|will)')
// m.concat(remain.match('(they|we|you|i) have'))
// m.concat(remain.match('i am'))
// m.concat(remain.match('(#Copula|#Modal|do|does|have|has|can|will) not'))
// return m
// }
Doc.prototype.possessives = function (n) {
//find currently-contracted
var found = this.match('#Noun+? #Possessive'); //todo: split consecutive contractions
if (typeof n === 'number') {
found = found.get(n);
}
return new Possessives(found.list, this, this.world);
};
return Doc;
};
var Possessives = addMethod$7;
var pairs = {
"\"": "\"",
// 'StraightDoubleQuotes'
"\uFF02": "\uFF02",
// 'StraightDoubleQuotesWide'
"'": "'",
// 'StraightSingleQuotes'
"\u201C": "\u201D",
// 'CommaDoubleQuotes'
"\u2018": "\u2019",
// 'CommaSingleQuotes'
"\u201F": "\u201D",
// 'CurlyDoubleQuotesReversed'
"\u201B": "\u2019",
// 'CurlySingleQuotesReversed'
"\u201E": "\u201D",
// 'LowCurlyDoubleQuotes'
"\u2E42": "\u201D",
// 'LowCurlyDoubleQuotesReversed'
"\u201A": "\u2019",
// 'LowCurlySingleQuotes'
"\xAB": "\xBB",
// 'AngleDoubleQuotes'
"\u2039": "\u203A",
// 'AngleSingleQuotes'
// Prime 'non quotation'
"\u2035": "\u2032",
// 'PrimeSingleQuotes'
"\u2036": "\u2033",
// 'PrimeDoubleQuotes'
"\u2037": "\u2034",
// 'PrimeTripleQuotes'
// Prime 'quotation' variation
"\u301D": "\u301E",
// 'PrimeDoubleQuotes'
"`": "\xB4",
// 'PrimeSingleQuotes'
"\u301F": "\u301E" // 'LowPrimeDoubleQuotesReversed'
};
var hasOpen = RegExp('(' + Object.keys(pairs).join('|') + ')');
var addMethod$8 = function addMethod(Doc) {
/** "these things" */
var Quotations = /*#__PURE__*/function (_Doc) {
_inherits(Quotations, _Doc);
var _super = _createSuper(Quotations);
function Quotations() {
_classCallCheck(this, Quotations);
return _super.apply(this, arguments);
}
_createClass(Quotations, [{
key: "unwrap",
/** remove the quote characters */
value: function unwrap() {
return this;
}
}]);
return Quotations;
}(Doc);
Doc.prototype.quotations = function (n) {
var list = [];
this.list.forEach(function (p) {
var terms = p.terms(); //look for opening quotes
for (var i = 0; i < terms.length; i += 1) {
var t = terms[i];
if (hasOpen.test(t.pre)) {
var _char = (t.pre.match(hasOpen) || [])[0];
var want = pairs[_char]; // if (!want) {
// console.warn('missing quote char ' + char)
// }
//look for the closing bracket..
for (var o = i; o < terms.length; o += 1) {
if (terms[o].post.indexOf(want) !== -1) {
var len = o - i + 1;
list.push(p.buildFrom(t.id, len));
i = o;
break;
}
}
}
}
}); //support nth result
if (typeof n === 'number') {
if (list[n]) {
list = [list[n]];
} else {
list = [];
}
return new Quotations(list, this, this.world);
}
return new Quotations(list, this, this.world);
}; // alias
Doc.prototype.quotes = Doc.prototype.quotations;
return Doc;
};
var Quotations = addMethod$8;
// walked => walk - turn a verb into it's root form
var toInfinitive$1 = function toInfinitive(parsed, world) {
var verb = parsed.verb; // console.log(parsed)
// verb.debug()
//1. if it's already infinitive
var str = verb.text('normal');
if (verb.has('#Infinitive')) {
return str;
} // 2. world transform does the heavy-lifting
var tense = null;
if (verb.has('#PastTense')) {
tense = 'PastTense';
} else if (verb.has('#Gerund')) {
tense = 'Gerund';
} else if (verb.has('#PresentTense')) {
tense = 'PresentTense';
} else if (verb.has('#Participle')) {
tense = 'Participle';
} else if (verb.has('#Actor')) {
tense = 'Actor';
}
return world.transforms.toInfinitive(str, world, tense);
};
var toInfinitive_1$1 = toInfinitive$1;
// spencer walks -> singular
// we walk -> plural
// the most-recent noun-phrase, before this verb.
var findNoun = function findNoun(vb) {
var noun = vb.lookBehind('#Noun+').last();
return noun;
}; //sometimes you can tell if a verb is plural/singular, just by the verb
// i am / we were
// othertimes you need its subject 'we walk' vs 'i walk'
var isPlural$3 = function isPlural(parsed) {
var vb = parsed.verb;
if (vb.has('(are|were|does)') || parsed.auxiliary.has('(are|were|does)')) {
return true;
}
if (vb.has('(is|am|do|was)') || parsed.auxiliary.has('(is|am|do|was)')) {
return false;
} //consider its prior noun
var noun = findNoun(vb);
if (noun.has('(we|they|you)')) {
return true;
}
if (noun.has('#Plural')) {
return true;
}
if (noun.has('#Singular')) {
return false;
}
return null;
};
var isPlural_1$2 = isPlural$3;
// #Copula : is -> 'is not'
// #PastTense : walked -> did not walk
// #PresentTense : walks -> does not walk
// #Gerund : walking: -> not walking
// #Infinitive : walk -> do not walk
var toNegative = function toNegative(parsed, world) {
var vb = parsed.verb; // if it's already negative...
if (parsed.negative.found) {
return;
} // would walk -> would not walk
if (parsed.auxiliary.found) {
parsed.auxiliary.eq(0).append('not'); // 'would not have' ➔ 'would not have'
if (parsed.auxiliary.has('#Modal have not')) {
parsed.auxiliary.replace('have not', 'not have');
}
return;
} // is walking -> is not walking
if (vb.has('(#Copula|will|has|had|do)')) {
vb.append('not');
return;
} // walked -> did not walk
if (vb.has('#PastTense')) {
var inf = toInfinitive_1$1(parsed, world);
vb.replaceWith(inf, true);
vb.prepend('did not');
return;
} // walks -> does not walk
if (vb.has('#PresentTense')) {
var _inf = toInfinitive_1$1(parsed, world);
vb.replaceWith(_inf, true);
if (isPlural_1$2(parsed)) {
vb.prepend('do not');
} else {
vb.prepend('does not');
}
return;
} //walking -> not walking
if (vb.has('#Gerund')) {
var _inf2 = toInfinitive_1$1(parsed, world);
vb.replaceWith(_inf2, true);
vb.prepend('not');
return;
} //fallback 1: walk -> does not walk
if (isPlural_1$2(parsed)) {
vb.prepend('does not');
return;
} //fallback 2: walk -> do not walk
vb.prepend('do not');
return;
};
var toNegative_1 = toNegative;
// turn 'would not really walk up' into parts
var parseVerb = function parseVerb(vb) {
var parsed = {
adverb: vb.match('#Adverb+'),
// 'really'
negative: vb.match('#Negative'),
// 'not'
auxiliary: vb.match('#Auxiliary+').not('(#Negative|#Adverb)'),
// 'will' of 'will go'
particle: vb.match('#Particle'),
// 'up' of 'pull up'
verb: vb.match('#Verb+').not('(#Adverb|#Negative|#Auxiliary|#Particle)')
}; // fallback, if no verb found
if (!parsed.verb.found) {
// blank-everything
Object.keys(parsed).forEach(function (k) {
parsed[k] = parsed[k].not('.');
}); // it's all the verb
parsed.verb = vb;
return parsed;
} //
if (parsed.adverb && parsed.adverb.found) {
var match = parsed.adverb.text('reduced') + '$';
if (vb.has(match)) {
parsed.adverbAfter = true;
}
}
return parsed;
};
var parse$2 = parseVerb;
/** too many special cases for is/was/will be*/
var toBe = function toBe(parsed) {
var isI = false;
var plural = isPlural_1$2(parsed);
var isNegative = parsed.negative.found; //account for 'i is' -> 'i am' irregular
// if (vb.parent && vb.parent.has('i #Adverb? #Copula')) {
// isI = true;
// }
// 'i look', not 'i looks'
if (parsed.verb.lookBehind('(i|we) (#Adverb|#Verb)?$').found) {
isI = true;
}
var obj = {
PastTense: 'was',
PresentTense: 'is',
FutureTense: 'will be',
Infinitive: 'is',
Gerund: 'being',
Actor: '',
PerfectTense: 'been',
Pluperfect: 'been'
}; //"i is" -> "i am"
if (isI === true) {
obj.PresentTense = 'am';
obj.Infinitive = 'am';
}
if (plural) {
obj.PastTense = 'were';
obj.PresentTense = 'are';
obj.Infinitive = 'are';
}
if (isNegative) {
obj.PastTense += ' not';
obj.PresentTense += ' not';
obj.FutureTense = 'will not be';
obj.Infinitive += ' not';
obj.PerfectTense = 'not ' + obj.PerfectTense;
obj.Pluperfect = 'not ' + obj.Pluperfect;
obj.Gerund = 'not ' + obj.Gerund;
}
return obj;
};
var toBe_1 = toBe;
// 'may/could/should' -> 'may/could/should have'
var doModal = function doModal(parsed) {
var str = parsed.verb.text();
var res = {
PastTense: str + ' have',
PresentTense: str,
FutureTense: str,
Infinitive: str // Gerund: ,
// Actor: '',
// PerfectTense: '',
// Pluperfect: '',
};
return res;
};
var doModal_1 = doModal;
var conjugate$2 = function conjugate(parsed, world) {
var verb = parsed.verb; //special handling of 'is', 'will be', etc.
if (verb.has('#Copula') || verb.out('normal') === 'be' && parsed.auxiliary.has('will')) {
return toBe_1(parsed);
} // special handling of 'he could.'
if (verb.has('#Modal')) {
return doModal_1(parsed);
}
var hasHyphen = parsed.verb.termList(0).hasHyphen();
var infinitive = toInfinitive_1$1(parsed, world);
if (!infinitive) {
return {};
}
var forms = world.transforms.conjugate(infinitive, world);
forms.Infinitive = infinitive; // add particle to phrasal verbs ('fall over')
if (parsed.particle.found) {
var particle = parsed.particle.text();
var space = hasHyphen === true ? '-' : ' ';
Object.keys(forms).forEach(function (k) {
return forms[k] += space + particle;
});
} //put the adverb at the end?
// if (parsed.adverb.found) {
// let adverb = parsed.adverb.text()
// let space = hasHyphen === true ? '-' : ' '
// if (parsed.adverbAfter === true) {
// Object.keys(forms).forEach(k => (forms[k] += space + adverb))
// } else {
// Object.keys(forms).forEach(k => (forms[k] = adverb + space + forms[k]))
// }
// }
//apply negative
var isNegative = parsed.negative.found;
if (isNegative) {
forms.PastTense = 'did not ' + forms.Infinitive;
forms.PresentTense = 'does not ' + forms.Infinitive;
forms.Gerund = 'not ' + forms.Gerund;
} //future Tense is pretty straightforward
if (!forms.FutureTense) {
if (isNegative) {
forms.FutureTense = 'will not ' + forms.Infinitive;
} else {
forms.FutureTense = 'will ' + forms.Infinitive;
}
}
if (isNegative) {
forms.Infinitive = 'not ' + forms.Infinitive;
}
return forms;
};
var conjugate_1$1 = conjugate$2;
// if something is 'modal-ish' we are forced to use past-participle
// ('i could drove' is wrong)
var useParticiple = function useParticiple(parsed) {
if (parsed.auxiliary.has('(could|should|would|may|can|must)')) {
return true;
}
if (parsed.auxiliary.has('am .+? being')) {
return true;
}
if (parsed.auxiliary.has('had .+? been')) {
return true;
}
return false;
}; // conjugate 'drive' ➔ 'have driven'
var toParticiple = function toParticiple(parsed, world) {
//is it already a participle?
if (parsed.auxiliary.has('(have|had)') && parsed.verb.has('#Participle')) {
return;
} // try to swap the main verb to its participle form
var obj = conjugate_1$1(parsed, world);
var str = obj.Participle || obj.PastTense;
if (str) {
parsed.verb.replaceWith(str, false);
} // 'am being driven' ➔ 'have been driven'
if (parsed.auxiliary.has('am .+? being')) {
parsed.auxiliary.remove('am');
parsed.auxiliary.replace('being', 'have been');
} // add a 'have'
if (!parsed.auxiliary.has('have')) {
parsed.auxiliary.append('have');
} // tag it as a participle
parsed.verb.tag('Participle', 'toParticiple'); // turn 'i can swim' to -> 'i could swim'
parsed.auxiliary.replace('can', 'could'); //'must be' ➔ 'must have been'
parsed.auxiliary.replace('be have', 'have been'); //'not have' ➔ 'have not'
parsed.auxiliary.replace('not have', 'have not'); // ensure all new words are tagged right
parsed.auxiliary.tag('Auxiliary');
};
var participle = {
useParticiple: useParticiple,
toParticiple: toParticiple
};
var _toParticiple = participle.toParticiple,
useParticiple$1 = participle.useParticiple; // remove any tense-information in auxiliary verbs
var makeNeutral = function makeNeutral(parsed) {
//remove tense-info from auxiliaries
parsed.auxiliary.remove('(will|are|am|being)');
parsed.auxiliary.remove('(did|does)');
parsed.auxiliary.remove('(had|has|have)'); //our conjugation includes the 'not' and the phrasal-verb particle
parsed.particle.remove();
parsed.negative.remove();
return parsed;
};
var methods$7 = {
/** overload the original json with verb information */
json: function json(options) {
var _this = this;
var n = null;
if (typeof options === 'number') {
n = options;
options = null;
}
options = options || {
text: true,
normal: true,
trim: true,
terms: true
};
var res = [];
this.forEach(function (p) {
var json = p.json(options)[0];
var parsed = parse$2(p);
json.parts = {};
Object.keys(parsed).forEach(function (k) {
if (parsed[k] && parsed[k].isA === 'Doc') {
json.parts[k] = parsed[k].text('normal');
} else {
json.parts[k] = parsed[k];
}
});
json.isNegative = p.has('#Negative');
json.conjugations = conjugate_1$1(parsed, _this.world);
res.push(json);
});
if (n !== null) {
return res[n];
}
return res;
},
/** grab the adverbs describing these verbs */
adverbs: function adverbs() {
var list = []; // look at internal adverbs
this.forEach(function (vb) {
var advb = parse$2(vb).adverb;
if (advb.found) {
list = list.concat(advb.list);
}
}); // look for leading adverbs
var m = this.lookBehind('#Adverb+$');
if (m.found) {
list = m.list.concat(list);
} // look for trailing adverbs
m = this.lookAhead('^#Adverb+');
if (m.found) {
list = list.concat(m.list);
}
return this.buildFrom(list);
},
/// Verb Inflection
/**return verbs like 'we walk' and not 'spencer walks' */
isPlural: function isPlural() {
var _this2 = this;
var list = [];
this.forEach(function (vb) {
var parsed = parse$2(vb);
if (isPlural_1$2(parsed, _this2.world) === true) {
list.push(vb.list[0]);
}
});
return this.buildFrom(list);
},
/** return verbs like 'spencer walks' and not 'we walk' */
isSingular: function isSingular() {
var _this3 = this;
var list = [];
this.forEach(function (vb) {
var parsed = parse$2(vb);
if (isPlural_1$2(parsed, _this3.world) === false) {
list.push(vb.list[0]);
}
});
return this.buildFrom(list);
},
/// Conjugation
/** return all forms of this verb */
conjugate: function conjugate() {
var _this4 = this;
var result = [];
this.forEach(function (vb) {
var parsed = parse$2(vb);
var forms = conjugate_1$1(parsed, _this4.world);
result.push(forms);
});
return result;
},
/** walk ➔ walked*/
toPastTense: function toPastTense() {
var _this5 = this;
this.forEach(function (vb) {
var parsed = parse$2(vb); // should we support 'would swim' ➔ 'would have swam'
if (useParticiple$1(parsed)) {
_toParticiple(parsed, _this5.world);
return;
}
var str = conjugate_1$1(parsed, _this5.world).PastTense;
if (str) {
parsed = makeNeutral(parsed);
parsed.verb.replaceWith(str, false); // vb.tag('PastTense')
}
});
return this;
},
/** walk ➔ walks */
toPresentTense: function toPresentTense() {
var _this6 = this;
this.forEach(function (vb) {
var parsed = parse$2(vb);
var obj = conjugate_1$1(parsed, _this6.world);
var str = obj.PresentTense; // 'i look', not 'i looks'
if (vb.lookBehind('(i|we) (#Adverb|#Verb)?$').found) {
str = obj.Infinitive;
}
if (str) {
//awkward support for present-participle form
// -- should we support 'have been swimming' ➔ 'am swimming'
if (parsed.auxiliary.has('(have|had) been')) {
parsed.auxiliary.replace('(have|had) been', 'am being');
if (obj.Particle) {
str = obj.Particle || obj.PastTense;
}
return;
}
parsed.verb.replaceWith(str, false);
parsed.verb.tag('PresentTense');
parsed = makeNeutral(parsed); // avoid 'he would walks'
parsed.auxiliary.remove('#Modal');
}
});
return this;
},
/** walk ➔ will walk*/
toFutureTense: function toFutureTense() {
var _this7 = this;
this.forEach(function (vb) {
var parsed = parse$2(vb); // 'i should drive' is already future-enough
if (useParticiple$1(parsed)) {
return;
}
var str = conjugate_1$1(parsed, _this7.world).FutureTense;
if (str) {
parsed = makeNeutral(parsed); // avoid 'he would will go'
parsed.auxiliary.remove('#Modal');
parsed.verb.replaceWith(str, false);
parsed.verb.tag('FutureTense');
}
});
return this;
},
/** walks ➔ walk */
toInfinitive: function toInfinitive() {
var _this8 = this;
this.forEach(function (vb) {
var parsed = parse$2(vb);
var str = conjugate_1$1(parsed, _this8.world).Infinitive;
if (str) {
vb.replaceWith(str, false);
vb.tag('Infinitive');
}
});
return this;
},
/** walk ➔ walking */
toGerund: function toGerund() {
var _this9 = this;
this.forEach(function (vb) {
var parsed = parse$2(vb);
var str = conjugate_1$1(parsed, _this9.world).Gerund;
if (str) {
vb.replaceWith(str, false);
vb.tag('Gerund');
}
});
return this;
},
/** drive ➔ driven - naked past-participle if it exists, otherwise past-tense */
toParticiple: function toParticiple() {
var _this10 = this;
this.forEach(function (vb) {
var parsed = parse$2(vb);
var noAux = !parsed.auxiliary.found;
_toParticiple(parsed, _this10.world); // dirty trick to ensure our new auxiliary is found
if (noAux) {
parsed.verb.prepend(parsed.auxiliary.text());
parsed.auxiliary.remove();
}
});
return this;
},
/// Negation
/** return only verbs with 'not'*/
isNegative: function isNegative() {
return this["if"]('#Negative');
},
/** return only verbs without 'not'*/
isPositive: function isPositive() {
return this.ifNo('#Negative');
},
/** add a 'not' to these verbs */
toNegative: function toNegative() {
var _this11 = this;
this.list.forEach(function (p) {
var doc = _this11.buildFrom([p]);
var parsed = parse$2(doc);
toNegative_1(parsed, doc.world);
});
return this;
},
/** remove 'not' from these verbs */
toPositive: function toPositive() {
var m = this.match('do not #Verb');
if (m.found) {
m.remove('do not');
}
return this.remove('#Negative');
}
};
var addMethod$9 = function addMethod(Doc) {
/** */
var Verbs = /*#__PURE__*/function (_Doc) {
_inherits(Verbs, _Doc);
var _super = _createSuper(Verbs);
function Verbs() {
_classCallCheck(this, Verbs);
return _super.apply(this, arguments);
}
return Verbs;
}(Doc); // add-in our methods
Object.assign(Verbs.prototype, methods$7); // aliases
Verbs.prototype.negate = Verbs.prototype.toNegative;
Doc.prototype.verbs = function (n) {
var match = this.match('(#Adverb|#Auxiliary|#Verb|#Negative|#Particle)+'); // try to ignore leading and trailing adverbs
match = match.not('^#Adverb+');
match = match.not('#Adverb+$'); // handle commas:
// don't split 'really, really'
var keep = match.match('(#Adverb && @hasComma) #Adverb'); // // but split the other commas
var m = match.not(keep).splitAfter('@hasComma'); // // combine them back together
m = m.concat(keep);
m.sort('index'); //handle slashes?
//ensure there's actually a verb
m = m["if"]('#Verb'); // the reason he will is ...
if (m.has('(is|was)$')) {
m = m.splitBefore('(is|was)$');
} //grab (n)th result
if (typeof n === 'number') {
m = m.get(n);
}
var vb = new Verbs(m.list, this, this.world);
return vb;
};
return Doc;
};
var Verbs = addMethod$9;
var addMethod$a = function addMethod(Doc) {
/** */
var People = /*#__PURE__*/function (_Doc) {
_inherits(People, _Doc);
var _super = _createSuper(People);
function People() {
_classCallCheck(this, People);
return _super.apply(this, arguments);
}
return People;
}(Doc);
Doc.prototype.people = function (n) {
var match = this.splitAfter('@hasComma');
match = match.match('#Person+'); //grab (n)th result
if (typeof n === 'number') {
match = match.get(n);
}
return new People(match.list, this, this.world);
};
return Doc;
};
var People = addMethod$a;
var subclass = [Abbreviations, Acronyms, Clauses, Contractions, Lists, Nouns, Parentheses, Possessives, Quotations, Verbs, People];
var extend = function extend(Doc) {
// add basic methods
Object.keys(_simple).forEach(function (k) {
return Doc.prototype[k] = _simple[k];
}); // add subclassed methods
subclass.forEach(function (addFn) {
return addFn(Doc);
});
return Doc;
};
var Subset = extend;
var methods$8 = {
misc: methods$4,
selections: _simple
};
/** a parsed text object */
var Doc = /*#__PURE__*/function () {
function Doc(list, from, world) {
var _this = this;
_classCallCheck(this, Doc);
this.list = list; //quiet these properties in console.logs
Object.defineProperty(this, 'from', {
enumerable: false,
value: from,
writable: true
}); //borrow some missing data from parent
if (world === undefined && from !== undefined) {
world = from.world;
} //'world' getter
Object.defineProperty(this, 'world', {
enumerable: false,
value: world,
writable: true
}); //fast-scans for our data
Object.defineProperty(this, '_cache', {
enumerable: false,
writable: true,
value: {}
}); //'found' getter
Object.defineProperty(this, 'found', {
get: function get() {
return _this.list.length > 0;
}
}); //'length' getter
Object.defineProperty(this, 'length', {
get: function get() {
return _this.list.length;
}
}); // this is way easier than .constructor.name...
Object.defineProperty(this, 'isA', {
get: function get() {
return 'Doc';
}
});
}
/** run part-of-speech tagger on all results*/
_createClass(Doc, [{
key: "tagger",
value: function tagger() {
return _02Tagger(this);
}
/** pool is stored on phrase objects */
}, {
key: "pool",
value: function pool() {
if (this.list.length > 0) {
return this.list[0].pool;
}
return this.all().list[0].pool;
}
}]);
return Doc;
}();
/** create a new Document object */
Doc.prototype.buildFrom = function (list) {
list = list.map(function (p) {
return p.clone(true);
}); // new this.constructor()
var doc = new Doc(list, this, this.world);
return doc;
};
/** create a new Document from plaintext. */
Doc.prototype.fromText = function (str) {
var list = _01Tokenizer(str, this.world, this.pool());
return this.buildFrom(list);
};
Object.assign(Doc.prototype, methods$8.misc);
Object.assign(Doc.prototype, methods$8.selections); //add sub-classes
Subset(Doc); //aliases
var aliases$1 = {
untag: 'unTag',
and: 'match',
notIf: 'ifNo',
only: 'if',
onlyIf: 'if'
};
Object.keys(aliases$1).forEach(function (k) {
return Doc.prototype[k] = Doc.prototype[aliases$1[k]];
});
var Doc_1 = Doc;
var smallTagger = function smallTagger(doc) {
var terms = doc.termList();
_01Lexicon(terms, doc.world);
return doc;
};
var tiny = smallTagger;
function instance(worldInstance) {
//blast-out our word-lists, just once
var world = worldInstance;
/** parse and tag text into a compromise object */
var nlp = function nlp() {
var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var lexicon = arguments.length > 1 ? arguments[1] : undefined;
if (lexicon) {
world.addWords(lexicon);
}
var list = _01Tokenizer(text, world);
var doc = new Doc_1(list, null, world);
doc.tagger();
return doc;
};
nlp.similar = similar_text;
/** parse text into a compromise object, without running POS-tagging */
nlp.tokenize = function () {
var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var lexicon = arguments.length > 1 ? arguments[1] : undefined;
var w = world;
if (lexicon) {
w = w.clone();
w.words = {};
w.addWords(lexicon);
}
var list = _01Tokenizer(text, w);
var doc = new Doc_1(list, null, w);
if (lexicon) {
tiny(doc);
}
return doc;
};
/** mix in a compromise-plugin */
nlp.extend = function (fn) {
fn(Doc_1, world, this, Phrase_1, Term_1, Pool_1);
return this;
};
/** create a compromise Doc object from .json() results */
nlp.fromJSON = function (json) {
var list = fromJSON_1(json, world);
return new Doc_1(list, null, world);
};
/** make a deep-copy of the library state */
nlp.clone = function () {
return instance(world.clone());
};
/** log our decision-making for debugging */
nlp.verbose = function () {
var bool = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
world.verbose(bool);
return this;
};
/** grab currently-used World object */
nlp.world = function () {
return world;
};
/** pre-parse any match statements */
nlp.parseMatch = function (str) {
return syntax_1(str);
};
/** current version of the library */
nlp.version = _version; // alias
nlp["import"] = nlp.load;
return nlp;
}
var src = instance(new World_1());
return src;
})));
};
BundleModuleCode['nlp/compromise-adjectives']=function (module,exports){
/* compromise-adjectives 0.0.6 MIT */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.compromiseAdjectives = factory());
}(this, (function () { 'use strict';
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
//turn 'quick' into 'quickly'
var not_matches = [/airs$/, /ll$/, /ee.$/, /ile$/, /y$/];
var irregulars = {
bad: 'badly',
good: 'well',
icy: 'icily',
idle: 'idly',
male: 'manly',
"public": 'publicly',
simple: 'simply',
single: 'singly',
special: 'especially',
straight: 'straight',
vague: 'vaguely',
whole: 'wholly'
};
var dontChange = ['best', 'early', 'hard', 'fast', 'wrong', 'well', 'late', 'latter', 'little', 'long', 'low'].reduce(function (h, c) {
h[c] = true;
return h;
}, {});
var transforms = [{
reg: /al$/i,
repl: 'ally'
}, {
reg: /ly$/i,
repl: 'ly'
}, {
reg: /(.{3})y$/i,
repl: '$1ily'
}, {
reg: /que$/i,
repl: 'quely'
}, {
reg: /ue$/i,
repl: 'uly'
}, {
reg: /ic$/i,
repl: 'ically'
}, {
reg: /ble$/i,
repl: 'bly'
}, {
reg: /l$/i,
repl: 'ly'
}];
var adj_to_adv = function adj_to_adv(str) {
if (irregulars.hasOwnProperty(str) === true) {
return irregulars[str];
}
if (dontChange.hasOwnProperty(str) === true) {
return str;
}
for (var i = 0; i < not_matches.length; i++) {
if (not_matches[i].test(str) === true) {
return null;
}
}
for (var _i = 0; _i < transforms.length; _i++) {
if (transforms[_i].reg.test(str) === true) {
return str.replace(transforms[_i].reg, transforms[_i].repl);
}
}
return str + 'ly';
};
var toAdverb = adj_to_adv;
//convert 'cute' to 'cuteness'
var irregulars$1 = {
clean: 'cleanliness',
naivety: 'naivety',
hurt: 'hurt'
};
var transforms$1 = [{
reg: /y$/,
repl: 'iness'
}, {
reg: /le$/,
repl: 'ility'
}, {
reg: /ial$/,
repl: 'y'
}, {
reg: /al$/,
repl: 'ality'
}, {
reg: /ting$/,
repl: 'ting'
}, {
reg: /ring$/,
repl: 'ring'
}, {
reg: /bing$/,
repl: 'bingness'
}, {
reg: /sing$/,
repl: 'se'
}, {
reg: /ing$/,
repl: 'ment'
}, {
reg: /ess$/,
repl: 'essness'
}, {
reg: /ous$/,
repl: 'ousness'
}];
var to_noun = function to_noun(w) {
if (irregulars$1.hasOwnProperty(w)) {
return irregulars$1[w];
}
var lastChar = w.charAt(w.length - 1);
if (lastChar === 'w' || lastChar === 's') {
return null;
}
for (var i = 0; i < transforms$1.length; i++) {
if (transforms$1[i].reg.test(w) === true) {
return w.replace(transforms$1[i].reg, transforms$1[i].repl);
}
}
return w + 'ness';
};
var toNoun = to_noun;
//turn an adjective like 'soft' into a verb like 'soften'
//(don't do words like 'green' -> 'greenen')
//these are suffices that are usually too weird
var dontDo = ['c', 'e', 'g', 'l', 'n', 'r', 'w', 'y'].reduce(function (h, c) {
h[c] = true;
return h;
}, {});
var dontDoTwo = {
ed: true,
nt: true
};
var banList = {
random: true,
wild: true
};
var irregulars$2 = {
bored: 'bore',
red: 'redden',
sad: 'sadden',
fat: 'fatten',
small: 'shrink',
full: 'fill',
tired: 'tire'
};
var toVerb = function toVerb(str) {
if (irregulars$2.hasOwnProperty(str) === true) {
return irregulars$2[str];
} //don't bother with these:
if (str.length <= 3) {
return null;
}
if (banList.hasOwnProperty(str) === true) {
return null;
} //suffixes to avoid
if (dontDo.hasOwnProperty(str[str.length - 1])) {
return null;
}
var suffix = str.substr(str.length - 2);
if (dontDoTwo.hasOwnProperty(suffix) === true) {
return null;
}
if (/e$/.test(str) === true) {
return str + 'n';
}
return str + 'en';
};
var toVerb_1 = toVerb;
var addMethods = function addMethods(Doc) {
/** */
var Adjective = /*#__PURE__*/function (_Doc) {
_inherits(Adjective, _Doc);
var _super = _createSuper(Adjective);
function Adjective() {
_classCallCheck(this, Adjective);
return _super.apply(this, arguments);
}
_createClass(Adjective, [{
key: "json",
/** overload the original json with noun information */
value: function json(options) {
var n = null;
if (typeof options === 'number') {
n = options;
options = null;
}
var res = [];
this.forEach(function (doc) {
var json = doc.json(options)[0];
var str = doc.text('reduced');
json.toAdverb = toAdverb(str);
json.toNoun = toNoun(str);
json.toVerb = toVerb_1(str);
res.push(json);
});
if (n !== null) {
return res[n];
}
return res;
}
}, {
key: "conjugate",
value: function conjugate(n) {
var transform = this.world.transforms.adjectives;
var arr = [];
this.forEach(function (doc) {
var str = doc.text('reduced');
var obj = transform(str);
obj.Adverb = toAdverb(str);
obj.Noun = toNoun(str);
obj.Verb = toVerb_1(str);
arr.push(obj);
}); //support nth result
if (typeof n === 'number') {
return arr[n];
}
return arr;
}
}, {
key: "toSuperlative",
value: function toSuperlative() {
var transform = this.world.transforms.adjectives;
this.forEach(function (doc) {
var obj = transform(doc.text('reduced'));
doc.replaceWith(obj.Superlative, true);
});
return this;
}
}, {
key: "toComparative",
value: function toComparative() {
var transform = this.world.transforms.adjectives;
this.forEach(function (doc) {
var obj = transform(doc.text('reduced'));
doc.replaceWith(obj.Comparative, true);
});
return this;
}
}, {
key: "toAdverb",
value: function toAdverb$1() {
this.forEach(function (doc) {
var adverb = toAdverb(doc.text('reduced'));
doc.replaceWith(adverb, true);
});
return this;
}
}, {
key: "toVerb",
value: function toVerb() {
this.forEach(function (doc) {
var verb = toVerb_1(doc.text('reduced'));
doc.replaceWith(verb, true);
});
return this;
}
}, {
key: "toNoun",
value: function toNoun$1() {
this.forEach(function (doc) {
var noun = toNoun(doc.text('reduced'));
doc.replaceWith(noun, true);
});
return this;
}
}]);
return Adjective;
}(Doc);
/** grab all the adjectives */
Doc.prototype.adjectives = function (n) {
var m = this.match('#Adjective'); //grab (n)th result
if (typeof n === 'number') {
m = m.get(n);
}
return new Adjective(m.list, this, this.world);
};
};
var src = addMethods;
return src;
})));
//# sourceMappingURL=compromise-adjectives.js.map
};
BundleModuleCode['nlp/compromise-dates']=function (module,exports){
/* compromise-dates 1.3.0 MIT */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.compromiseDates = factory());
}(this, (function () { 'use strict';
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
//ambiguous 'may' and 'march'
var preps = '(in|by|before|during|on|until|after|of|within|all)'; //6
var thisNext = '(last|next|this|previous|current|upcoming|coming)'; //2
var sections = '(start|end|middle|starting|ending|midpoint|beginning)'; //2
var seasons = '(spring|summer|winter|fall|autumn)'; //ensure a year is approximately typical for common years
//please change in one thousand years
var tagYear = function tagYear(m, reason) {
if (m.found !== true) {
return;
}
m.forEach(function (p) {
var str = p.text('reduced');
var num = parseInt(str, 10);
if (num && num > 1000 && num < 3000) {
p.tag('Year', reason);
}
});
}; //same, but for less-confident values
var tagYearSafe = function tagYearSafe(m, reason) {
if (m.found !== true) {
return;
}
m.forEach(function (p) {
var str = p.text('reduced');
var num = parseInt(str, 10);
if (num && num > 1900 && num < 2030) {
p.tag('Year', reason);
}
});
};
var tagDates = function tagDates(doc) {
// in the evening
doc.match('in the (night|evening|morning|afternoon|day|daytime)').tag('Time', 'in-the-night'); // 8 pm
doc.match('(#Value|#Time) (am|pm)').tag('Time', 'value-ampm'); // 22-aug
// doc.match('/^[0-9]{2}-(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov)/').tag('Date', '20-jan')
// 2012-06
doc.match('/^[0-9]{4}-[0-9]{2}$/').tag('Date', '2012-06'); // misc weekday words
doc.match('(tue|thu)').tag('WeekDay', 'misc-weekday'); //months:
var month = doc["if"]('#Month');
if (month.found === true) {
//June 5-7th
month.match("#Month #Date+").tag('Date', 'correction-numberRange'); //5th of March
month.match('#Value of #Month').tag('Date', 'value-of-month'); //5 March
month.match('#Cardinal #Month').tag('Date', 'cardinal-month'); //march 5 to 7
month.match('#Month #Value to #Value').tag('Date', 'value-to-value'); //march the 12th
month.match('#Month the #Value').tag('Date', 'month-the-value');
} //months:
var val = doc["if"]('#Value');
if (val.found === true) {
//june 7
val.match('(#WeekDay|#Month) #Value').ifNo('#Money').tag('Date', 'date-value'); //7 june
val.match('#Value (#WeekDay|#Month)').ifNo('#Money').tag('Date', 'value-date'); //may twenty five
val.match('#TextValue #TextValue')["if"]('#Date').tag('#Date', 'textvalue-date'); //two thursdays back
val.match('#Value (#WeekDay|#Duration) back').tag('#Date', '3-back'); //eg 'year'
var duration = val["if"]('#Duration');
if (duration.found === true) {
//for 4 months
duration.match('for #Value #Duration').tag('Date', 'for-x-duration'); //two days before
duration.match('#Value #Duration #Conjunction').tag('Date', 'val-duration-conjunction'); //for four days
duration.match("".concat(preps, "? #Value #Duration")).tag('Date', 'value-duration'); //two years old
duration.match('#Value #Duration old').unTag('Date', 'val-years-old');
}
} //seasons
var season = doc["if"](seasons);
if (season.found === true) {
season.match("".concat(preps, "? ").concat(thisNext, " ").concat(seasons)).tag('Date', 'thisNext-season');
season.match("the? ".concat(sections, " of ").concat(seasons)).tag('Date', 'section-season');
season.match("".concat(seasons, " ").concat(preps, "? #Cardinal")).tag('Date', 'season-year');
} //rest-dates
var date = doc["if"]('#Date');
if (date.found === true) {
//june the 5th
date.match('#Date the? #Ordinal').tag('Date', 'correction'); //last month
date.match("".concat(thisNext, " #Date")).tag('Date', 'thisNext'); //by 5 March
date.match('due? (by|before|after|until) #Date').tag('Date', 'by'); //next feb
date.match('(last|next|this|previous|current|upcoming|coming|the) #Date').tag('Date', 'next-feb'); //start of june
date.match("the? ".concat(sections, " of #Date")).tag('Date', 'section-of'); //fifth week in 1998
date.match('#Ordinal #Duration in #Date').tag('Date', 'duration-in'); //early in june
date.match('(early|late) (at|in)? the? #Date').tag('Time', 'early-evening'); //tomorrow before 3
date.match('#Date (by|before|after|at|@|about) #Cardinal').not('^#Date').tag('Time', 'date-before-Cardinal'); //saturday am
date.match('#Date [(am|pm)]', 0).unTag('Verb').unTag('Copula').tag('Time', 'date-am'); //feb to june
date.match('#Date (#Preposition|to) #Date').ifNo('#Duration').tag('Date', 'date-prep-date'); //2nd quarter of 2019
// date.match('#Date of #Date').tag('Date', 'date-of-date')
} //year/cardinal tagging
var cardinal = doc["if"]('#Cardinal');
if (cardinal.found === true) {
var v = cardinal.match("#Date #Value [#Cardinal]", 0);
tagYear(v, 'date-value-year'); //scoops up a bunch
v = cardinal.match("#Date [#Cardinal]", 0);
tagYearSafe(v, 'date-year'); //middle of 1999
v = cardinal.match("".concat(sections, " of [#Cardinal]"));
tagYearSafe(v, 'section-year'); //feb 8 2018
v = cardinal.match("#Month #Value [#Cardinal]", 0);
tagYear(v, 'month-value-year'); //feb 8 to 10th 2018
v = cardinal.match("#Month #Value to #Value [#Cardinal]", 0);
tagYear(v, 'month-range-year'); //in 1998
v = cardinal.match("(in|of|by|during|before|starting|ending|for|year|since) [#Cardinal]", 0);
tagYear(v, 'in-year-1'); //q2 2009
v = cardinal.match('(q1|q2|q3|q4) [#Cardinal]', 0);
tagYear(v, 'in-year-2'); //2nd quarter 2009
v = cardinal.match('#Ordinal quarter [#Cardinal]', 0);
tagYear(v, 'in-year-3'); //in the year 1998
v = cardinal.match('the year [#Cardinal]', 0);
tagYear(v, 'in-year-4'); //it was 1998
v = cardinal.match('it (is|was) [#Cardinal]', 0);
tagYearSafe(v, 'in-year-5'); // re-tag this part
cardinal.match("".concat(sections, " of #Year")).tag('Date');
}
var time = doc["if"]('#Time');
if (time.found === true) {
//by 6pm
time.match('(by|before|after|at|@|about) #Time').tag('Time', 'preposition-time'); //7 7pm
// time.match('#Cardinal #Time').not('#Year').tag('Time', 'value-time')
//2pm est
time.match('#Time [(eastern|pacific|central|mountain)]', 0).tag('Date', 'timezone'); //6pm est
time.match('#Time [(est|pst|gmt)]', 0).tag('Date', 'timezone abbr');
} //'2020' bare input
var m = doc.match('^/^20[012][0-9]$/$');
tagYearSafe(m, '2020-ish'); // in 20mins
doc.match('(in|after) /^[0-9]+(min|sec|wk)s?/').tag('Date', 'shift-units');
return doc;
};
var _00Basic = tagDates;
var here = 'date-values'; //
var values = function values(doc) {
// a year ago
if (!doc.has('once [a] #Duration')) {
doc.match('[a] #Duration', 0).replaceWith('1').tag('Cardinal', here);
}
if (doc.has('#Value')) {
//june 5 to 7th
doc.match('#Month #Value to #Value of? #Year?').tag('Date', here); //5 to 7th june
doc.match('#Value to #Value of? #Month #Year?').tag('Date', here); //third week of may
doc.match('#Value #Duration of #Date').tag('Date', here); //two days after
doc.match('#Value+ #Duration (after|before|into|later|afterwards|ago)?').tag('Date', here); //two days
doc.match('#Value #Date').tag('Date', here); //june 5th
doc.match('#Date #Value').tag('Date', here); //tuesday at 5
doc.match('#Date #Preposition #Value').tag('Date', here); //tomorrow before 3
doc.match('#Date (after|before|during|on|in) #Value').tag('Date', here); //a year and a half
doc.match('#Value (year|month|week|day) and a half').tag('Date', here); //5 and a half years
doc.match('#Value and a half (years|months|weeks|days)').tag('Date', here); //on the fifth
doc.match('on the #Ordinal').tag('Date', here);
}
return doc;
};
var _01Values = values;
var here$1 = 'date-tagger'; //
var dateTagger = function dateTagger(doc) {
doc.match('(spring|summer|winter|fall|autumn|springtime|wintertime|summertime)').match('#Noun').tag('Season', here$1);
doc.match('(q1|q2|q3|q4)').tag('FinancialQuarter', here$1);
doc.match('(this|next|last|current) quarter').tag('FinancialQuarter', here$1);
doc.match('(this|next|last|current) season').tag('Season', here$1);
if (doc.has('#Date')) {
//friday to sunday
doc.match('#Date #Preposition #Date').tag('Date', here$1); //once a day..
doc.match('(once|twice) (a|an|each) #Date').tag('Date', here$1); //TODO:fixme
doc.match('(by|until|on|in|at|during|over|every|each|due) the? #Date').tag('Date', here$1); //tuesday
doc.match('#Date+').tag('Date', here$1); //by June
doc.match('(by|until|on|in|at|during|over|every|each|due) the? #Date').tag('Date', here$1); //a year after..
doc.match('a #Duration').tag('Date', here$1); //between x and y
doc.match('(between|from) #Date').tag('Date', here$1);
doc.match('(to|until|upto) #Date').tag('Date', here$1);
doc.match('#Date and #Date').tag('Date', here$1); //during this june
doc.match('(by|until|after|before|during|on|in|following|since) (next|this|last)? (#Date|#Date)').tag('Date', here$1); //day after next
doc.match('the? #Date after next one?').tag('Date', here$1); //approximately...
doc.match('(about|approx|approximately|around) #Date').tag('Date', here$1);
}
return doc;
};
var _02Dates = dateTagger;
var here$2 = 'section-tagger'; //
var sectionTagger = function sectionTagger(doc) {
if (doc.has('#Date')) {
// //next september
doc.match('this? (last|next|past|this|previous|current|upcoming|coming|the) #Date').tag('Date', here$2); //starting this june
doc.match('(starting|beginning|ending) #Date').tag('Date', here$2); //start of june
doc.match('the? (start|end|middle|beginning) of (last|next|this|the) (#Date|#Date)').tag('Date', here$2); //this coming june
doc.match('(the|this) #Date').tag('Date', here$2); //january up to june
doc.match('#Date up to #Date').tag('Date', here$2);
}
return doc;
};
var _03Sections = sectionTagger;
var here$3 = 'time-tagger'; //
var timeTagger = function timeTagger(doc) {
// 2 oclock
doc.match('#Cardinal oclock').tag('Time', here$3); // 13h30
doc.match('/^[0-9]{2}h[0-9]{2}$/').tag('Time', here$3); // 03/02
doc.match('/^[0-9]{2}/[0-9]{2}/').tag('Date', here$3).unTag('Value'); // 3 in the morning
doc.match('[#Value] (in|at) the? (morning|evening|night|nighttime)').tag('Time', here$3); // quarter to seven (not march 5 to 7)
if (doc.has('#Cardinal') && !doc.has('#Month')) {
doc.match('1? (half|quarter|25|15|10|5) (past|after|to) #Cardinal').tag('Time', here$3);
} //timezone
if (doc.has('#Date')) {
// iso (2020-03-02T00:00:00.000Z)
doc.match('/^[0-9]{4}[:-][0-9]{2}[:-][0-9]{2}T[0-9]/').tag('Time', here$3); // tuesday at 4
doc.match('#Date [at #Cardinal]', 0).notIf('#Year').tag('Time', here$3); // half an hour
doc.match('half an (hour|minute|second)').tag('Date', here$3); //eastern daylight time
doc.match('#Noun (standard|daylight|central|mountain)? time').tag('Timezone', here$3); //utc+5
doc.match('/^utc[+-][0-9]/').tag('Timezone', here$3);
doc.match('/^gmt[+-][0-9]/').tag('Timezone', here$3);
doc.match('(in|for|by|near|at) #Timezone').tag('Timezone', here$3); // 2pm eastern
doc.match('#Time [(eastern|mountain|pacific|central)]', 0).tag('Timezone', here$3);
}
return doc;
};
var _04Time = timeTagger;
var here$4 = 'shift-tagger'; //
var shiftTagger = function shiftTagger(doc) {
if (doc.has('#Date')) {
//'two days before'/ 'nine weeks frow now'
doc.match('#Cardinal #Duration (before|after|ago|from|hence|back)').tag('DateShift', here$4); // in two weeks
doc.match('in #Cardinal #Duration').tag('DateShift', here$4); // in a few weeks
doc.match('in a (few|couple) of? #Duration').tag('DateShift', here$4); //two weeks and three days before
doc.match('#Cardinal #Duration and? #DateShift').tag('DateShift', here$4);
doc.match('#DateShift and #Cardinal #Duration').tag('DateShift', here$4); // 'day after tomorrow'
doc.match('[#Duration (after|before)] #Date', 0).tag('DateShift', here$4); // in half an hour
doc.match('in half (a|an) #Duration').tag('DateShift', here$4);
}
return doc;
};
var _05Shifts = shiftTagger;
var here$5 = 'fix-tagger'; //
var fixUp = function fixUp(doc) {
//fixups
if (doc.has('#Date')) {
//first day by monday
var oops = doc.match('#Date+ by #Date+');
if (oops.found && !oops.has('^due')) {
oops.match('^#Date+').unTag('Date', 'by-monday');
}
var d = doc.match('#Date+'); //'spa day'
d.match('^day$').unTag('Date', 'spa-day'); // tomorrow's meeting
d.match('(in|of|by|for)? (#Possessive && #Date)').unTag('Date', 'tomorrows meeting');
var knownDate = '(yesterday|today|tomorrow)';
if (d.has(knownDate)) {
//yesterday 7
d.match("".concat(knownDate, " [#Value]$")).unTag('Date', 'yesterday-7'); //7 yesterday
d.match("^[#Value] ".concat(knownDate, "$"), 0).unTag('Date', '7 yesterday'); //friday yesterday
d.match("#WeekDay+ ".concat(knownDate, "$")).unTag('Date').lastTerm().tag('Date', 'fri-yesterday'); // yesterday yesterday
// d.match(`${knownDate}+ ${knownDate}$`)
// .unTag('Date')
// .lastTerm()
// .tag('Date', here)
d.match("(this|last|next) #Date ".concat(knownDate, "$")).unTag('Date').lastTerm().tag('Date', 'this month yesterday');
} //tomorrow on 5
d.match("on #Cardinal$").unTag('Date', here$5); //this tomorrow
d.match("this tomorrow").terms(0).unTag('Date', 'this-tomorrow'); //q2 2019
d.match("(q1|q2|q3|q4) #Year").tag('Date', here$5); //5 tuesday
// d.match(`^#Value #WeekDay`).terms(0).unTag('Date');
//5 next week
d.match("^#Value (this|next|last)").terms(0).unTag('Date', here$5);
if (d.has('(last|this|next)')) {
//this month 7
d.match("(last|this|next) #Duration #Value").terms(2).unTag('Date', here$5); //7 this month
d.match("!#Month #Value (last|this|next) #Date").terms(0).unTag('Date', here$5);
} //january 5 5
if (d.has('(#Year|#Time|#TextValue|#NumberRange)') === false) {
d.match('(#Month|#WeekDay) #Value #Value').terms(2).unTag('Date', here$5);
} //between june
if (d.has('^between') && !d.has('and .')) {
d.unTag('Date', here$5);
} //june june
if (d.has('#Month #Month') && !d.has('@hasHyphen') && !d.has('@hasComma')) {
d.match('#Month').lastTerm().unTag('Date', 'month-month');
} // log the hours
if (d.has('(minutes|seconds|weeks|hours|days|months)') && !d.has('#Value #Duration')) {
d.match('(minutes|seconds|weeks|hours|days|months)').unTag('Date', 'log-hours');
} // about thanksgiving
if (d.has('about #Holiday')) {
d.match('about').unTag('#Date', 'about-thanksgiving');
} // a month from now
d.match('(from|by|before) now').unTag('Time'); // dangling date-chunks
// if (d.has('!#Date (in|of|by|for) !#Date')) {
// d.unTag('Date', 'dangling-date')
// }
// the day after next
d.match('#Date+').match('^the').unTag('Date');
}
return doc;
};
var _06Fixup = fixUp;
var methods = [_00Basic, _01Values, _02Dates, _03Sections, _04Time, _05Shifts, _06Fixup]; // normalizations to run before tagger
var normalize = function normalize(doc) {
// turn '20mins' into '20 mins'
doc.numbers().normalize(); // this is sorta problematic
return doc;
}; // run each of the taggers
var tagDate = function tagDate(doc) {
doc = normalize(doc); // run taggers
methods.forEach(function (fn) {
return fn(doc);
});
return doc;
};
var _01Tagger = tagDate;
var _tags = {
FinancialQuarter: {
isA: 'Date'
},
// 'summer'
Season: {
isA: 'Date'
},
// '1982'
Year: {
isA: ['Date'],
notA: 'RomanNumeral'
},
// 'months'
Duration: {
isA: ['Date', 'Noun']
},
// 'easter'
Holiday: {
isA: ['Date', 'Noun']
},
// 'PST'
Timezone: {
isA: ['Date', 'Noun'],
notA: ['Adjective', 'DateShift']
},
// 'two weeks before'
DateShift: {
isA: ['Date'],
notA: ['TimeZone', 'Holiday']
}
};
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn) {
var module = { exports: {} };
return fn(module, module.exports), module.exports;
}
/* spencermountain/spacetime 6.12.2 Apache 2.0 */
var spacetime = createCommonjsModule(function (module, exports) {
(function (global, factory) {
module.exports = factory() ;
})(commonjsGlobal, function () {
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArrayLimit(arr, i) {
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) {
arr2[i] = arr[i];
}
return arr2;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var MSEC_IN_HOUR = 60 * 60 * 1000; //convert our local date syntax a javascript UTC date
var toUtc = function toUtc(dstChange, offset, year) {
var _dstChange$split = dstChange.split('/'),
_dstChange$split2 = _slicedToArray(_dstChange$split, 2),
month = _dstChange$split2[0],
rest = _dstChange$split2[1];
var _rest$split = rest.split(':'),
_rest$split2 = _slicedToArray(_rest$split, 2),
day = _rest$split2[0],
hour = _rest$split2[1];
return Date.UTC(year, month - 1, day, hour) - offset * MSEC_IN_HOUR;
}; // compare epoch with dst change events (in utc)
var inSummerTime = function inSummerTime(epoch, start, end, summerOffset, winterOffset) {
var year = new Date(epoch).getUTCFullYear();
var startUtc = toUtc(start, winterOffset, year);
var endUtc = toUtc(end, summerOffset, year); // console.log(epoch, endUtc)
// simple number comparison now
return epoch >= startUtc && epoch < endUtc;
};
var summerTime = inSummerTime; // it reproduces some things in ./index.js, but speeds up spacetime considerably
var quickOffset = function quickOffset(s) {
var zones = s.timezones;
var obj = zones[s.tz];
if (obj === undefined) {
console.warn("Warning: couldn't find timezone " + s.tz);
return 0;
}
if (obj.dst === undefined) {
return obj.offset;
} //get our two possible offsets
var jul = obj.offset;
var dec = obj.offset + 1; // assume it's the same for now
if (obj.hem === 'n') {
dec = jul - 1;
}
var split = obj.dst.split('->');
var inSummer = summerTime(s.epoch, split[0], split[1], jul, dec);
if (inSummer === true) {
return jul;
}
return dec;
};
var quick = quickOffset;
var _build = {
"9|s": "2/dili,2/jayapura",
"9|n": "2/chita,2/khandyga,2/pyongyang,2/seoul,2/tokyo,11/palau",
"9.5|s|04/05:03->10/04:02": "4/adelaide,4/broken_hill,4/south,4/yancowinna",
"9.5|s": "4/darwin,4/north",
"8|s|03/08:01->10/04:00": "12/casey",
"8|s": "2/kuala_lumpur,2/makassar,2/singapore,4/perth,4/west",
"8|n|03/25:03->09/29:23": "2/ulan_bator",
"8|n": "2/brunei,2/choibalsan,2/chongqing,2/chungking,2/harbin,2/hong_kong,2/irkutsk,2/kuching,2/macao,2/macau,2/manila,2/shanghai,2/taipei,2/ujung_pandang,2/ulaanbaatar",
"8.75|s": "4/eucla",
"7|s": "12/davis,2/jakarta,9/christmas",
"7|n": "2/bangkok,2/barnaul,2/ho_chi_minh,2/hovd,2/krasnoyarsk,2/novokuznetsk,2/novosibirsk,2/phnom_penh,2/pontianak,2/saigon,2/tomsk,2/vientiane",
"6|s": "12/vostok",
"6|n": "2/almaty,2/bishkek,2/dacca,2/dhaka,2/kashgar,2/omsk,2/qyzylorda,2/qostanay,2/thimbu,2/thimphu,2/urumqi,9/chagos",
"6.5|n": "2/rangoon,2/yangon,9/cocos",
"5|s": "12/mawson,9/kerguelen",
"5|n": "2/aqtau,2/aqtobe,2/ashgabat,2/ashkhabad,2/atyrau,2/baku,2/dushanbe,2/karachi,2/oral,2/samarkand,2/tashkent,2/yekaterinburg,9/maldives",
"5.75|n": "2/kathmandu,2/katmandu",
"5.5|n": "2/calcutta,2/colombo,2/kolkata",
"4|s": "9/reunion",
"4|n": "2/dubai,2/muscat,2/tbilisi,2/yerevan,8/astrakhan,8/samara,8/saratov,8/ulyanovsk,8/volgograd,2/volgograd,9/mahe,9/mauritius",
"4.5|n|03/21:00->09/20:24": "2/tehran",
"4.5|n": "2/kabul",
"3|s": "12/syowa,9/antananarivo",
"3|n|03/29:03->10/25:04": "2/famagusta,2/nicosia,8/athens,8/bucharest,8/helsinki,8/kiev,8/mariehamn,8/nicosia,8/riga,8/sofia,8/tallinn,8/uzhgorod,8/vilnius,8/zaporozhye",
"3|n|03/29:02->10/25:03": "8/chisinau,8/tiraspol",
"3|n|03/29:00->10/24:24": "2/beirut",
"3|n|03/28:00->10/24:01": "2/gaza,2/hebron",
"3|n|03/27:02->10/25:02": "2/jerusalem,2/tel_aviv",
"3|n|03/27:00->10/30:01": "2/amman",
"3|n|03/27:00->10/29:24": "2/damascus",
"3|n": "0/addis_ababa,0/asmara,0/asmera,0/dar_es_salaam,0/djibouti,0/juba,0/kampala,0/mogadishu,0/nairobi,2/aden,2/baghdad,2/bahrain,2/istanbul,2/kuwait,2/qatar,2/riyadh,8/istanbul,8/kirov,8/minsk,8/moscow,8/simferopol,9/comoro,9/mayotte",
"2|s|03/29:02->10/25:02": "12/troll",
"2|s": "0/gaborone,0/harare,0/johannesburg,0/lubumbashi,0/lusaka,0/maputo,0/maseru,0/mbabane",
"2|n|03/29:02->10/25:03": "0/ceuta,arctic/longyearbyen,3/jan_mayen,8/amsterdam,8/andorra,8/belgrade,8/berlin,8/bratislava,8/brussels,8/budapest,8/busingen,8/copenhagen,8/gibraltar,8/ljubljana,8/luxembourg,8/madrid,8/malta,8/monaco,8/oslo,8/paris,8/podgorica,8/prague,8/rome,8/san_marino,8/sarajevo,8/skopje,8/stockholm,8/tirane,8/vaduz,8/vatican,8/vienna,8/warsaw,8/zagreb,8/zurich",
"2|n": "0/blantyre,0/bujumbura,0/cairo,0/khartoum,0/kigali,0/tripoli,8/kaliningrad",
"1|s|04/02:01->09/03:03": "0/windhoek",
"1|s": "0/kinshasa,0/luanda",
"1|n|04/19:03->05/31:02": "0/casablanca,0/el_aaiun",
"1|n|03/29:01->10/25:02": "3/canary,3/faeroe,3/faroe,3/madeira,8/belfast,8/dublin,8/guernsey,8/isle_of_man,8/jersey,8/lisbon,8/london",
"1|n": "0/algiers,0/bangui,0/brazzaville,0/douala,0/lagos,0/libreville,0/malabo,0/ndjamena,0/niamey,0/porto-novo,0/tunis",
"14|n": "11/kiritimati",
"13|s|04/05:04->09/27:03": "11/apia",
"13|s|01/15:02->11/05:03": "11/tongatapu",
"13|n": "11/enderbury,11/fakaofo",
"12|s|04/05:03->09/27:02": "12/mcmurdo,12/south_pole,11/auckland",
"12|s|01/12:03->12/20:02": "11/fiji",
"12|n": "2/anadyr,2/kamchatka,2/srednekolymsk,11/funafuti,11/kwajalein,11/majuro,11/nauru,11/tarawa,11/wake,11/wallis",
"12.75|s|04/05:03->04/05:02": "11/chatham",
"11|s|04/05:03->10/04:02": "12/macquarie",
"11|s": "11/bougainville",
"11|n": "2/magadan,2/sakhalin,11/efate,11/guadalcanal,11/kosrae,11/noumea,11/pohnpei,11/ponape",
"11.5|n|04/05:03->10/04:02": "11/norfolk",
"10|s|04/05:03->10/04:02": "4/act,4/canberra,4/currie,4/hobart,4/melbourne,4/nsw,4/sydney,4/tasmania,4/victoria",
"10|s": "12/dumontdurville,4/brisbane,4/lindeman,4/queensland",
"10|n": "2/ust-nera,2/vladivostok,2/yakutsk,11/chuuk,11/guam,11/port_moresby,11/saipan,11/truk,11/yap",
"10.5|s|04/05:01->10/04:02": "4/lhi,4/lord_howe",
"0|n|03/29:00->10/25:01": "1/scoresbysund,3/azores",
"0|n": "0/abidjan,0/accra,0/bamako,0/banjul,0/bissau,0/conakry,0/dakar,0/freetown,0/lome,0/monrovia,0/nouakchott,0/ouagadougou,0/sao_tome,0/timbuktu,1/danmarkshavn,3/reykjavik,3/st_helena,13/gmt,13/gmt+0,13/gmt-0,13/gmt0,13/greenwich,13/utc,13/universal,13/zulu",
"-9|n|03/08:02->11/01:02": "1/adak,1/atka",
"-9|n": "11/gambier",
"-9.5|n": "11/marquesas",
"-8|n|03/08:02->11/01:02": "1/anchorage,1/juneau,1/metlakatla,1/nome,1/sitka,1/yakutat",
"-8|n": "11/pitcairn",
"-7|n|03/08:02->11/01:02": "1/ensenada,1/los_angeles,1/santa_isabel,1/tijuana,1/vancouver,6/pacific,10/bajanorte",
"-7|n|03/08:02->11/01:01": "1/dawson,1/whitehorse,6/yukon",
"-7|n": "1/creston,1/dawson_creek,1/fort_nelson,1/hermosillo,1/phoenix",
"-6|s|04/04:22->09/05:22": "7/easterisland,11/easter",
"-6|n|04/05:02->10/25:02": "1/chihuahua,1/mazatlan,10/bajasur",
"-6|n|03/08:02->11/01:02": "1/boise,1/cambridge_bay,1/denver,1/edmonton,1/inuvik,1/ojinaga,1/shiprock,1/yellowknife,6/mountain",
"-6|n": "1/belize,1/costa_rica,1/el_salvador,1/guatemala,1/managua,1/regina,1/swift_current,1/tegucigalpa,6/east-saskatchewan,6/saskatchewan,11/galapagos",
"-5|s": "1/lima,1/rio_branco,5/acre",
"-5|n|04/05:02->10/25:02": "1/bahia_banderas,1/merida,1/mexico_city,1/monterrey,10/general",
"-5|n|03/12:03->11/05:01": "1/north_dakota",
"-5|n|03/08:02->11/01:02": "1/chicago,1/knox_in,1/matamoros,1/menominee,1/rainy_river,1/rankin_inlet,1/resolute,1/winnipeg,6/central",
"-5|n": "1/atikokan,1/bogota,1/cancun,1/cayman,1/coral_harbour,1/eirunepe,1/guayaquil,1/jamaica,1/panama,1/porto_acre",
"-4|s|05/13:23->08/13:01": "12/palmer",
"-4|s|04/04:24->09/06:00": "1/santiago,7/continental",
"-4|s|03/21:24->10/04:00": "1/asuncion",
"-4|s|02/16:24->11/03:00": "1/campo_grande,1/cuiaba",
"-4|s": "1/la_paz,1/manaus,5/west",
"-4|n|03/12:03->11/05:01": "1/indiana,1/kentucky",
"-4|n|03/08:02->11/01:02": "1/detroit,1/fort_wayne,1/grand_turk,1/indianapolis,1/iqaluit,1/louisville,1/montreal,1/nassau,1/new_york,1/nipigon,1/pangnirtung,1/port-au-prince,1/thunder_bay,1/toronto,6/eastern",
"-4|n|03/08:00->11/01:01": "1/havana",
"-4|n": "1/anguilla,1/antigua,1/aruba,1/barbados,1/blanc-sablon,1/boa_vista,1/caracas,1/curacao,1/dominica,1/grenada,1/guadeloupe,1/guyana,1/kralendijk,1/lower_princes,1/marigot,1/martinique,1/montserrat,1/port_of_spain,1/porto_velho,1/puerto_rico,1/santo_domingo,1/st_barthelemy,1/st_kitts,1/st_lucia,1/st_thomas,1/st_vincent,1/tortola,1/virgin",
"-3|s": "1/argentina,1/buenos_aires,1/cordoba,1/fortaleza,1/montevideo,1/punta_arenas,1/sao_paulo,12/rothera,3/stanley,5/east",
"-3|n|03/28:22->10/24:23": "1/nuuk",
"-3|n|03/08:02->11/01:02": "1/glace_bay,1/goose_bay,1/halifax,1/moncton,1/thule,3/bermuda,6/atlantic",
"-3|n": "1/araguaina,1/bahia,1/belem,1/catamarca,1/cayenne,1/jujuy,1/maceio,1/mendoza,1/paramaribo,1/recife,1/rosario,1/santarem",
"-2|s": "5/denoronha",
"-2|n|03/28:22->10/24:23": "1/godthab",
"-2|n|03/08:02->11/01:02": "1/miquelon",
"-2|n": "1/noronha,3/south_georgia",
"-2.5|n|03/08:02->11/01:02": "1/st_johns,6/newfoundland",
"-1|n": "3/cape_verde",
"-11|n": "11/midway,11/niue,11/pago_pago,11/samoa",
"-10|n": "11/honolulu,11/johnston,11/rarotonga,11/tahiti"
};
var _build$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
'default': _build
}); //prefixes for iana names..
var _prefixes = ['africa', 'america', 'asia', 'atlantic', 'australia', 'brazil', 'canada', 'chile', 'europe', 'indian', 'mexico', 'pacific', 'antarctica', 'etc'];
function createCommonjsModule(fn, module) {
return module = {
exports: {}
}, fn(module, module.exports), module.exports;
}
function getCjsExportFromNamespace(n) {
return n && n['default'] || n;
}
var data = getCjsExportFromNamespace(_build$1);
var all = {};
Object.keys(data).forEach(function (k) {
var split = k.split('|');
var obj = {
offset: Number(split[0]),
hem: split[1]
};
if (split[2]) {
obj.dst = split[2];
}
var names = data[k].split(',');
names.forEach(function (str) {
str = str.replace(/(^[0-9]+)\//, function (before, num) {
num = Number(num);
return _prefixes[num] + '/';
});
all[str] = obj;
});
});
all['utc'] = {
offset: 0,
hem: 'n' //default to northern hemisphere - (sorry!)
}; //add etc/gmt+n
for (var i = -14; i <= 14; i += 0.5) {
var num = i;
if (num > 0) {
num = '+' + num;
}
var name = 'etc/gmt' + num;
all[name] = {
offset: i * -1,
//they're negative!
hem: 'n' //(sorry)
};
name = 'utc/gmt' + num; //this one too, why not.
all[name] = {
offset: i * -1,
hem: 'n'
};
}
var unpack = all; //find the implicit iana code for this machine.
//safely query the Intl object
//based on - https://bitbucket.org/pellepim/jstimezonedetect/src
var fallbackTZ = 'utc'; //
//this Intl object is not supported often, yet
var safeIntl = function safeIntl() {
if (typeof Intl === 'undefined' || typeof Intl.DateTimeFormat === 'undefined') {
return null;
}
var format = Intl.DateTimeFormat();
if (typeof format === 'undefined' || typeof format.resolvedOptions === 'undefined') {
return null;
}
var timezone = format.resolvedOptions().timeZone;
if (!timezone) {
return null;
}
return timezone.toLowerCase();
};
var guessTz = function guessTz() {
var timezone = safeIntl();
if (timezone === null) {
return fallbackTZ;
}
return timezone;
}; //do it once per computer
var guessTz_1 = guessTz;
var isOffset = /(\-?[0-9]+)h(rs)?/i;
var isNumber = /(\-?[0-9]+)/;
var utcOffset = /utc([\-+]?[0-9]+)/i;
var gmtOffset = /gmt([\-+]?[0-9]+)/i;
var toIana = function toIana(num) {
num = Number(num);
if (num >= -13 && num <= 13) {
num = num * -1; //it's opposite!
num = (num > 0 ? '+' : '') + num; //add plus sign
return 'etc/gmt' + num;
}
return null;
};
var parseOffset = function parseOffset(tz) {
// '+5hrs'
var m = tz.match(isOffset);
if (m !== null) {
return toIana(m[1]);
} // 'utc+5'
m = tz.match(utcOffset);
if (m !== null) {
return toIana(m[1]);
} // 'GMT-5' (not opposite)
m = tz.match(gmtOffset);
if (m !== null) {
var num = Number(m[1]) * -1;
return toIana(num);
} // '+5'
m = tz.match(isNumber);
if (m !== null) {
return toIana(m[1]);
}
return null;
};
var parseOffset_1 = parseOffset;
var local = guessTz_1(); //add all the city names by themselves
var cities = Object.keys(unpack).reduce(function (h, k) {
var city = k.split('/')[1] || '';
city = city.replace(/_/g, ' ');
h[city] = k;
return h;
}, {}); //try to match these against iana form
var normalize = function normalize(tz) {
tz = tz.replace(/ time/g, '');
tz = tz.replace(/ (standard|daylight|summer)/g, '');
tz = tz.replace(/\b(east|west|north|south)ern/g, '$1');
tz = tz.replace(/\b(africa|america|australia)n/g, '$1');
tz = tz.replace(/\beuropean/g, 'europe');
tz = tz.replace(/\islands/g, 'island');
return tz;
}; // try our best to reconcile the timzone to this given string
var lookupTz = function lookupTz(str, zones) {
if (!str) {
return local;
}
if (typeof str !== 'string') {
console.error("Timezone must be a string - recieved: '", str, "'\n");
}
var tz = str.trim();
var split = str.split('/'); //support long timezones like 'America/Argentina/Rio_Gallegos'
if (split.length > 2 && zones.hasOwnProperty(tz) === false) {
tz = split[0] + '/' + split[1];
}
tz = tz.toLowerCase();
if (zones.hasOwnProperty(tz) === true) {
return tz;
} //lookup more loosely..
tz = normalize(tz);
if (zones.hasOwnProperty(tz) === true) {
return tz;
} //try city-names
if (cities.hasOwnProperty(tz) === true) {
return cities[tz];
} // //try to parse '-5h'
if (/[0-9]/.test(tz) === true) {
var id = parseOffset_1(tz);
if (id) {
return id;
}
}
throw new Error("Spacetime: Cannot find timezone named: '" + str + "'. Please enter an IANA timezone id.");
};
var find = lookupTz;
var o = {
millisecond: 1
};
o.second = 1000;
o.minute = 60000;
o.hour = 3.6e6; // dst is supported post-hoc
o.day = 8.64e7; //
o.date = o.day;
o.month = 8.64e7 * 29.5; //(average)
o.week = 6.048e8;
o.year = 3.154e10; // leap-years are supported post-hoc
//add plurals
Object.keys(o).forEach(function (k) {
o[k + 's'] = o[k];
});
var milliseconds = o;
var walk = function walk(s, n, fn, unit, previous) {
var current = s.d[fn]();
if (current === n) {
return; //already there
}
var startUnit = previous === null ? null : s.d[previous]();
var original = s.epoch; //try to get it as close as we can
var diff = n - current;
s.epoch += milliseconds[unit] * diff; //DST edge-case: if we are going many days, be a little conservative
// console.log(unit, diff)
if (unit === 'day') {
// s.epoch -= ms.minute
//but don't push it over a month
if (Math.abs(diff) > 28 && n < 28) {
s.epoch += milliseconds.hour;
}
} // 1st time: oops, did we change previous unit? revert it.
if (previous !== null && startUnit !== s.d[previous]()) {
// console.warn('spacetime warning: missed setting ' + unit)
s.epoch = original; // s.epoch += ms[unit] * diff * 0.89 // maybe try and make it close...?
} //repair it if we've gone too far or something
//(go by half-steps, just in case)
var halfStep = milliseconds[unit] / 2;
while (s.d[fn]() < n) {
s.epoch += halfStep;
}
while (s.d[fn]() > n) {
s.epoch -= halfStep;
} // 2nd time: did we change previous unit? revert it.
if (previous !== null && startUnit !== s.d[previous]()) {
// console.warn('spacetime warning: missed setting ' + unit)
s.epoch = original;
}
}; //find the desired date by a increment/check while loop
var units = {
year: {
valid: function valid(n) {
return n > -4000 && n < 4000;
},
walkTo: function walkTo(s, n) {
return walk(s, n, 'getFullYear', 'year', null);
}
},
month: {
valid: function valid(n) {
return n >= 0 && n <= 11;
},
walkTo: function walkTo(s, n) {
var d = s.d;
var current = d.getMonth();
var original = s.epoch;
var startUnit = d.getFullYear();
if (current === n) {
return;
} //try to get it as close as we can..
var diff = n - current;
s.epoch += milliseconds.day * (diff * 28); //special case
//oops, did we change the year? revert it.
if (startUnit !== s.d.getFullYear()) {
s.epoch = original;
} //incriment by day
while (s.d.getMonth() < n) {
s.epoch += milliseconds.day;
}
while (s.d.getMonth() > n) {
s.epoch -= milliseconds.day;
}
}
},
date: {
valid: function valid(n) {
return n > 0 && n <= 31;
},
walkTo: function walkTo(s, n) {
return walk(s, n, 'getDate', 'day', 'getMonth');
}
},
hour: {
valid: function valid(n) {
return n >= 0 && n < 24;
},
walkTo: function walkTo(s, n) {
return walk(s, n, 'getHours', 'hour', 'getDate');
}
},
minute: {
valid: function valid(n) {
return n >= 0 && n < 60;
},
walkTo: function walkTo(s, n) {
return walk(s, n, 'getMinutes', 'minute', 'getHours');
}
},
second: {
valid: function valid(n) {
return n >= 0 && n < 60;
},
walkTo: function walkTo(s, n) {
//do this one directly
s.epoch = s.seconds(n).epoch;
}
},
millisecond: {
valid: function valid(n) {
return n >= 0 && n < 1000;
},
walkTo: function walkTo(s, n) {
//do this one directly
s.epoch = s.milliseconds(n).epoch;
}
}
};
var walkTo = function walkTo(s, wants) {
var keys = Object.keys(units);
var old = s.clone();
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
var n = wants[k];
if (n === undefined) {
n = old[k]();
}
if (typeof n === 'string') {
n = parseInt(n, 10);
} //make-sure it's valid
if (!units[k].valid(n)) {
s.epoch = null;
if (s.silent === false) {
console.warn('invalid ' + k + ': ' + n);
}
return;
}
units[k].walkTo(s, n);
}
return;
};
var walk_1 = walkTo;
var shortMonths = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sept', 'oct', 'nov', 'dec'];
var longMonths = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'];
function buildMapping() {
var obj = {
sep: 8 //support this format
};
for (var i = 0; i < shortMonths.length; i++) {
obj[shortMonths[i]] = i;
}
for (var _i = 0; _i < longMonths.length; _i++) {
obj[longMonths[_i]] = _i;
}
return obj;
}
var months = {
"short": function _short() {
return shortMonths;
},
"long": function _long() {
return longMonths;
},
mapping: function mapping() {
return buildMapping();
},
set: function set(i18n) {
shortMonths = i18n["short"] || shortMonths;
longMonths = i18n["long"] || longMonths;
}
}; //pull-apart ISO offsets, like "+0100"
var parseOffset$1 = function parseOffset(s, offset) {
if (!offset) {
return s;
} //this is a fancy-move
if (offset === 'Z' || offset === 'z') {
offset = '+0000';
} // according to ISO8601, tz could be hh:mm, hhmm or hh
// so need few more steps before the calculation.
var num = 0; // for (+-)hh:mm
if (/^[\+-]?[0-9]{2}:[0-9]{2}$/.test(offset)) {
//support "+01:00"
if (/:00/.test(offset) === true) {
offset = offset.replace(/:00/, '');
} //support "+01:30"
if (/:30/.test(offset) === true) {
offset = offset.replace(/:30/, '.5');
}
} // for (+-)hhmm
if (/^[\+-]?[0-9]{4}$/.test(offset)) {
offset = offset.replace(/30$/, '.5');
}
num = parseFloat(offset); //divide by 100 or 10 - , "+0100", "+01"
if (Math.abs(num) > 100) {
num = num / 100;
} //okay, try to match it to a utc timezone
//remember - this is opposite! a -5 offset maps to Etc/GMT+5 ¯\_(:/)_/¯
//https://askubuntu.com/questions/519550/why-is-the-8-timezone-called-gmt-8-in-the-filesystem
num *= -1;
if (num >= 0) {
num = '+' + num;
}
var tz = 'etc/gmt' + num;
var zones = s.timezones;
if (zones[tz]) {
// log a warning if we're over-writing a given timezone?
// console.log('changing timezone to: ' + tz)
s.tz = tz;
}
return s;
};
var parseOffset_1$1 = parseOffset$1;
var parseTime = function parseTime(s) {
var str = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
str = str.replace(/^\s+/, '').toLowerCase(); //trim
//formal time formats - 04:30.23
var arr = str.match(/([0-9]{1,2}):([0-9]{1,2}):?([0-9]{1,2})?[:\.]?([0-9]{1,4})?/);
if (arr !== null) {
//validate it a little
var h = Number(arr[1]);
if (h < 0 || h > 24) {
return s.startOf('day');
}
var m = Number(arr[2]); //don't accept '5:3pm'
if (arr[2].length < 2 || m < 0 || m > 59) {
return s.startOf('day');
}
if (arr[4] > 999) {
// fix overflow issue with milliseconds, if input is longer than standard (e.g. 2017-08-06T09:00:00.123456Z)
arr[4] = parseInt("".concat(arr[4]).substring(0, 3), 10);
}
s = s.hour(h);
s = s.minute(m);
s = s.seconds(arr[3] || 0);
s = s.millisecond(arr[4] || 0); //parse-out am/pm
var ampm = str.match(/[\b0-9](am|pm)\b/);
if (ampm !== null && ampm[1]) {
s = s.ampm(ampm[1]);
}
return s;
} //try an informal form - 5pm (no minutes)
arr = str.match(/([0-9]+) ?(am|pm)/);
if (arr !== null && arr[1]) {
var _h = Number(arr[1]); //validate it a little..
if (_h > 12 || _h < 1) {
return s.startOf('day');
}
s = s.hour(arr[1] || 0);
s = s.ampm(arr[2]);
s = s.startOf('hour');
return s;
} //no time info found, use start-of-day
s = s.startOf('day');
return s;
};
var parseTime_1 = parseTime;
var monthLengths = [31, // January - 31 days
28, // February - 28 days in a common year and 29 days in leap years
31, // March - 31 days
30, // April - 30 days
31, // May - 31 days
30, // June - 30 days
31, // July - 31 days
31, // August - 31 days
30, // September - 30 days
31, // October - 31 days
30, // November - 30 days
31 // December - 31 days
];
var monthLengths_1 = monthLengths; // 28 - feb
var fns = createCommonjsModule(function (module, exports) {
//git:blame @JuliasCaesar https://www.timeanddate.com/date/leapyear.html
exports.isLeapYear = function (year) {
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
}; // unsurprisingly-nasty `typeof date` call
exports.isDate = function (d) {
return Object.prototype.toString.call(d) === '[object Date]' && !isNaN(d.valueOf());
};
exports.isArray = function (input) {
return Object.prototype.toString.call(input) === '[object Array]';
};
exports.isObject = function (input) {
return Object.prototype.toString.call(input) === '[object Object]';
};
exports.isBoolean = function (input) {
return Object.prototype.toString.call(input) === '[object Boolean]';
};
exports.zeroPad = function (str) {
var len = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
var pad = '0';
str = str + '';
return str.length >= len ? str : new Array(len - str.length + 1).join(pad) + str;
};
exports.titleCase = function (str) {
if (!str) {
return '';
}
return str[0].toUpperCase() + str.substr(1);
};
exports.ordinal = function (i) {
var j = i % 10;
var k = i % 100;
if (j === 1 && k !== 11) {
return i + 'st';
}
if (j === 2 && k !== 12) {
return i + 'nd';
}
if (j === 3 && k !== 13) {
return i + 'rd';
}
return i + 'th';
}; //strip 'st' off '1st'..
exports.toCardinal = function (str) {
str = String(str);
str = str.replace(/([0-9])(st|nd|rd|th)$/i, '$1');
return parseInt(str, 10);
}; //used mostly for cleanup of unit names, like 'months'
exports.normalize = function () {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
str = str.toLowerCase().trim();
str = str.replace(/ies$/, 'y'); //'centuries'
str = str.replace(/s$/, '');
str = str.replace(/-/g, '');
if (str === 'day' || str === 'days') {
return 'date';
}
if (str === 'min' || str === 'mins') {
return 'minute';
}
return str;
};
exports.getEpoch = function (tmp) {
//support epoch
if (typeof tmp === 'number') {
return tmp;
} //suport date objects
if (exports.isDate(tmp)) {
return tmp.getTime();
}
if (tmp.epoch) {
return tmp.epoch;
}
return null;
}; //make sure this input is a spacetime obj
exports.beADate = function (d, s) {
if (exports.isObject(d) === false) {
return s.clone().set(d);
}
return d;
};
exports.formatTimezone = function (offset) {
var delimiter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var sign = offset > 0 ? '+' : '-';
var absOffset = Math.abs(offset);
var hours = exports.zeroPad(parseInt('' + absOffset, 10));
var minutes = exports.zeroPad(absOffset % 1 * 60);
return "".concat(sign).concat(hours).concat(delimiter).concat(minutes);
};
});
var fns_1 = fns.isLeapYear;
var fns_2 = fns.isDate;
var fns_3 = fns.isArray;
var fns_4 = fns.isObject;
var fns_5 = fns.isBoolean;
var fns_6 = fns.zeroPad;
var fns_7 = fns.titleCase;
var fns_8 = fns.ordinal;
var fns_9 = fns.toCardinal;
var fns_10 = fns.normalize;
var fns_11 = fns.getEpoch;
var fns_12 = fns.beADate;
var fns_13 = fns.formatTimezone;
var isLeapYear = fns.isLeapYear; //given a month, return whether day number exists in it
var hasDate = function hasDate(obj) {
//invalid values
if (monthLengths_1.hasOwnProperty(obj.month) !== true) {
return false;
} //support leap-year in february
if (obj.month === 1) {
if (isLeapYear(obj.year) && obj.date <= 29) {
return true;
} else {
return obj.date <= 28;
}
} //is this date too-big for this month?
var max = monthLengths_1[obj.month] || 0;
if (obj.date <= max) {
return true;
}
return false;
};
var hasDate_1 = hasDate;
var months$1 = months.mapping();
var parseYear = function parseYear() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var today = arguments.length > 1 ? arguments[1] : undefined;
var year = parseInt(str.trim(), 10); // use a given year from options.today
if (!year && today) {
year = today.year;
} // fallback to this year
year = year || new Date().getFullYear();
return year;
};
var strFmt = [//iso-this 1998-05-30T22:00:00:000Z, iso-that 2017-04-03T08:00:00-0700
{
reg: /^(\-?0?0?[0-9]{3,4})-([0-9]{1,2})-([0-9]{1,2})[T| ]([0-9.:]+)(Z|[0-9\-\+:]+)?$/i,
parse: function parse(s, arr, givenTz, options) {
var month = parseInt(arr[2], 10) - 1;
var obj = {
year: arr[1],
month: month,
date: arr[3]
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
parseOffset_1$1(s, arr[5]);
walk_1(s, obj);
s = parseTime_1(s, arr[4]);
return s;
}
}, //iso "2015-03-25" or "2015/03/25" or "2015/03/25 12:26:14 PM"
{
reg: /^([0-9]{4})[\-\/.]([0-9]{1,2})[\-\/.]([0-9]{1,2}),?( [0-9]{1,2}:[0-9]{2}:?[0-9]{0,2}? ?(am|pm|gmt))?$/i,
parse: function parse(s, arr) {
var obj = {
year: arr[1],
month: parseInt(arr[2], 10) - 1,
date: parseInt(arr[3], 10)
};
if (obj.month >= 12) {
//support yyyy/dd/mm (weird, but ok)
obj.date = parseInt(arr[2], 10);
obj.month = parseInt(arr[3], 10) - 1;
}
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s, arr[4]);
return s;
}
}, //mm/dd/yyyy - uk/canada "6/28/2019, 12:26:14 PM"
{
reg: /^([0-9]{1,2})[\-\/.]([0-9]{1,2})[\-\/.]?([0-9]{4})?,?( [0-9]{1,2}:[0-9]{2}:?[0-9]{0,2}? ?(am|pm|gmt))?$/i,
parse: function parse(s, arr) {
var month = parseInt(arr[1], 10) - 1;
var date = parseInt(arr[2], 10); //support dd/mm/yyy
if (s.british || month >= 12) {
date = parseInt(arr[1], 10);
month = parseInt(arr[2], 10) - 1;
}
var year = arr[3] || new Date().getFullYear();
var obj = {
year: year,
month: month,
date: date
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s, arr[4]);
return s;
}
}, // '2012-06' last attempt at iso-like format
{
reg: /^([0-9]{4})[\-\/]([0-9]{2})$/i,
parse: function parse(s, arr, givenTz, options) {
var month = parseInt(arr[2], 10) - 1;
var obj = {
year: arr[1],
month: month,
date: 1
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
parseOffset_1$1(s, arr[5]);
walk_1(s, obj);
s = parseTime_1(s, arr[4]);
return s;
}
}, //common british format - "25-feb-2015"
{
reg: /^([0-9]{1,2})[\-\/]([a-z]+)[\-\/]?([0-9]{4})?$/i,
parse: function parse(s, arr) {
var month = months$1[arr[2].toLowerCase()];
var year = parseYear(arr[3], s._today);
var obj = {
year: year,
month: month,
date: fns.toCardinal(arr[1] || '')
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s, arr[4]);
return s;
}
}, //alt short format - "feb-25-2015"
{
reg: /^([a-z]+)[\-\/]([0-9]{1,2})[\-\/]?([0-9]{4})?$/i,
parse: function parse(s, arr) {
var month = months$1[arr[1].toLowerCase()];
var year = parseYear(arr[3], s._today);
var obj = {
year: year,
month: month,
date: fns.toCardinal(arr[2] || '')
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s, arr[4]);
return s;
}
}, //Long "Mar 25 2015"
//February 22, 2017 15:30:00
{
reg: /^([a-z]+) ([0-9]{1,2}(?:st|nd|rd|th)?),?( [0-9]{4})?( ([0-9:]+( ?am| ?pm| ?gmt)?))?$/i,
parse: function parse(s, arr) {
var month = months$1[arr[1].toLowerCase()];
var year = parseYear(arr[3], s._today);
var obj = {
year: year,
month: month,
date: fns.toCardinal(arr[2] || '')
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s, arr[4]);
return s;
}
}, //February 2017 (implied date)
{
reg: /^([a-z]+) ([0-9]{4})$/i,
parse: function parse(s, arr) {
var month = months$1[arr[1].toLowerCase()];
var year = parseYear(arr[2], s._today);
var obj = {
year: year,
month: month,
date: s._today.date || 1
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s, arr[4]);
return s;
}
}, //Long "25 Mar 2015"
{
reg: /^([0-9]{1,2}(?:st|nd|rd|th)?) ([a-z]+),?( [0-9]{4})?,? ?([0-9]{1,2}:[0-9]{2}:?[0-9]{0,2}? ?(am|pm|gmt))?$/i,
parse: function parse(s, arr) {
var month = months$1[arr[2].toLowerCase()];
if (!month) {
return null;
}
var year = parseYear(arr[3], s._today);
var obj = {
year: year,
month: month,
date: fns.toCardinal(arr[1])
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s, arr[4]);
return s;
}
}, {
// 'q2 2002'
reg: /^(q[0-9])( of)?( [0-9]{4})?/i,
parse: function parse(s, arr) {
var quarter = arr[1] || '';
s = s.quarter(quarter);
var year = arr[3] || '';
if (year) {
year = year.trim();
s = s.year(year);
}
return s;
}
}, {
// 'summer 2002'
reg: /^(spring|summer|winter|fall|autumn)( of)?( [0-9]{4})?/i,
parse: function parse(s, arr) {
var season = arr[1] || '';
s = s.season(season);
var year = arr[3] || '';
if (year) {
year = year.trim();
s = s.year(year);
}
return s;
}
}, {
// '200bc'
reg: /^[0-9,]+ ?b\.?c\.?$/i,
parse: function parse(s, arr) {
var str = arr[0] || ''; //make negative-year
str = str.replace(/^([0-9,]+) ?b\.?c\.?$/i, '-$1'); //remove commas
str = str.replace(/,/g, '');
var year = parseInt(str.trim(), 10);
var d = new Date();
var obj = {
year: year,
month: d.getMonth(),
date: d.getDate()
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s);
return s;
}
}, {
// '200ad'
reg: /^[0-9,]+ ?(a\.?d\.?|c\.?e\.?)$/i,
parse: function parse(s, arr) {
var str = arr[0] || ''; //remove commas
str = str.replace(/,/g, '');
var year = parseInt(str.trim(), 10);
var d = new Date();
var obj = {
year: year,
month: d.getMonth(),
date: d.getDate()
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s);
return s;
}
}, {
// '1992'
reg: /^[0-9]{4}( ?a\.?d\.?)?$/i,
parse: function parse(s, arr) {
var today = s._today;
var year = parseYear(arr[0], today);
var d = new Date(); // using today's date, but a new month is awkward.
if (today.month && !today.date) {
today.date = 1;
}
var obj = {
year: year,
month: today.month || d.getMonth(),
date: today.date || d.getDate()
};
if (hasDate_1(obj) === false) {
s.epoch = null;
return s;
}
walk_1(s, obj);
s = parseTime_1(s);
return s;
}
}];
var strParse = strFmt; // pull in 'today' data for the baseline moment
var getNow = function getNow(s) {
s.epoch = Date.now();
Object.keys(s._today || {}).forEach(function (k) {
if (typeof s[k] === 'function') {
s = s[k](s._today[k]);
}
});
return s;
};
var dates = {
now: function now(s) {
return getNow(s);
},
today: function today(s) {
return getNow(s);
},
tonight: function tonight(s) {
s = getNow(s);
s = s.hour(18); //6pm
return s;
},
tomorrow: function tomorrow(s) {
s = getNow(s);
s = s.add(1, 'day');
s = s.startOf('day');
return s;
},
yesterday: function yesterday(s) {
s = getNow(s);
s = s.subtract(1, 'day');
s = s.startOf('day');
return s;
},
christmas: function christmas(s) {
var year = getNow(s).year();
s = s.set([year, 11, 25, 18, 0, 0]); // Dec 25
return s;
},
'new years': function newYears(s) {
var year = getNow(s).year();
s = s.set([year, 11, 31, 18, 0, 0]); // Dec 31
return s;
}
};
dates['new years eve'] = dates['new years'];
var namedDates = dates; // - can't use built-in js parser ;(
//=========================================
// ISO Date "2015-03-25"
// Short Date "03/25/2015" or "2015/03/25"
// Long Date "Mar 25 2015" or "25 Mar 2015"
// Full Date "Wednesday March 25 2015"
//=========================================
//-- also -
// if the given epoch is really small, they've probably given seconds and not milliseconds
// anything below this number is likely (but not necessarily) a mistaken input.
// this may seem like an arbitrary number, but it's 'within jan 1970'
// this is only really ambiguous until 2054 or so
var minimumEpoch = 2500000000;
var defaults = {
year: new Date().getFullYear(),
month: 0,
date: 1
}; //support [2016, 03, 01] format
var handleArray = function handleArray(s, arr, today) {
if (arr.length === 0) {
return s;
}
var order = ['year', 'month', 'date', 'hour', 'minute', 'second', 'millisecond'];
for (var i = 0; i < order.length; i++) {
var num = arr[i] || today[order[i]] || defaults[order[i]] || 0;
s = s[order[i]](num);
}
return s;
}; //support {year:2016, month:3} format
var handleObject = function handleObject(s, obj, today) {
// if obj is empty, do nothing
if (Object.keys(obj).length === 0) {
return s;
}
obj = Object.assign({}, defaults, today, obj);
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
var unit = keys[i]; //make sure we have this method
if (s[unit] === undefined || typeof s[unit] !== 'function') {
continue;
} //make sure the value is a number
if (obj[unit] === null || obj[unit] === undefined || obj[unit] === '') {
continue;
}
var num = obj[unit] || today[unit] || defaults[unit] || 0;
s = s[unit](num);
}
return s;
}; //find the epoch from different input styles
var parseInput = function parseInput(s, input, givenTz) {
var today = s._today || defaults; //if we've been given a epoch number, it's easy
if (typeof input === 'number') {
if (input > 0 && input < minimumEpoch && s.silent === false) {
console.warn(' - Warning: You are setting the date to January 1970.');
console.warn(' - did input seconds instead of milliseconds?');
}
s.epoch = input;
return s;
} //set tmp time
s.epoch = Date.now(); // overwrite tmp time with 'today' value, if exists
if (s._today && fns.isObject(s._today) && Object.keys(s._today).length > 0) {
var res = handleObject(s, today, defaults);
if (res.isValid()) {
s.epoch = res.epoch;
}
} // null input means 'now'
if (input === null || input === undefined || input === '') {
return s; //k, we're good.
} //support input of Date() object
if (fns.isDate(input) === true) {
s.epoch = input.getTime();
return s;
} //support [2016, 03, 01] format
if (fns.isArray(input) === true) {
s = handleArray(s, input, today);
return s;
} //support {year:2016, month:3} format
if (fns.isObject(input) === true) {
//support spacetime object as input
if (input.epoch) {
s.epoch = input.epoch;
s.tz = input.tz;
return s;
}
s = handleObject(s, input, today);
return s;
} //input as a string..
if (typeof input !== 'string') {
return s;
} //little cleanup..
input = input.replace(/\b(mon|tues|wed|wednes|thu|thurs|fri|sat|satur|sun)(day)?\b/i, '');
input = input.replace(/,/g, '');
input = input.replace(/ +/g, ' ').trim(); //try some known-words, like 'now'
if (namedDates.hasOwnProperty(input) === true) {
s = namedDates[input](s);
return s;
} //try each text-parse template, use the first good result
for (var i = 0; i < strParse.length; i++) {
var m = input.match(strParse[i].reg);
if (m) {
// console.log(strFmt[i].reg)
var _res = strParse[i].parse(s, m, givenTz);
if (_res !== null && _res.isValid()) {
return _res;
}
}
}
if (s.silent === false) {
console.warn("Warning: couldn't parse date-string: '" + input + "'");
}
s.epoch = null;
return s;
};
var input = parseInput;
var shortDays = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
var longDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
var days = {
"short": function _short2() {
return shortDays;
},
"long": function _long2() {
return longDays;
},
set: function set(i18n) {
shortDays = i18n["short"] || shortDays;
longDays = i18n["long"] || longDays;
},
aliases: {
tues: 2,
thur: 4,
thurs: 4
}
};
var titleCaseEnabled = true;
var caseFormat = {
useTitleCase: function useTitleCase() {
return titleCaseEnabled;
},
set: function set(useTitleCase) {
titleCaseEnabled = useTitleCase;
}
}; // it's kind of nuts how involved this is
// "+01:00", "+0100", or simply "+01"
var isoOffset = function isoOffset(s) {
var offset = s.timezone().current.offset;
return !offset ? 'Z' : fns.formatTimezone(offset, ':');
};
var _offset = isoOffset;
var applyCaseFormat = function applyCaseFormat(str) {
if (caseFormat.useTitleCase()) {
return fns.titleCase(str);
}
return str;
};
var format = {
day: function day(s) {
return applyCaseFormat(s.dayName());
},
'day-short': function dayShort(s) {
return applyCaseFormat(days["short"]()[s.day()]);
},
'day-number': function dayNumber(s) {
return s.day();
},
'day-ordinal': function dayOrdinal(s) {
return fns.ordinal(s.day());
},
'day-pad': function dayPad(s) {
return fns.zeroPad(s.day());
},
date: function date(s) {
return s.date();
},
'date-ordinal': function dateOrdinal(s) {
return fns.ordinal(s.date());
},
'date-pad': function datePad(s) {
return fns.zeroPad(s.date());
},
month: function month(s) {
return applyCaseFormat(s.monthName());
},
'month-short': function monthShort(s) {
return applyCaseFormat(months["short"]()[s.month()]);
},
'month-number': function monthNumber(s) {
return s.month();
},
'month-ordinal': function monthOrdinal(s) {
return fns.ordinal(s.month());
},
'month-pad': function monthPad(s) {
return fns.zeroPad(s.month());
},
'iso-month': function isoMonth(s) {
return fns.zeroPad(s.month() + 1);
},
//1-based months
year: function year(s) {
var year = s.year();
if (year > 0) {
return year;
}
year = Math.abs(year);
return year + ' BC';
},
'year-short': function yearShort(s) {
var year = s.year();
if (year > 0) {
return "'".concat(String(s.year()).substr(2, 4));
}
year = Math.abs(year);
return year + ' BC';
},
'iso-year': function isoYear(s) {
var year = s.year();
var isNegative = year < 0;
var str = fns.zeroPad(Math.abs(year), 4); //0-padded
if (isNegative) {
//negative years are for some reason 6-digits ('-00008')
str = fns.zeroPad(str, 6);
str = '-' + str;
}
return str;
},
time: function time(s) {
return s.time();
},
'time-24': function time24(s) {
return "".concat(s.hour24(), ":").concat(fns.zeroPad(s.minute()));
},
hour: function hour(s) {
return s.hour12();
},
'hour-pad': function hourPad(s) {
return fns.zeroPad(s.hour12());
},
'hour-24': function hour24(s) {
return s.hour24();
},
'hour-24-pad': function hour24Pad(s) {
return fns.zeroPad(s.hour24());
},
minute: function minute(s) {
return s.minute();
},
'minute-pad': function minutePad(s) {
return fns.zeroPad(s.minute());
},
second: function second(s) {
return s.second();
},
'second-pad': function secondPad(s) {
return fns.zeroPad(s.second());
},
ampm: function ampm(s) {
return s.ampm();
},
quarter: function quarter(s) {
return 'Q' + s.quarter();
},
season: function season(s) {
return s.season();
},
era: function era(s) {
return s.era();
},
json: function json(s) {
return s.json();
},
timezone: function timezone(s) {
return s.timezone().name;
},
offset: function offset(s) {
return _offset(s);
},
numeric: function numeric(s) {
return "".concat(s.year(), "/").concat(fns.zeroPad(s.month() + 1), "/").concat(fns.zeroPad(s.date()));
},
// yyyy/mm/dd
'numeric-us': function numericUs(s) {
return "".concat(fns.zeroPad(s.month() + 1), "/").concat(fns.zeroPad(s.date()), "/").concat(s.year());
},
// mm/dd/yyyy
'numeric-uk': function numericUk(s) {
return "".concat(fns.zeroPad(s.date()), "/").concat(fns.zeroPad(s.month() + 1), "/").concat(s.year());
},
//dd/mm/yyyy
'mm/dd': function mmDd(s) {
return "".concat(fns.zeroPad(s.month() + 1), "/").concat(fns.zeroPad(s.date()));
},
//mm/dd
// ... https://en.wikipedia.org/wiki/ISO_8601 ;(((
iso: function iso(s) {
var year = s.format('iso-year');
var month = fns.zeroPad(s.month() + 1); //1-based months
var date = fns.zeroPad(s.date());
var hour = fns.zeroPad(s.h24());
var minute = fns.zeroPad(s.minute());
var second = fns.zeroPad(s.second());
var ms = fns.zeroPad(s.millisecond(), 3);
var offset = _offset(s);
return "".concat(year, "-").concat(month, "-").concat(date, "T").concat(hour, ":").concat(minute, ":").concat(second, ".").concat(ms).concat(offset); //2018-03-09T08:50:00.000-05:00
},
'iso-short': function isoShort(s) {
var month = fns.zeroPad(s.month() + 1); //1-based months
var date = fns.zeroPad(s.date());
return "".concat(s.year(), "-").concat(month, "-").concat(date); //2017-02-15
},
'iso-utc': function isoUtc(s) {
return new Date(s.epoch).toISOString(); //2017-03-08T19:45:28.367Z
},
//i made these up
nice: function nice(s) {
return "".concat(months["short"]()[s.month()], " ").concat(fns.ordinal(s.date()), ", ").concat(s.time());
},
'nice-year': function niceYear(s) {
return "".concat(months["short"]()[s.month()], " ").concat(fns.ordinal(s.date()), ", ").concat(s.year());
},
'nice-day': function niceDay(s) {
return "".concat(days["short"]()[s.day()], " ").concat(applyCaseFormat(months["short"]()[s.month()]), " ").concat(fns.ordinal(s.date()));
},
'nice-full': function niceFull(s) {
return "".concat(s.dayName(), " ").concat(applyCaseFormat(s.monthName()), " ").concat(fns.ordinal(s.date()), ", ").concat(s.time());
}
}; //aliases
var aliases = {
'day-name': 'day',
'month-name': 'month',
'iso 8601': 'iso',
'time-h24': 'time-24',
'time-12': 'time',
'time-h12': 'time',
tz: 'timezone',
'day-num': 'day-number',
'month-num': 'month-number',
'month-iso': 'iso-month',
'year-iso': 'iso-year',
'nice-short': 'nice',
mdy: 'numeric-us',
dmy: 'numeric-uk',
ymd: 'numeric',
'yyyy/mm/dd': 'numeric',
'mm/dd/yyyy': 'numeric-us',
'dd/mm/yyyy': 'numeric-us',
'little-endian': 'numeric-uk',
'big-endian': 'numeric',
'day-nice': 'nice-day'
};
Object.keys(aliases).forEach(function (k) {
return format[k] = format[aliases[k]];
});
var printFormat = function printFormat(s) {
var str = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; //don't print anything if it's an invalid date
if (s.isValid() !== true) {
return '';
} //support .format('month')
if (format.hasOwnProperty(str)) {
var out = format[str](s) || '';
if (str !== 'json') {
out = String(out);
if (str !== 'ampm') {
out = applyCaseFormat(out);
}
}
return out;
} //support '{hour}:{minute}' notation
if (str.indexOf('{') !== -1) {
var sections = /\{(.+?)\}/g;
str = str.replace(sections, function (_, fmt) {
fmt = fmt.toLowerCase().trim();
if (format.hasOwnProperty(fmt)) {
return String(format[fmt](s));
}
return '';
});
return str;
}
return s.format('iso-short');
};
var format_1 = printFormat;
var pad = fns.zeroPad;
var formatTimezone = fns.formatTimezone; //parse this insane unix-time-templating thing, from the 19th century
//http://unicode.org/reports/tr35/tr35-25.html#Date_Format_Patterns
//time-symbols we support
var mapping = {
G: function G(s) {
return s.era();
},
GG: function GG(s) {
return s.era();
},
GGG: function GGG(s) {
return s.era();
},
GGGG: function GGGG(s) {
return s.era() === 'AD' ? 'Anno Domini' : 'Before Christ';
},
//year
y: function y(s) {
return s.year();
},
yy: function yy(s) {
//last two chars
return parseInt(String(s.year()).substr(2, 4), 10);
},
yyy: function yyy(s) {
return s.year();
},
yyyy: function yyyy(s) {
return s.year();
},
yyyyy: function yyyyy(s) {
return '0' + s.year();
},
// u: (s) => {},//extended non-gregorian years
//quarter
Q: function Q(s) {
return s.quarter();
},
QQ: function QQ(s) {
return s.quarter();
},
QQQ: function QQQ(s) {
return s.quarter();
},
QQQQ: function QQQQ(s) {
return s.quarter();
},
//month
M: function M(s) {
return s.month() + 1;
},
MM: function MM(s) {
return pad(s.month() + 1);
},
MMM: function MMM(s) {
return s.format('month-short');
},
MMMM: function MMMM(s) {
return s.format('month');
},
//week
w: function w(s) {
return s.week();
},
ww: function ww(s) {
return pad(s.week());
},
//week of month
// W: (s) => s.week(),
//date of month
d: function d(s) {
return s.date();
},
dd: function dd(s) {
return pad(s.date());
},
//date of year
D: function D(s) {
return s.dayOfYear();
},
DD: function DD(s) {
return pad(s.dayOfYear());
},
DDD: function DDD(s) {
return pad(s.dayOfYear(), 3);
},
// F: (s) => {},//date of week in month
// g: (s) => {},//modified julian day
//day
E: function E(s) {
return s.format('day-short');
},
EE: function EE(s) {
return s.format('day-short');
},
EEE: function EEE(s) {
return s.format('day-short');
},
EEEE: function EEEE(s) {
return s.format('day');
},
EEEEE: function EEEEE(s) {
return s.format('day')[0];
},
e: function e(s) {
return s.day();
},
ee: function ee(s) {
return s.day();
},
eee: function eee(s) {
return s.format('day-short');
},
eeee: function eeee(s) {
return s.format('day');
},
eeeee: function eeeee(s) {
return s.format('day')[0];
},
//am/pm
a: function a(s) {
return s.ampm().toUpperCase();
},
aa: function aa(s) {
return s.ampm().toUpperCase();
},
aaa: function aaa(s) {
return s.ampm().toUpperCase();
},
aaaa: function aaaa(s) {
return s.ampm().toUpperCase();
},
//hour
h: function h(s) {
return s.h12();
},
hh: function hh(s) {
return pad(s.h12());
},
H: function H(s) {
return s.hour();
},
HH: function HH(s) {
return pad(s.hour());
},
// j: (s) => {},//weird hour format
m: function m(s) {
return s.minute();
},
mm: function mm(s) {
return pad(s.minute());
},
s: function s(_s) {
return _s.second();
},
ss: function ss(s) {
return pad(s.second());
},
//milliseconds in the day
A: function A(s) {
return s.epoch - s.startOf('day').epoch;
},
//timezone
z: function z(s) {
return s.timezone().name;
},
zz: function zz(s) {
return s.timezone().name;
},
zzz: function zzz(s) {
return s.timezone().name;
},
zzzz: function zzzz(s) {
return s.timezone().name;
},
Z: function Z(s) {
return formatTimezone(s.timezone().current.offset);
},
ZZ: function ZZ(s) {
return formatTimezone(s.timezone().current.offset);
},
ZZZ: function ZZZ(s) {
return formatTimezone(s.timezone().current.offset);
},
ZZZZ: function ZZZZ(s) {
return formatTimezone(s.timezone().current.offset, ':');
}
};
var addAlias = function addAlias(_char, to, n) {
var name = _char;
var toName = to;
for (var i = 0; i < n; i += 1) {
mapping[name] = mapping[toName];
name += _char;
toName += to;
}
};
addAlias('q', 'Q', 4);
addAlias('L', 'M', 4);
addAlias('Y', 'y', 4);
addAlias('c', 'e', 4);
addAlias('k', 'H', 2);
addAlias('K', 'h', 2);
addAlias('S', 's', 2);
addAlias('v', 'z', 4);
addAlias('V', 'Z', 4); // support unix-style escaping with ' character
var escapeChars = function escapeChars(arr) {
for (var i = 0; i < arr.length; i += 1) {
if (arr[i] === "'") {
// greedy-search for next apostrophe
for (var o = i + 1; o < arr.length; o += 1) {
if (arr[o]) {
arr[i] += arr[o];
}
if (arr[o] === "'") {
arr[o] = null;
break;
}
arr[o] = null;
}
}
}
return arr.filter(function (ch) {
return ch;
});
}; //combine consecutive chars, like 'yyyy' as one.
var combineRepeated = function combineRepeated(arr) {
for (var i = 0; i < arr.length; i += 1) {
var c = arr[i]; // greedy-forward
for (var o = i + 1; o < arr.length; o += 1) {
if (arr[o] === c) {
arr[i] += arr[o];
arr[o] = null;
} else {
break;
}
}
} // '' means one apostrophe
arr = arr.filter(function (ch) {
return ch;
});
arr = arr.map(function (str) {
if (str === "''") {
str = "'";
}
return str;
});
return arr;
};
var unixFmt = function unixFmt(s, str) {
var arr = str.split(''); // support character escaping
arr = escapeChars(arr); //combine 'yyyy' as string.
arr = combineRepeated(arr);
return arr.reduce(function (txt, c) {
if (mapping[c] !== undefined) {
txt += mapping[c](s) || '';
} else {
// 'unescape'
if (/^'.{1,}'$/.test(c)) {
c = c.replace(/'/g, '');
}
txt += c;
}
return txt;
}, '');
};
var unixFmt_1 = unixFmt;
var units$1 = ['year', 'season', 'quarter', 'month', 'week', 'day', 'quarterHour', 'hour', 'minute'];
var doUnit = function doUnit(s, k) {
var start = s.clone().startOf(k);
var end = s.clone().endOf(k);
var duration = end.epoch - start.epoch;
var percent = (s.epoch - start.epoch) / duration;
return parseFloat(percent.toFixed(2));
}; //how far it is along, from 0-1
var progress = function progress(s, unit) {
if (unit) {
unit = fns.normalize(unit);
return doUnit(s, unit);
}
var obj = {};
units$1.forEach(function (k) {
obj[k] = doUnit(s, k);
});
return obj;
};
var progress_1 = progress;
var nearest = function nearest(s, unit) {
//how far have we gone?
var prog = s.progress();
unit = fns.normalize(unit); //fix camel-case for this one
if (unit === 'quarterhour') {
unit = 'quarterHour';
}
if (prog[unit] !== undefined) {
// go forward one?
if (prog[unit] > 0.5) {
s = s.add(1, unit);
} // go to start
s = s.startOf(unit);
} else if (s.silent === false) {
console.warn("no known unit '" + unit + "'");
}
return s;
};
var nearest_1 = nearest; //increment until dates are the same
var climb = function climb(a, b, unit) {
var i = 0;
a = a.clone();
while (a.isBefore(b)) {
//do proper, expensive increment to catch all-the-tricks
a = a.add(1, unit);
i += 1;
} //oops, we went too-far..
if (a.isAfter(b, unit)) {
i -= 1;
}
return i;
}; // do a thurough +=1 on the unit, until they match
// for speed-reasons, only used on day, month, week.
var diffOne = function diffOne(a, b, unit) {
if (a.isBefore(b)) {
return climb(a, b, unit);
} else {
return climb(b, a, unit) * -1; //reverse it
}
};
var one = diffOne; // 2020 - 2019 may be 1 year, or 0 years
// - '1 year difference' means 366 days during a leap year
var fastYear = function fastYear(a, b) {
var years = b.year() - a.year(); // should we decrement it by 1?
a = a.year(b.year());
if (a.isAfter(b)) {
years -= 1;
}
return years;
}; // use a waterfall-method for computing a diff of any 'pre-knowable' units
// compute years, then compute months, etc..
// ... then ms-math for any very-small units
var diff = function diff(a, b) {
// an hour is always the same # of milliseconds
// so these units can be 'pre-calculated'
var msDiff = b.epoch - a.epoch;
var obj = {
milliseconds: msDiff,
seconds: parseInt(msDiff / 1000, 10)
};
obj.minutes = parseInt(obj.seconds / 60, 10);
obj.hours = parseInt(obj.minutes / 60, 10); //do the year
var tmp = a.clone();
obj.years = fastYear(tmp, b);
tmp = a.add(obj.years, 'year'); //there's always 12 months in a year...
obj.months = obj.years * 12;
tmp = a.add(obj.months, 'month');
obj.months += one(tmp, b, 'month'); // there's always atleast 52 weeks in a year..
// (month * 4) isn't as close
obj.weeks = obj.years * 52;
tmp = a.add(obj.weeks, 'week');
obj.weeks += one(tmp, b, 'week'); // there's always atleast 7 days in a week
obj.days = obj.weeks * 7;
tmp = a.add(obj.days, 'day');
obj.days += one(tmp, b, 'day');
return obj;
};
var waterfall = diff;
var reverseDiff = function reverseDiff(obj) {
Object.keys(obj).forEach(function (k) {
obj[k] *= -1;
});
return obj;
}; // this method counts a total # of each unit, between a, b.
// '1 month' means 28 days in february
// '1 year' means 366 days in a leap year
var main = function main(a, b, unit) {
b = fns.beADate(b, a); //reverse values, if necessary
var reversed = false;
if (a.isAfter(b)) {
var tmp = a;
a = b;
b = tmp;
reversed = true;
} //compute them all (i know!)
var obj = waterfall(a, b);
if (reversed) {
obj = reverseDiff(obj);
} //return just the requested unit
if (unit) {
//make sure it's plural-form
unit = fns.normalize(unit);
if (/s$/.test(unit) !== true) {
unit += 's';
}
if (unit === 'dates') {
unit = 'days';
}
return obj[unit];
}
return obj;
};
var diff$1 = main; //our conceptual 'break-points' for each unit
var qualifiers = {
months: {
almost: 10,
over: 4
},
days: {
almost: 25,
over: 10
},
hours: {
almost: 20,
over: 8
},
minutes: {
almost: 50,
over: 20
},
seconds: {
almost: 50,
over: 20
}
}; //get number of hours/minutes... between the two dates
function getDiff(a, b) {
var isBefore = a.isBefore(b);
var later = isBefore ? b : a;
var earlier = isBefore ? a : b;
earlier = earlier.clone();
var diff = {
years: 0,
months: 0,
days: 0,
hours: 0,
minutes: 0,
seconds: 0
};
Object.keys(diff).forEach(function (unit) {
if (earlier.isSame(later, unit)) {
return;
}
var max = earlier.diff(later, unit);
earlier = earlier.add(max, unit);
diff[unit] = max;
}); //reverse it, if necessary
if (isBefore) {
Object.keys(diff).forEach(function (u) {
if (diff[u] !== 0) {
diff[u] *= -1;
}
});
}
return diff;
} // Expects a plural unit arg
function pluralize(value, unit) {
if (value === 1) {
unit = unit.slice(0, -1);
}
return value + ' ' + unit;
} //create the human-readable diff between the two dates
var since = function since(start, end) {
end = fns.beADate(end, start);
var diff = getDiff(start, end);
var isNow = Object.keys(diff).every(function (u) {
return !diff[u];
});
if (isNow === true) {
return {
diff: diff,
rounded: 'now',
qualified: 'now',
precise: 'now'
};
}
var rounded;
var qualified;
var precise;
var englishValues = []; //go through each value and create its text-representation
Object.keys(diff).forEach(function (unit, i, units) {
var value = Math.abs(diff[unit]);
if (value === 0) {
return;
}
var englishValue = pluralize(value, unit);
englishValues.push(englishValue);
if (!rounded) {
rounded = qualified = englishValue;
if (i > 4) {
return;
} //is it a 'almost' something, etc?
var nextUnit = units[i + 1];
var nextValue = Math.abs(diff[nextUnit]);
if (nextValue > qualifiers[nextUnit].almost) {
rounded = pluralize(value + 1, unit);
qualified = 'almost ' + rounded;
} else if (nextValue > qualifiers[nextUnit].over) qualified = 'over ' + englishValue;
}
}); //make them into a string
precise = englishValues.splice(0, 2).join(', '); //handle before/after logic
if (start.isAfter(end) === true) {
rounded += ' ago';
qualified += ' ago';
precise += ' ago';
} else {
rounded = 'in ' + rounded;
qualified = 'in ' + qualified;
precise = 'in ' + precise;
}
return {
diff: diff,
rounded: rounded,
qualified: qualified,
precise: precise
};
};
var since_1 = since; //https://www.timeanddate.com/calendar/aboutseasons.html
// Spring - from March 1 to May 31;
// Summer - from June 1 to August 31;
// Fall (autumn) - from September 1 to November 30; and,
// Winter - from December 1 to February 28 (February 29 in a leap year).
var seasons = {
north: [['spring', 2, 1], //spring march 1
['summer', 5, 1], //june 1
['fall', 8, 1], //sept 1
['autumn', 8, 1], //sept 1
['winter', 11, 1] //dec 1
],
south: [['fall', 2, 1], //march 1
['autumn', 2, 1], //march 1
['winter', 5, 1], //june 1
['spring', 8, 1], //sept 1
['summer', 11, 1] //dec 1
]
};
var quarters = [null, [0, 1], //jan 1
[3, 1], //apr 1
[6, 1], //july 1
[9, 1] //oct 1
];
var units$2 = {
minute: function minute(s) {
walk_1(s, {
second: 0,
millisecond: 0
});
return s;
},
quarterhour: function quarterhour(s) {
var minute = s.minutes();
if (minute >= 45) {
s = s.minutes(45);
} else if (minute >= 30) {
s = s.minutes(30);
} else if (minute >= 15) {
s = s.minutes(15);
} else {
s = s.minutes(0);
}
walk_1(s, {
second: 0,
millisecond: 0
});
return s;
},
hour: function hour(s) {
walk_1(s, {
minute: 0,
second: 0,
millisecond: 0
});
return s;
},
day: function day(s) {
walk_1(s, {
hour: 0,
minute: 0,
second: 0,
millisecond: 0
});
return s;
},
week: function week(s) {
var original = s.clone();
s = s.day(s._weekStart); //monday
if (s.isAfter(original)) {
s = s.subtract(1, 'week');
}
walk_1(s, {
hour: 0,
minute: 0,
second: 0,
millisecond: 0
});
return s;
},
month: function month(s) {
walk_1(s, {
date: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0
});
return s;
},
quarter: function quarter(s) {
var q = s.quarter();
if (quarters[q]) {
walk_1(s, {
month: quarters[q][0],
date: quarters[q][1],
hour: 0,
minute: 0,
second: 0,
millisecond: 0
});
}
return s;
},
season: function season(s) {
var current = s.season();
var hem = 'north';
if (s.hemisphere() === 'South') {
hem = 'south';
}
for (var i = 0; i < seasons[hem].length; i++) {
if (seasons[hem][i][0] === current) {
//winter goes between years
var year = s.year();
if (current === 'winter' && s.month() < 3) {
year -= 1;
}
walk_1(s, {
year: year,
month: seasons[hem][i][1],
date: seasons[hem][i][2],
hour: 0,
minute: 0,
second: 0,
millisecond: 0
});
return s;
}
}
return s;
},
year: function year(s) {
walk_1(s, {
month: 0,
date: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0
});
return s;
},
decade: function decade(s) {
s = s.startOf('year');
var year = s.year();
var decade = parseInt(year / 10, 10) * 10;
s = s.year(decade);
return s;
},
century: function century(s) {
s = s.startOf('year');
var year = s.year(); // near 0AD goes '-1 | +1'
var decade = parseInt(year / 100, 10) * 100;
s = s.year(decade);
return s;
}
};
units$2.date = units$2.day;
var startOf = function startOf(a, unit) {
var s = a.clone();
unit = fns.normalize(unit);
if (units$2[unit]) {
return units$2[unit](s);
}
if (unit === 'summer' || unit === 'winter') {
s = s.season(unit);
return units$2.season(s);
}
return s;
}; //piggy-backs off startOf
var endOf = function endOf(a, unit) {
var s = a.clone();
unit = fns.normalize(unit);
if (units$2[unit]) {
s = units$2[unit](s); // startof
s = s.add(1, unit);
s = s.subtract(1, 'milliseconds');
return s;
}
return s;
};
var startOf_1 = {
startOf: startOf,
endOf: endOf
};
var isDay = function isDay(unit) {
if (days["short"]().find(function (s) {
return s === unit;
})) {
return true;
}
if (days["long"]().find(function (s) {
return s === unit;
})) {
return true;
}
return false;
}; // return a list of the weeks/months/days between a -> b
// returns spacetime objects in the timezone of the input
var every = function every(start) {
var unit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var end = arguments.length > 2 ? arguments[2] : undefined;
if (!unit || !end) {
return [];
} //cleanup unit param
unit = fns.normalize(unit); //cleanup to param
end = start.clone().set(end); //swap them, if they're backwards
if (start.isAfter(end)) {
var tmp = start;
start = end;
end = tmp;
} //support 'every wednesday'
var d = start.clone();
if (isDay(unit)) {
d = d.next(unit);
unit = 'week';
} else {
d = d.next(unit);
} //okay, actually start doing it
var result = [];
while (d.isBefore(end)) {
result.push(d);
d = d.add(1, unit);
}
return result;
};
var every_1 = every;
var parseDst = function parseDst(dst) {
if (!dst) {
return [];
}
return dst.split('->');
};
var titleCase = function titleCase(str) {
str = str[0].toUpperCase() + str.substr(1);
str = str.replace(/\/gmt/, '/GMT');
str = str.replace(/[\/_]([a-z])/gi, function (s) {
return s.toUpperCase();
});
return str;
}; //get metadata about this timezone
var timezone = function timezone(s) {
var zones = s.timezones;
var tz = s.tz;
if (zones.hasOwnProperty(tz) === false) {
tz = find(s.tz, zones);
}
if (tz === null) {
if (s.silent === false) {
console.warn("Warn: could not find given or local timezone - '" + s.tz + "'");
}
return {
current: {
epochShift: 0
}
};
}
var found = zones[tz];
var result = {
name: titleCase(tz),
hasDst: Boolean(found.dst),
default_offset: found.offset,
//do north-hemisphere version as default (sorry!)
hemisphere: found.hem === 's' ? 'South' : 'North',
current: {}
};
if (result.hasDst) {
var arr = parseDst(found.dst);
result.change = {
start: arr[0],
back: arr[1]
};
} //find the offsets for summer/winter times
//(these variable names are north-centric)
var summer = found.offset; // (july)
var winter = summer; // (january) assume it's the same for now
if (result.hasDst === true) {
if (result.hemisphere === 'North') {
winter = summer - 1;
} else {
//southern hemisphere
winter = found.offset + 1;
}
} //find out which offset to use right now
//use 'summer' time july-time
if (result.hasDst === false) {
result.current.offset = summer;
result.current.isDST = false;
} else if (summerTime(s.epoch, result.change.start, result.change.back, summer, winter) === true) {
result.current.offset = summer;
result.current.isDST = result.hemisphere === 'North'; //dst 'on' in winter in north
} else {
//use 'winter' january-time
result.current.offset = winter;
result.current.isDST = result.hemisphere === 'South'; //dst 'on' in summer in south
}
return result;
};
var timezone_1 = timezone;
var units$3 = ['century', 'decade', 'year', 'month', 'date', 'day', 'hour', 'minute', 'second', 'millisecond']; //the spacetime instance methods (also, the API)
var methods = {
set: function set(input$1, tz) {
var s = this.clone();
s = input(s, input$1, null);
if (tz) {
this.tz = find(tz);
}
return s;
},
timezone: function timezone() {
return timezone_1(this);
},
isDST: function isDST() {
return timezone_1(this).current.isDST;
},
hasDST: function hasDST() {
return timezone_1(this).hasDst;
},
offset: function offset() {
return timezone_1(this).current.offset * 60;
},
hemisphere: function hemisphere() {
return timezone_1(this).hemisphere;
},
format: function format(fmt) {
return format_1(this, fmt);
},
unixFmt: function unixFmt(fmt) {
return unixFmt_1(this, fmt);
},
startOf: function startOf(unit) {
return startOf_1.startOf(this, unit);
},
endOf: function endOf(unit) {
return startOf_1.endOf(this, unit);
},
leapYear: function leapYear() {
var year = this.year();
return fns.isLeapYear(year);
},
progress: function progress(unit) {
return progress_1(this, unit);
},
nearest: function nearest(unit) {
return nearest_1(this, unit);
},
diff: function diff(d, unit) {
return diff$1(this, d, unit);
},
since: function since(d) {
if (!d) {
d = this.clone().set();
}
return since_1(this, d);
},
next: function next(unit) {
var s = this.add(1, unit);
return s.startOf(unit);
},
//the start of the previous year/week/century
last: function last(unit) {
var s = this.subtract(1, unit);
return s.startOf(unit);
},
isValid: function isValid() {
//null/undefined epochs
if (!this.epoch && this.epoch !== 0) {
return false;
}
return !isNaN(this.d.getTime());
},
//travel to this timezone
"goto": function _goto(tz) {
var s = this.clone();
s.tz = find(tz, s.timezones); //science!
return s;
},
//get each week/month/day between a -> b
every: function every(unit, to) {
return every_1(this, unit, to);
},
isAwake: function isAwake() {
var hour = this.hour(); //10pm -> 8am
if (hour < 8 || hour > 22) {
return false;
}
return true;
},
isAsleep: function isAsleep() {
return !this.isAwake();
},
//pretty-printing
log: function log() {
console.log('');
console.log(format_1(this, 'nice-short'));
return this;
},
logYear: function logYear() {
console.log('');
console.log(format_1(this, 'full-short'));
return this;
},
json: function json() {
var _this = this;
return units$3.reduce(function (h, unit) {
h[unit] = _this[unit]();
return h;
}, {});
},
debug: function debug() {
var tz = this.timezone();
var date = this.format('MM') + ' ' + this.format('date-ordinal') + ' ' + this.year();
date += '\n - ' + this.format('time');
console.log('\n\n', date + '\n - ' + tz.name + ' (' + tz.current.offset + ')');
return this;
},
//alias of 'since' but opposite - like moment.js
from: function from(d) {
d = this.clone().set(d);
return d.since(this);
},
fromNow: function fromNow() {
var d = this.clone().set(Date.now());
return d.since(this);
},
weekStart: function weekStart(input) {
//accept a number directly
if (typeof input === 'number') {
this._weekStart = input;
return this;
}
if (typeof input === 'string') {
// accept 'wednesday'
input = input.toLowerCase().trim();
var num = days["short"]().indexOf(input);
if (num === -1) {
num = days["long"]().indexOf(input);
}
if (num === -1) {
num = 1; //go back to default
}
this._weekStart = num;
} else {
console.warn('Spacetime Error: Cannot understand .weekStart() input:', input);
}
return this;
}
}; // aliases
methods.inDST = methods.isDST;
methods.round = methods.nearest;
methods.each = methods.every;
var methods_1 = methods; //these methods wrap around them.
var isLeapYear$1 = fns.isLeapYear;
var validate = function validate(n) {
//handle number as a string
if (typeof n === 'string') {
n = parseInt(n, 10);
}
return n;
};
var order = ['year', 'month', 'date', 'hour', 'minute', 'second', 'millisecond']; //reduce hostile micro-changes when moving dates by millisecond
var confirm = function confirm(s, tmp, unit) {
var n = order.indexOf(unit);
var arr = order.slice(n, order.length);
for (var i = 0; i < arr.length; i++) {
var want = tmp[arr[i]]();
s[arr[i]](want);
}
return s;
};
var set = {
milliseconds: function milliseconds(s, n) {
n = validate(n);
var current = s.millisecond();
var diff = current - n; //milliseconds to shift by
return s.epoch - diff;
},
seconds: function seconds(s, n) {
n = validate(n);
var diff = s.second() - n;
var shift = diff * milliseconds.second;
return s.epoch - shift;
},
minutes: function minutes(s, n) {
n = validate(n);
var old = s.clone();
var diff = s.minute() - n;
var shift = diff * milliseconds.minute;
s.epoch -= shift; // check against a screw-up
// if (old.hour() != s.hour()) {
// walkTo(old, {
// minute: n
// })
// return old.epoch
// }
confirm(s, old, 'second');
return s.epoch;
},
hours: function hours(s, n) {
n = validate(n);
if (n >= 24) {
n = 24;
} else if (n < 0) {
n = 0;
}
var old = s.clone();
var diff = s.hour() - n;
var shift = diff * milliseconds.hour;
s.epoch -= shift; // oops, did we change the day?
if (s.date() !== old.date()) {
s = old.clone();
if (diff > 1) {
diff -= 1;
}
if (diff < 1) {
diff += 1;
}
shift = diff * milliseconds.hour;
s.epoch -= shift;
}
walk_1(s, {
hour: n
});
confirm(s, old, 'minute');
return s.epoch;
},
//support setting time by '4:25pm' - this isn't very-well developed..
time: function time(s, str) {
var m = str.match(/([0-9]{1,2})[:h]([0-9]{1,2})(:[0-9]{1,2})? ?(am|pm)?/);
if (!m) {
//fallback to support just '2am'
m = str.match(/([0-9]{1,2}) ?(am|pm)/);
if (!m) {
return s.epoch;
}
m.splice(2, 0, '0'); //add implicit 0 minutes
m.splice(3, 0, ''); //add implicit seconds
}
var h24 = false;
var hour = parseInt(m[1], 10);
var minute = parseInt(m[2], 10);
if (hour > 12) {
h24 = true;
} //make the hour into proper 24h time
if (h24 === false) {
if (m[4] === 'am' && hour === 12) {
//12am is midnight
hour = 0;
}
if (m[4] === 'pm' && hour < 12) {
//12pm is noon
hour += 12;
}
} // handle seconds
m[3] = m[3] || '';
m[3] = m[3].replace(/:/, '');
var sec = parseInt(m[3], 10) || 0;
s = s.hour(hour);
s = s.minute(minute);
s = s.second(sec);
s = s.millisecond(0);
return s.epoch;
},
date: function date(s, n) {
n = validate(n); //avoid setting february 31st
if (n > 28) {
var month = s.month();
var max = monthLengths_1[month]; // support leap day in february
if (month === 1 && n === 29 && isLeapYear$1(s.year())) {
max = 29;
}
if (n > max) {
n = max;
}
} //avoid setting < 0
if (n <= 0) {
n = 1;
}
walk_1(s, {
date: n
});
return s.epoch;
},
//this one's tricky
month: function month(s, n) {
if (typeof n === 'string') {
n = months.mapping()[n.toLowerCase()];
}
n = validate(n); //don't go past december
if (n >= 12) {
n = 11;
}
if (n <= 0) {
n = 0;
}
var date = s.date(); //there's no 30th of february, etc.
if (date > monthLengths_1[n]) {
//make it as close as we can..
date = monthLengths_1[n];
}
walk_1(s, {
month: n,
date: date
});
return s.epoch;
},
year: function year(s, n) {
// support '97
if (typeof n === 'string' && /^'[0-9]{2}$/.test(n)) {
n = n.replace(/'/, '').trim();
n = Number(n); // '89 is 1989
if (n > 30) {
//change this in 10y
n = 1900 + n;
} else {
// '12 is 2012
n = 2000 + n;
}
}
n = validate(n);
walk_1(s, {
year: n
});
return s.epoch;
},
dayOfYear: function dayOfYear(s, n) {
n = validate(n);
var old = s.clone();
n -= 1; //days are 1-based
if (n <= 0) {
n = 0;
} else if (n >= 365) {
n = 364;
}
s = s.startOf('year');
s = s.add(n, 'day');
confirm(s, old, 'hour');
return s.epoch;
}
};
var methods$1 = {
millisecond: function millisecond(num) {
if (num !== undefined) {
var s = this.clone();
s.epoch = set.milliseconds(s, num);
return s;
}
return this.d.getMilliseconds();
},
second: function second(num) {
if (num !== undefined) {
var s = this.clone();
s.epoch = set.seconds(s, num);
return s;
}
return this.d.getSeconds();
},
minute: function minute(num) {
if (num !== undefined) {
var s = this.clone();
s.epoch = set.minutes(s, num);
return s;
}
return this.d.getMinutes();
},
hour: function hour(num) {
var d = this.d;
if (num !== undefined) {
var s = this.clone();
s.epoch = set.hours(s, num);
return s;
}
return d.getHours();
},
//'3:30' is 3.5
hourFloat: function hourFloat(num) {
if (num !== undefined) {
var s = this.clone();
var _minute = num % 1;
_minute = _minute * 60;
var _hour = parseInt(num, 10);
s.epoch = set.hours(s, _hour);
s.epoch = set.minutes(s, _minute);
return s;
}
var d = this.d;
var hour = d.getHours();
var minute = d.getMinutes();
minute = minute / 60;
return hour + minute;
},
// hour in 12h format
hour12: function hour12(str) {
var d = this.d;
if (str !== undefined) {
var s = this.clone();
str = '' + str;
var m = str.match(/^([0-9]+)(am|pm)$/);
if (m) {
var hour = parseInt(m[1], 10);
if (m[2] === 'pm') {
hour += 12;
}
s.epoch = set.hours(s, hour);
}
return s;
} //get the hour
var hour12 = d.getHours();
if (hour12 > 12) {
hour12 = hour12 - 12;
}
if (hour12 === 0) {
hour12 = 12;
}
return hour12;
},
//some ambiguity here with 12/24h
time: function time(str) {
if (str !== undefined) {
var s = this.clone();
str = str.toLowerCase().trim();
s.epoch = set.time(s, str);
return s;
}
return "".concat(this.h12(), ":").concat(fns.zeroPad(this.minute())).concat(this.ampm());
},
// either 'am' or 'pm'
ampm: function ampm(input) {
var which = 'am';
var hour = this.hour();
if (hour >= 12) {
which = 'pm';
}
if (typeof input !== 'string') {
return which;
} //okay, we're doing a setter
var s = this.clone();
input = input.toLowerCase().trim(); //ampm should never change the day
// - so use `.hour(n)` instead of `.minus(12,'hour')`
if (hour >= 12 && input === 'am') {
//noon is 12pm
hour -= 12;
return s.hour(hour);
}
if (hour < 12 && input === 'pm') {
hour += 12;
return s.hour(hour);
}
return s;
},
//some hard-coded times of day, like 'noon'
dayTime: function dayTime(str) {
if (str !== undefined) {
var times = {
morning: '7:00am',
breakfast: '7:00am',
noon: '12:00am',
lunch: '12:00pm',
afternoon: '2:00pm',
evening: '6:00pm',
dinner: '6:00pm',
night: '11:00pm',
midnight: '23:59pm'
};
var s = this.clone();
str = str || '';
str = str.toLowerCase();
if (times.hasOwnProperty(str) === true) {
s = s.time(times[str]);
}
return s;
}
var h = this.hour();
if (h < 6) {
return 'night';
}
if (h < 12) {
//until noon
return 'morning';
}
if (h < 17) {
//until 5pm
return 'afternoon';
}
if (h < 22) {
//until 10pm
return 'evening';
}
return 'night';
},
//parse a proper iso string
iso: function iso(num) {
if (num !== undefined) {
return this.set(num);
}
return this.format('iso');
}
};
var _01Time = methods$1;
var methods$2 = {
// # day in the month
date: function date(num) {
if (num !== undefined) {
var s = this.clone();
s.epoch = set.date(s, num);
return s;
}
return this.d.getDate();
},
//like 'wednesday' (hard!)
day: function day(input) {
if (input === undefined) {
return this.d.getDay();
}
var original = this.clone();
var want = input; // accept 'wednesday'
if (typeof input === 'string') {
input = input.toLowerCase();
if (days.aliases.hasOwnProperty(input)) {
want = days.aliases[input];
} else {
want = days["short"]().indexOf(input);
if (want === -1) {
want = days["long"]().indexOf(input);
}
}
} //move approx
var day = this.d.getDay();
var diff = day - want;
var s = this.subtract(diff, 'days'); //tighten it back up
walk_1(s, {
hour: original.hour(),
minute: original.minute(),
second: original.second()
});
return s;
},
//these are helpful name-wrappers
dayName: function dayName(input) {
if (input === undefined) {
return days["long"]()[this.day()];
}
var s = this.clone();
s = s.day(input);
return s;
},
//either name or number
month: function month(input) {
if (input !== undefined) {
var s = this.clone();
s.epoch = set.month(s, input);
return s;
}
return this.d.getMonth();
}
};
var _02Date = methods$2;
var clearMinutes = function clearMinutes(s) {
s = s.minute(0);
s = s.second(0);
s = s.millisecond(1);
return s;
};
var methods$3 = {
// day 0-366
dayOfYear: function dayOfYear(num) {
if (num !== undefined) {
var s = this.clone();
s.epoch = set.dayOfYear(s, num);
return s;
} //days since newyears - jan 1st is 1, jan 2nd is 2...
var sum = 0;
var month = this.d.getMonth();
var tmp; //count the num days in each month
for (var i = 1; i <= month; i++) {
tmp = new Date();
tmp.setDate(1);
tmp.setFullYear(this.d.getFullYear()); //the year matters, because leap-years
tmp.setHours(1);
tmp.setMinutes(1);
tmp.setMonth(i);
tmp.setHours(-2); //the last day of the month
sum += tmp.getDate();
}
return sum + this.d.getDate();
},
//since the start of the year
week: function week(num) {
// week-setter
if (num !== undefined) {
var s = this.clone();
s = s.month(0);
s = s.date(1);
s = s.day('monday');
s = clearMinutes(s); //first week starts first Thurs in Jan
// so mon dec 28th is 1st week
// so mon dec 29th is not the week
if (s.monthName() === 'december' && s.date() >= 28) {
s = s.add(1, 'week');
}
num -= 1; //1-based
s = s.add(num, 'weeks');
return s;
} //find-out which week it is
var tmp = this.clone();
tmp = tmp.month(0);
tmp = tmp.date(1);
tmp = clearMinutes(tmp);
tmp = tmp.day('monday'); //don't go into last-year
if (tmp.monthName() === 'december' && tmp.date() >= 28) {
tmp = tmp.add(1, 'week');
} // is first monday the 1st?
var toAdd = 1;
if (tmp.date() === 1) {
toAdd = 0;
}
tmp = tmp.minus(1, 'second');
var thisOne = this.epoch; //if the week technically hasn't started yet
if (tmp.epoch > thisOne) {
return 1;
} //speed it up, if we can
var i = 0;
var skipWeeks = this.month() * 4;
tmp.epoch += milliseconds.week * skipWeeks;
i += skipWeeks;
for (; i < 52; i++) {
if (tmp.epoch > thisOne) {
return i + toAdd;
}
tmp = tmp.add(1, 'week');
}
return 52;
},
//'january'
monthName: function monthName(input) {
if (input === undefined) {
return months["long"]()[this.month()];
}
var s = this.clone();
s = s.month(input);
return s;
},
//q1, q2, q3, q4
quarter: function quarter(num) {
if (num !== undefined) {
if (typeof num === 'string') {
num = num.replace(/^q/i, '');
num = parseInt(num, 10);
}
if (quarters[num]) {
var s = this.clone();
var _month = quarters[num][0];
s = s.month(_month);
s = s.date(1);
s = s.startOf('day');
return s;
}
}
var month = this.d.getMonth();
for (var i = 1; i < quarters.length; i++) {
if (month < quarters[i][0]) {
return i - 1;
}
}
return 4;
},
//spring, summer, winter, fall
season: function season(input) {
var hem = 'north';
if (this.hemisphere() === 'South') {
hem = 'south';
}
if (input !== undefined) {
var s = this.clone();
for (var i = 0; i < seasons[hem].length; i++) {
if (input === seasons[hem][i][0]) {
s = s.month(seasons[hem][i][1]);
s = s.date(1);
s = s.startOf('day');
}
}
return s;
}
var month = this.d.getMonth();
for (var _i = 0; _i < seasons[hem].length - 1; _i++) {
if (month >= seasons[hem][_i][1] && month < seasons[hem][_i + 1][1]) {
return seasons[hem][_i][0];
}
}
return 'winter';
},
//the year number
year: function year(num) {
if (num !== undefined) {
var s = this.clone();
s.epoch = set.year(s, num);
return s;
}
return this.d.getFullYear();
},
//bc/ad years
era: function era(str) {
if (str !== undefined) {
var s = this.clone();
str = str.toLowerCase(); //TODO: there is no year-0AD i think. may have off-by-1 error here
var year = s.d.getFullYear(); //make '1992' into 1992bc..
if (str === 'bc' && year > 0) {
s.epoch = set.year(s, year * -1);
} //make '1992bc' into '1992'
if (str === 'ad' && year < 0) {
s.epoch = set.year(s, year * -1);
}
return s;
}
if (this.d.getFullYear() < 0) {
return 'BC';
}
return 'AD';
},
// 2019 -> 2010
decade: function decade(input) {
if (input !== undefined) {
input = String(input);
input = input.replace(/([0-9])'?s$/, '$1'); //1950's
input = input.replace(/([0-9])(th|rd|st|nd)/, '$1'); //fix ordinals
if (!input) {
console.warn('Spacetime: Invalid decade input');
return this;
} // assume 20th century?? for '70s'.
if (input.length === 2 && /[0-9][0-9]/.test(input)) {
input = '19' + input;
}
var year = Number(input);
if (isNaN(year)) {
return this;
} // round it down to the decade
year = Math.floor(year / 10) * 10;
return this.year(year); //.startOf('decade')
}
return this.startOf('decade').year();
},
// 1950 -> 19+1
century: function century(input) {
if (input !== undefined) {
if (typeof input === 'string') {
input = input.replace(/([0-9])(th|rd|st|nd)/, '$1'); //fix ordinals
input = input.replace(/([0-9]+) ?(b\.?c\.?|a\.?d\.?)/i, function (a, b, c) {
if (c.match(/b\.?c\.?/i)) {
b = '-' + b;
}
return b;
});
input = input.replace(/c$/, ''); //20thC
}
var year = Number(input);
if (isNaN(input)) {
console.warn('Spacetime: Invalid century input');
return this;
} // there is no century 0
if (year === 0) {
year = 1;
}
if (year >= 0) {
year = (year - 1) * 100;
} else {
year = (year + 1) * 100;
}
return this.year(year);
} // century getter
var num = this.startOf('century').year();
num = Math.floor(num / 100);
if (num < 0) {
return num - 1;
}
return num + 1;
},
// 2019 -> 2+1
millenium: function millenium(input) {
if (input !== undefined) {
if (typeof input === 'string') {
input = input.replace(/([0-9])(th|rd|st|nd)/, '$1'); //fix ordinals
input = Number(input);
if (isNaN(input)) {
console.warn('Spacetime: Invalid millenium input');
return this;
}
}
if (input > 0) {
input -= 1;
}
var year = input * 1000; // there is no year 0
if (year === 0) {
year = 1;
}
return this.year(year);
} // get the current millenium
var num = Math.floor(this.year() / 1000);
if (num >= 0) {
num += 1;
}
return num;
}
};
var _03Year = methods$3;
var methods$4 = Object.assign({}, _01Time, _02Date, _03Year); //aliases
methods$4.milliseconds = methods$4.millisecond;
methods$4.seconds = methods$4.second;
methods$4.minutes = methods$4.minute;
methods$4.hours = methods$4.hour;
methods$4.hour24 = methods$4.hour;
methods$4.h12 = methods$4.hour12;
methods$4.h24 = methods$4.hour24;
methods$4.days = methods$4.day;
var addMethods = function addMethods(Space) {
//hook the methods into prototype
Object.keys(methods$4).forEach(function (k) {
Space.prototype[k] = methods$4[k];
});
};
var query = addMethods;
var isLeapYear$2 = fns.isLeapYear;
var getMonthLength = function getMonthLength(month, year) {
if (month === 1 && isLeapYear$2(year)) {
return 29;
}
return monthLengths_1[month];
}; //month is the one thing we 'model/compute'
//- because ms-shifting can be off by enough
var rollMonth = function rollMonth(want, old) {
//increment year
if (want.month > 0) {
var years = parseInt(want.month / 12, 10);
want.year = old.year() + years;
want.month = want.month % 12;
} else if (want.month < 0) {
//decrement year
var _years = Math.floor(Math.abs(want.month) / 13, 10);
_years = Math.abs(_years) + 1;
want.year = old.year() - _years; //ignore extras
want.month = want.month % 12;
want.month = want.month + 12;
if (want.month === 12) {
want.month = 0;
}
}
return want;
}; // briefly support day=-2 (this does not need to be perfect.)
var rollDaysDown = function rollDaysDown(want, old, sum) {
want.year = old.year();
want.month = old.month();
var date = old.date();
want.date = date - Math.abs(sum);
while (want.date < 1) {
want.month -= 1;
if (want.month < 0) {
want.month = 11;
want.year -= 1;
}
var max = getMonthLength(want.month, want.year);
want.date += max;
}
return want;
}; // briefly support day=33 (this does not need to be perfect.)
var rollDaysUp = function rollDaysUp(want, old, sum) {
var year = old.year();
var month = old.month();
var max = getMonthLength(month, year);
while (sum > max) {
sum -= max;
month += 1;
if (month >= 12) {
month -= 12;
year += 1;
}
max = getMonthLength(month, year);
}
want.month = month;
want.date = sum;
return want;
};
var _model = {
months: rollMonth,
days: rollDaysUp,
daysBack: rollDaysDown
}; // but briefly:
// millisecond-math, and some post-processing covers most-things
// we 'model' the calendar here only a little bit
// and that usually works-out...
var order$1 = ['millisecond', 'second', 'minute', 'hour', 'date', 'month'];
var keep = {
second: order$1.slice(0, 1),
minute: order$1.slice(0, 2),
quarterhour: order$1.slice(0, 2),
hour: order$1.slice(0, 3),
date: order$1.slice(0, 4),
month: order$1.slice(0, 4),
quarter: order$1.slice(0, 4),
season: order$1.slice(0, 4),
year: order$1,
decade: order$1,
century: order$1
};
keep.week = keep.hour;
keep.season = keep.date;
keep.quarter = keep.date; // Units need to be dst adjuested
var dstAwareUnits = {
year: true,
quarter: true,
season: true,
month: true,
week: true,
day: true
};
var keepDate = {
month: true,
quarter: true,
season: true,
year: true
};
var addMethods$1 = function addMethods(SpaceTime) {
SpaceTime.prototype.add = function (num, unit) {
var s = this.clone();
if (!unit || num === 0) {
return s; //don't bother
}
var old = this.clone();
unit = fns.normalize(unit); // support 'fortnight' alias
if (unit === 'fortnight') {
num *= 2;
unit = 'week';
} //move forward by the estimated milliseconds (rough)
if (milliseconds[unit]) {
s.epoch += milliseconds[unit] * num;
} else if (unit === 'week') {
s.epoch += milliseconds.day * (num * 7);
} else if (unit === 'quarter' || unit === 'season') {
s.epoch += milliseconds.month * (num * 3.1); //go a little too-far
} else if (unit === 'quarterhour') {
s.epoch += milliseconds.minute * 15 * num;
} //now ensure our milliseconds/etc are in-line
var want = {};
if (keep[unit]) {
keep[unit].forEach(function (u) {
want[u] = old[u]();
});
}
if (dstAwareUnits[unit]) {
var diff = old.timezone().current.offset - s.timezone().current.offset;
s.epoch += diff * 3600 * 1000;
} //ensure month/year has ticked-over
if (unit === 'month') {
want.month = old.month() + num; //month is the one unit we 'model' directly
want = _model.months(want, old);
} //support coercing a week, too
if (unit === 'week') {
var sum = old.date() + num * 7;
if (sum <= 28 && sum > 1) {
want.date = sum;
}
} //support 25-hour day-changes on dst-changes
else if (unit === 'date') {
if (num < 0) {
want = _model.daysBack(want, old, num);
} else {
//specify a naive date number, if it's easy to do...
var _sum = old.date() + num; // ok, model this one too
want = _model.days(want, old, _sum);
} //manually punt it if we haven't moved at all..
if (num !== 0 && old.isSame(s, 'day')) {
want.date = old.date() + num;
}
} //ensure year has changed (leap-years)
else if (unit === 'year') {
var wantYear = old.year() + num;
var haveYear = s.year();
if (haveYear < wantYear) {
s.epoch += milliseconds.day;
} else if (haveYear > wantYear) {
s.epoch += milliseconds.day;
}
} //these are easier
else if (unit === 'decade') {
want.year = s.year() + 10;
} else if (unit === 'century') {
want.year = s.year() + 100;
} //keep current date, unless the month doesn't have it.
if (keepDate[unit]) {
var max = monthLengths_1[want.month];
want.date = old.date();
if (want.date > max) {
want.date = max;
}
}
walk_1(s, want);
return s;
}; //subtract is only add *-1
SpaceTime.prototype.subtract = function (num, unit) {
var s = this.clone();
return s.add(num * -1, unit);
}; //add aliases
SpaceTime.prototype.minus = SpaceTime.prototype.subtract;
SpaceTime.prototype.plus = SpaceTime.prototype.add;
};
var add = addMethods$1; //make a string, for easy comparison between dates
var print = {
millisecond: function millisecond(s) {
return s.epoch;
},
second: function second(s) {
return [s.year(), s.month(), s.date(), s.hour(), s.minute(), s.second()].join('-');
},
minute: function minute(s) {
return [s.year(), s.month(), s.date(), s.hour(), s.minute()].join('-');
},
hour: function hour(s) {
return [s.year(), s.month(), s.date(), s.hour()].join('-');
},
day: function day(s) {
return [s.year(), s.month(), s.date()].join('-');
},
week: function week(s) {
return [s.year(), s.week()].join('-');
},
month: function month(s) {
return [s.year(), s.month()].join('-');
},
quarter: function quarter(s) {
return [s.year(), s.quarter()].join('-');
},
year: function year(s) {
return s.year();
}
};
print.date = print.day;
var addMethods$2 = function addMethods(SpaceTime) {
SpaceTime.prototype.isSame = function (b, unit) {
var tzAware = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var a = this;
if (!unit) {
return null;
}
if (typeof b === 'string' || typeof b === 'number') {
b = new SpaceTime(b, this.timezone.name);
} //support 'seconds' aswell as 'second'
unit = unit.replace(/s$/, ''); // make them the same timezone for proper comparison
if (tzAware === true && a.tz !== b.tz) {
b = b.clone();
b.tz = a.tz;
}
if (print[unit]) {
return print[unit](a) === print[unit](b);
}
return null;
};
};
var same = addMethods$2;
var addMethods$3 = function addMethods(SpaceTime) {
var methods = {
isAfter: function isAfter(d) {
d = fns.beADate(d, this);
var epoch = fns.getEpoch(d);
if (epoch === null) {
return null;
}
return this.epoch > epoch;
},
isBefore: function isBefore(d) {
d = fns.beADate(d, this);
var epoch = fns.getEpoch(d);
if (epoch === null) {
return null;
}
return this.epoch < epoch;
},
isEqual: function isEqual(d) {
d = fns.beADate(d, this);
var epoch = fns.getEpoch(d);
if (epoch === null) {
return null;
}
return this.epoch === epoch;
},
isBetween: function isBetween(start, end) {
var isInclusive = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
start = fns.beADate(start, this);
end = fns.beADate(end, this);
var startEpoch = fns.getEpoch(start);
if (startEpoch === null) {
return null;
}
var endEpoch = fns.getEpoch(end);
if (endEpoch === null) {
return null;
}
if (isInclusive) {
return this.isBetween(start, end) || this.isEqual(start) || this.isEqual(end);
}
return startEpoch < this.epoch && this.epoch < endEpoch;
}
}; //hook them into proto
Object.keys(methods).forEach(function (k) {
SpaceTime.prototype[k] = methods[k];
});
};
var compare = addMethods$3;
var addMethods$4 = function addMethods(SpaceTime) {
var methods = {
i18n: function i18n(data) {
//change the day names
if (fns.isObject(data.days)) {
days.set(data.days);
} //change the month names
if (fns.isObject(data.months)) {
months.set(data.months);
} // change the the display style of the month / day names
if (fns.isBoolean(data.useTitleCase)) {
caseFormat.set(data.useTitleCase);
}
}
}; //hook them into proto
Object.keys(methods).forEach(function (k) {
SpaceTime.prototype[k] = methods[k];
});
};
var i18n = addMethods$4;
var timezones = unpack; //fake timezone-support, for fakers (es5 class)
var SpaceTime = function SpaceTime(input$1, tz) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; //the holy moment
this.epoch = null; //the shift for the given timezone
this.tz = find(tz, timezones); //whether to output warnings to console
this.silent = options.silent || true; // favour british interpretation of 02/02/2018, etc
this.british = options.dmy || options.british; //does the week start on sunday, or monday:
this._weekStart = 1; //default to monday
if (options.weekStart !== undefined) {
this._weekStart = options.weekStart;
} // the reference today date object, (for testing)
this._today = {};
if (options.today !== undefined) {
this._today = options.today;
} //add getter/setters
Object.defineProperty(this, 'd', {
//return a js date object
get: function get() {
var offset = quick(this); //every computer is somewhere- get this computer's built-in offset
var bias = new Date(this.epoch).getTimezoneOffset() || 0; //movement
var shift = bias + offset * 60; //in minutes
shift = shift * 60 * 1000; //in ms
//remove this computer's offset
var epoch = this.epoch + shift;
var d = new Date(epoch);
return d;
}
}); //add this data on the object, to allow adding new timezones
Object.defineProperty(this, 'timezones', {
get: function get() {
return timezones;
},
set: function set(obj) {
timezones = obj;
return obj;
}
}); //parse the various formats
var tmp = input(this, input$1, tz);
this.epoch = tmp.epoch;
}; //(add instance methods to prototype)
Object.keys(methods_1).forEach(function (k) {
SpaceTime.prototype[k] = methods_1[k];
}); // ¯\_(ツ)_/¯
SpaceTime.prototype.clone = function () {
return new SpaceTime(this.epoch, this.tz, {
silent: this.silent,
weekStart: this._weekStart,
today: this._today
});
}; //return native date object at the same epoch
SpaceTime.prototype.toLocalDate = function () {
return new Date(this.epoch);
}; //append more methods
query(SpaceTime);
add(SpaceTime);
same(SpaceTime);
compare(SpaceTime);
i18n(SpaceTime);
var spacetime = SpaceTime;
var whereIts = function whereIts(a, b) {
var start = new spacetime(null);
var end = new spacetime(null);
start = start.time(a); //if b is undefined, use as 'within one hour'
if (b) {
end = end.time(b);
} else {
end = start.add(59, 'minutes');
}
var startHour = start.hour();
var endHour = end.hour();
var tzs = Object.keys(start.timezones).filter(function (tz) {
if (tz.indexOf('/') === -1) {
return false;
}
var m = new spacetime(null, tz);
var hour = m.hour(); //do 'calendar-compare' not real-time-compare
if (hour >= startHour && hour <= endHour) {
//test minutes too, if applicable
if (hour === startHour && m.minute() < start.minute()) {
return false;
}
if (hour === endHour && m.minute() > end.minute()) {
return false;
}
return true;
}
return false;
});
return tzs;
};
var whereIts_1 = whereIts;
var _version = '6.12.2';
var main$1 = function main(input, tz, options) {
return new spacetime(input, tz, options);
}; // set all properties of a given 'today' object
var setToday = function setToday(s) {
var today = s._today || {};
Object.keys(today).forEach(function (k) {
s = s[k](today[k]);
});
return s;
}; //some helper functions on the main method
main$1.now = function (tz, options) {
var s = new spacetime(new Date().getTime(), tz, options);
s = setToday(s);
return s;
};
main$1.today = function (tz, options) {
var s = new spacetime(new Date().getTime(), tz, options);
s = setToday(s);
return s.startOf('day');
};
main$1.tomorrow = function (tz, options) {
var s = new spacetime(new Date().getTime(), tz, options);
s = setToday(s);
return s.add(1, 'day').startOf('day');
};
main$1.yesterday = function (tz, options) {
var s = new spacetime(new Date().getTime(), tz, options);
s = setToday(s);
return s.subtract(1, 'day').startOf('day');
};
main$1.extend = function (obj) {
Object.keys(obj).forEach(function (k) {
spacetime.prototype[k] = obj[k];
});
return this;
}; //find tz by time
main$1.whereIts = whereIts_1;
main$1.version = _version; //aliases:
main$1.plugin = main$1.extend;
var src = main$1;
return src;
});
});
// these timezone abbreviations are wholly made-up by me, Spencer Kelly, with no expertise in geography
// generated humbly from https://github.com/spencermountain/spacetime-informal
var america = 'America/';
var asia = 'Asia/';
var europe = 'Europe/';
var africa = 'Africa/';
var aus = 'Australia/';
var pac = 'Pacific/';
var informal = {
//europe
'british summer time': europe + 'London',
bst: europe + 'London',
'british time': europe + 'London',
'britain time': europe + 'London',
'irish summer time': europe + 'Dublin',
'irish time': europe + 'Dublin',
ireland: europe + 'Dublin',
'central european time': europe + 'Berlin',
cet: europe + 'Berlin',
'central european summer time': europe + 'Berlin',
cest: europe + 'Berlin',
'central europe': europe + 'Berlin',
'eastern european time': europe + 'Riga',
eet: europe + 'Riga',
'eastern european summer time': europe + 'Riga',
eest: europe + 'Riga',
'eastern europe time': europe + 'Riga',
'western european time': europe + 'Lisbon',
// wet: europe+'Lisbon',
'western european summer time': europe + 'Lisbon',
// west: europe+'Lisbon',
'western europe': europe + 'Lisbon',
'turkey standard time': europe + 'Istanbul',
trt: europe + 'Istanbul',
'turkish time': europe + 'Istanbul',
//africa
etc: africa + 'Freetown',
utc: africa + 'Freetown',
'greenwich standard time': africa + 'Freetown',
gmt: africa + 'Freetown',
'east africa time': africa + 'Nairobi',
// eat: africa+'Nairobi',
'east african time': africa + 'Nairobi',
'eastern africa time': africa + 'Nairobi',
'central africa time': africa + 'Khartoum',
// cat: africa+'Khartoum',
'central african time': africa + 'Khartoum',
'south africa standard time': africa + 'Johannesburg',
sast: africa + 'Johannesburg',
'southern africa': africa + 'Johannesburg',
'south african': africa + 'Johannesburg',
'west africa standard time': africa + 'Lagos',
// wat: africa+'Lagos',
'western africa time': africa + 'Lagos',
'west african time': africa + 'Lagos',
'australian central standard time': aus + 'Adelaide',
acst: aus + 'Adelaide',
'australian central daylight time': aus + 'Adelaide',
acdt: aus + 'Adelaide',
'australia central': aus + 'Adelaide',
'australian eastern standard time': aus + 'Brisbane',
aest: aus + 'Brisbane',
'australian eastern daylight time': aus + 'Brisbane',
aedt: aus + 'Brisbane',
'australia east': aus + 'Brisbane',
'australian western standard time': aus + 'Perth',
awst: aus + 'Perth',
'australian western daylight time': aus + 'Perth',
awdt: aus + 'Perth',
'australia west': aus + 'Perth',
'australian central western standard time': aus + 'Eucla',
acwst: aus + 'Eucla',
'australia central west': aus + 'Eucla',
'lord howe standard time': aus + 'Lord_Howe',
lhst: aus + 'Lord_Howe',
'lord howe daylight time': aus + 'Lord_Howe',
lhdt: aus + 'Lord_Howe',
'russian standard time': europe + 'Moscow',
msk: europe + 'Moscow',
russian: europe + 'Moscow',
//america
'central standard time': america + 'Chicago',
'central time': america + 'Chicago',
cst: america + 'Havana',
'central daylight time': america + 'Chicago',
cdt: america + 'Havana',
'mountain standard time': america + 'Denver',
'mountain time': america + 'Denver',
mst: america + 'Denver',
'mountain daylight time': america + 'Denver',
mdt: america + 'Denver',
'atlantic standard time': america + 'Halifax',
'atlantic time': america + 'Halifax',
ast: asia + 'Baghdad',
'atlantic daylight time': america + 'Halifax',
adt: america + 'Halifax',
'eastern standard time': america + 'New_York',
'eastern time': america + 'New_York',
est: america + 'New_York',
'eastern daylight time': america + 'New_York',
edt: america + 'New_York',
'pacific time': america + 'Los_Angeles',
'pacific standard time': america + 'Los_Angeles',
pst: america + 'Los_Angeles',
'pacific daylight time': america + 'Los_Angeles',
pdt: america + 'Los_Angeles',
'alaskan standard time': america + 'Anchorage',
'alaskan time': america + 'Anchorage',
ahst: america + 'Anchorage',
'alaskan daylight time': america + 'Anchorage',
ahdt: america + 'Anchorage',
'hawaiian standard time': pac + 'Honolulu',
'hawaiian time': pac + 'Honolulu',
hst: pac + 'Honolulu',
'aleutian time': pac + 'Honolulu',
'hawaii time': pac + 'Honolulu',
'newfoundland standard time': america + 'St_Johns',
'newfoundland time': america + 'St_Johns',
nst: america + 'St_Johns',
'newfoundland daylight time': america + 'St_Johns',
ndt: america + 'St_Johns',
'brazil time': america + 'Sao_Paulo',
brt: america + 'Sao_Paulo',
brasília: america + 'Sao_Paulo',
brasilia: america + 'Sao_Paulo',
'brazilian time': america + 'Sao_Paulo',
'argentina time': america + 'Buenos_Aires',
// art: a+'Buenos_Aires',
'argentinian time': america + 'Buenos_Aires',
'amazon time': america + 'Manaus',
amt: america + 'Manaus',
'amazonian time': america + 'Manaus',
'easter island standard time': 'Chile/Easterisland',
east: 'Chile/Easterisland',
'easter island summer time': 'Chile/Easterisland',
easst: 'Chile/Easterisland',
'venezuelan standard time': america + 'Caracas',
'venezuelan time': america + 'Caracas',
vet: america + 'Caracas',
'venezuela time': america + 'Caracas',
'paraguay time': america + 'Asuncion',
pyt: america + 'Asuncion',
'paraguay summer time': america + 'Asuncion',
pyst: america + 'Asuncion',
'cuba standard time': america + 'Havana',
'cuba time': america + 'Havana',
'cuba daylight time': america + 'Havana',
'cuban time': america + 'Havana',
'bolivia time': america + 'La_Paz',
// bot: a+'La_Paz',
'bolivian time': america + 'La_Paz',
'colombia time': america + 'Bogota',
cot: america + 'Bogota',
'colombian time': america + 'Bogota',
'acre time': america + 'Eirunepe',
// act: a+'Eirunepe',
'peru time': america + 'Lima',
// pet: a+'Lima',
'chile standard time': america + 'Punta_Arenas',
'chile time': america + 'Punta_Arenas',
clst: america + 'Punta_Arenas',
'chile summer time': america + 'Punta_Arenas',
cldt: america + 'Punta_Arenas',
'uruguay time': america + 'Montevideo',
uyt: america + 'Montevideo',
//asia
ist: asia + 'Jerusalem',
'arabic standard time': asia + 'Baghdad',
'arabic time': asia + 'Baghdad',
'arab time': asia + 'Baghdad',
'iran standard time': asia + 'Tehran',
'iran time': asia + 'Tehran',
irst: asia + 'Tehran',
'iran daylight time': asia + 'Tehran',
irdt: asia + 'Tehran',
iranian: asia + 'Tehran',
'pakistan standard time': asia + 'Karachi',
'pakistan time': asia + 'Karachi',
pkt: asia + 'Karachi',
'india standard time': asia + 'Kolkata',
'indian time': asia + 'Kolkata',
'indochina time': asia + 'Bangkok',
ict: asia + 'Bangkok',
'south east asia': asia + 'Bangkok',
'china standard time': asia + 'Shanghai',
ct: asia + 'Shanghai',
'chinese time': asia + 'Shanghai',
'alma-ata time': asia + 'Almaty',
almt: asia + 'Almaty',
'oral time': asia + 'Oral',
'orat time': asia + 'Oral',
'yakutsk time': asia + 'Yakutsk',
yakt: asia + 'Yakutsk',
'gulf standard time': asia + 'Dubai',
'gulf time': asia + 'Dubai',
gst: asia + 'Dubai',
uae: asia + 'Dubai',
'hong kong time': asia + 'Hong_Kong',
hkt: asia + 'Hong_Kong',
'western indonesian time': asia + 'Jakarta',
wib: asia + 'Jakarta',
'indonesia time': asia + 'Jakarta',
'central indonesian time': asia + 'Makassar',
wita: asia + 'Makassar',
'israel daylight time': asia + 'Jerusalem',
idt: asia + 'Jerusalem',
'israel standard time': asia + 'Jerusalem',
'israel time': asia + 'Jerusalem',
israeli: asia + 'Jerusalem',
'krasnoyarsk time': asia + 'Krasnoyarsk',
krat: asia + 'Krasnoyarsk',
'malaysia time': asia + 'Kuala_Lumpur',
myt: asia + 'Kuala_Lumpur',
'singapore time': asia + 'Singapore',
sgt: asia + 'Singapore',
'korea standard time': asia + 'Seoul',
'korea time': asia + 'Seoul',
kst: asia + 'Seoul',
'korean time': asia + 'Seoul',
'uzbekistan time': asia + 'Samarkand',
uzt: asia + 'Samarkand',
'vladivostok time': asia + 'Vladivostok',
vlat: asia + 'Vladivostok',
//indian
'maldives time': 'Indian/Maldives',
mvt: 'Indian/Maldives',
'mauritius time': 'Indian/Mauritius',
mut: 'Indian/Mauritius',
// pacific
'marshall islands time': pac + 'Kwajalein',
mht: pac + 'Kwajalein',
'samoa standard time': pac + 'Midway',
sst: pac + 'Midway',
'somoan time': pac + 'Midway',
'chamorro standard time': pac + 'Guam',
chst: pac + 'Guam',
'papua new guinea time': pac + 'Bougainville',
pgt: pac + 'Bougainville'
}; //add the official iana zonefile names
var iana = spacetime().timezones;
var formal = Object.keys(iana).reduce(function (h, k) {
h[k] = k;
return h;
}, {});
var _timezones = Object.assign({}, informal, formal);
var dates = ['weekday', 'summer', 'winter', 'autumn', 'some day', 'one day', 'all day', 'some point', 'eod', 'eom', 'eoy', 'standard time', 'daylight time', 'tommorrow'];
var durations = ['centuries', 'century', 'day', 'days', 'decade', 'decades', 'hour', 'hours', 'hr', 'hrs', 'millisecond', 'milliseconds', 'minute', 'minutes', 'min', 'mins', 'month', 'months', 'seconds', 'sec', 'secs', 'week end', 'week ends', 'weekend', 'weekends', 'week', 'weeks', 'wk', 'wks', 'year', 'years', 'yr', 'yrs', 'quarter', 'quarters', 'qtr', 'qtrs', 'season', 'seasons'];
var holidays = ['all hallows eve', 'all saints day', 'all sts day', 'april fools', 'armistice day', 'australia day', 'bastille day', 'boxing day', 'canada day', 'christmas eve', 'christmas', 'cinco de mayo', 'day of the dead', 'dia de muertos', 'dieciseis de septiembre', 'emancipation day', 'grito de dolores', 'groundhog day', 'halloween', 'harvey milk day', 'inauguration day', 'independence day', 'independents day', 'juneteenth', 'labour day', 'national freedom day', 'national nurses day', 'new years eve', 'new years', 'purple heart day', 'rememberance day', 'rosa parks day', 'saint andrews day', 'saint patricks day', 'saint stephens day', 'saint valentines day', 'st andrews day', 'st patricks day', 'st stephens day', 'st valentines day ', 'valentines day', 'valentines', 'veterans day', 'victoria day', 'womens equality day', 'xmas', // Fixed religious and cultural holidays
// Catholic + Christian
'epiphany', 'orthodox christmas day', 'orthodox new year', 'assumption of mary', 'all souls day', 'feast of the immaculate conception', 'feast of our lady of guadalupe', // Kwanzaa
'kwanzaa', // Pagan / metal 🤘
'imbolc', 'beltaine', 'lughnassadh', 'samhain', 'martin luther king day', 'mlk day', 'presidents day', 'mardi gras', 'tax day', 'commonwealth day', 'mothers day', 'memorial day', 'fathers day', 'columbus day', 'indigenous peoples day', 'canadian thanksgiving', 'election day', 'thanksgiving', 't-day', 'turkey day', 'black friday', 'cyber monday', // Astronomical religious and cultural holidays
'ash wednesday', 'palm sunday', 'maundy thursday', 'good friday', 'holy saturday', 'easter', 'easter sunday', 'easter monday', 'orthodox good friday', 'orthodox holy saturday', 'orthodox easter', 'orthodox easter monday', 'ascension day', 'pentecost', 'whitsunday', 'whit sunday', 'whit monday', 'trinity sunday', 'corpus christi', 'advent', // Jewish
'tu bishvat', 'tu bshevat', 'purim', 'passover', 'yom hashoah', 'lag baomer', 'shavuot', 'tisha bav', 'rosh hashana', 'yom kippur', 'sukkot', 'shmini atzeret', 'simchat torah', 'chanukah', 'hanukkah', // Muslim
'isra and miraj', 'lailat al-qadr', 'eid al-fitr', 'id al-Fitr', 'eid ul-Fitr', 'ramadan', 'eid al-adha', 'muharram', 'the prophets birthday', 'ostara', 'march equinox', 'vernal equinox', 'litha', 'june solistice', 'summer solistice', 'mabon', 'september equinox', 'fall equinox', 'autumnal equinox', 'yule', 'december solstice', 'winter solstice', // Additional important holidays
'chinese new year', 'diwali'];
var times = ['noon', 'midnight', 'now', 'morning', 'tonight', 'evening', 'afternoon', 'night', 'breakfast time', 'lunchtime', 'dinnertime', 'sometime', 'midday', 'eod', 'oclock', 'oclock', 'all day', 'at night'];
var data = [[dates, '#Date'], [durations, '#Duration'], [holidays, '#Holiday'], [times, '#Time'], [Object.keys(_timezones), '#Timezone']];
var lex = {
'a couple': 'Value'
};
data.forEach(function (a) {
for (var i = 0; i < a[0].length; i++) {
lex[a[0][i]] = a[1];
}
});
var words = lex;
var knownUnits = {
second: true,
minute: true,
hour: true,
day: true,
week: true,
weekend: true,
month: true,
season: true,
quarter: true,
year: true
};
var aliases = {
wk: 'week',
min: 'minute',
sec: 'second',
weekend: 'week' //for now...
};
var parseUnit = function parseUnit(m) {
var unit = m.match('#Duration').text('normal');
unit = unit.replace(/s$/, ''); // support shorthands like 'min'
if (aliases.hasOwnProperty(unit)) {
unit = aliases[unit];
}
return unit;
}; //turn '5 weeks before' to {weeks:5}
var parseShift = function parseShift(doc) {
var result = {};
var shift = doc.match('#DateShift+');
if (shift.found === false) {
return result;
} // '5 weeks'
shift.match('#Cardinal #Duration').forEach(function (ts) {
var num = ts.match('#Cardinal').text('normal');
num = parseFloat(num);
if (num && typeof num === 'number') {
var unit = parseUnit(ts);
if (knownUnits[unit] === true) {
result[unit] = num;
}
}
}); //is it 2 weeks ago? → -2
if (shift.has('(before|ago|hence|back)$') === true) {
Object.keys(result).forEach(function (k) {
return result[k] *= -1;
});
}
shift.remove('#Cardinal #Duration'); // supoprt '1 day after tomorrow'
var m = shift.match('[<unit>#Duration] [<dir>(after|before)]');
if (m.found) {
var unit = m.groups('unit').text('reduced'); // unit = unit.replace(/s$/, '')
var dir = m.groups('dir').text('reduced');
if (dir === 'after') {
result[unit] = 1;
} else if (dir === 'before') {
result[unit] = -1;
}
} // in half an hour
m = shift.match('half (a|an) [#Duration]', 0);
if (m.found) {
var _unit = parseUnit(m);
result[_unit] = 0.5;
} // finally, remove it from our text
doc.remove('#DateShift');
return result;
};
var _01Shift = parseShift;
/*
a 'counter' is a Unit determined after a point
* first hour of x
* 7th week in x
* last year in x
*
unlike a shift, like "2 weeks after x"
*/
var oneBased = {
minute: true
};
var getCounter = function getCounter(doc) {
// 7th week of
var m = doc.match('[<num>#Value] [<unit>#Duration+] (of|in)');
if (m.found) {
var obj = m.groups();
var num = obj.num.text('reduced');
var unit = obj.unit.text('reduced');
var found = {
unit: unit,
num: Number(num) || 0
}; // 0-based or 1-based units
if (!oneBased[unit]) {
found.num -= 1;
}
doc = doc.remove(m);
return found;
} // first week of
m = doc.match('[<dir>(first|initial|last|final)] [<unit>#Duration+] (of|in)');
if (m.found) {
var _obj = m.groups();
var dir = _obj.dir.text('reduced');
var _unit = _obj.unit.text('reduced');
if (dir === 'initial') {
dir = 'first';
}
if (dir === 'final') {
dir = 'last';
}
var _found = {
unit: _unit,
dir: dir
};
doc = doc.remove(m);
return _found;
}
return {};
};
var _02Counter = getCounter;
var hardCoded = {
daybreak: '7:00am',
//ergh
breakfast: '8:00am',
morning: '9:00am',
noon: '12:00pm',
midday: '12:00pm',
afternoon: '2:00pm',
lunchtime: '12:00pm',
evening: '6:00pm',
dinnertime: '6:00pm',
night: '8:00pm',
eod: '10:00pm',
midnight: '12:00am'
};
var halfPast = function halfPast(m, s) {
var hour = m.match('#Cardinal$').text('reduced');
var term = m.match('(half|quarter|25|15|10|5)');
var mins = term.text('reduced');
if (term.has('half')) {
mins = '30';
}
if (term.has('quarter')) {
mins = '15';
}
var behind = m.has('to'); // apply it
s = s.hour(hour);
s = s.startOf('hour'); // assume 'half past 5' is 5pm
if (hour < 6) {
s = s.ampm('pm');
}
if (behind) {
s = s.subtract(mins, 'minutes');
} else {
s = s.add(mins, 'minutes');
}
return s;
};
var parseTime = function parseTime(doc, context) {
var time = doc.match('(at|by|for|before|this)? #Time+');
if (time.found) {
doc.remove(time);
} // get the main part of the time
time = time.not('^(at|by|for|before|this)');
time = time.not('sharp');
time = time.not('on the dot');
var s = spacetime.now(context.timezone);
var now = s.clone(); // check for known-times (like 'today')
var timeStr = time.text('reduced');
if (hardCoded.hasOwnProperty(timeStr)) {
return hardCoded[timeStr];
} // '5 oclock'
var m = time.match('^#Cardinal oclock (am|pm)?');
if (m.found) {
m = m.not('oclock');
s = s.hour(m.text('reduced'));
s = s.startOf('hour');
if (s.isValid() && !s.isEqual(now)) {
return s.time();
}
} // 'quarter to two'
m = time.match('(half|quarter|25|15|10|5) (past|after|to) #Cardinal');
if (m.found) {
s = halfPast(m, s);
if (s.isValid() && !s.isEqual(now)) {
return s.time();
}
} // '4 in the evening'
m = time.match('[<time>#Time] (in|at) the? [<desc>(morning|evening|night|nighttime)]');
if (m.found) {
var _str = m.groups('time').text('reduced');
if (/^[0-9]{1,2}$/.test(_str)) {
s = s.hour(_str); //3 in the morning
} else {
s = s.time(_str); // 3:30 in the morning
}
if (s.isValid() && !s.isEqual(now)) {
var desc = m.groups('desc').text('reduced');
if (desc === 'evening' || desc === 'night') {
s = s.ampm('pm');
}
return s.time();
}
} // 'this morning at 4'
m = time.match('this? [<desc>(morning|evening|tonight)] at [<time>(#Cardinal|#Time)]');
if (m.found) {
var g = m.groups();
var _str2 = g.time.text('reduced');
if (/^[0-9]{1,2}$/.test(_str2)) {
s = s.hour(_str2); //3
s = s.startOf('hour');
} else {
s = s.time(_str2); // 3:30
}
if (s.isValid() && !s.isEqual(now)) {
var _desc = g.desc.text('reduced');
if (_desc === 'morning') {
s = s.ampm('am');
}
if (_desc === 'evening' || _desc === 'tonight') {
s = s.ampm('pm');
}
return s.time();
}
} // 'at 4' -> '4'
m = time.match('^#Cardinal$');
if (m.found) {
s = s.hour(m.text('reduced'));
s = s.startOf('hour');
if (s.isValid() && !s.isEqual(now)) {
return s.time();
}
} // parse random a time like '4:54pm'
var str = time.text('reduced');
s = s.time(str);
if (s.isValid() && !s.isEqual(now)) {
return s.time();
}
return null;
};
var _03Time = parseTime;
// interpret 'this halloween' or 'next june'
var parseRelative = function parseRelative(doc) {
// avoid parsing 'last month of 2019'
// if (doc.has('^(this|current|next|upcoming|last|previous) #Duration')) {
// return null
// }
// parse 'this evening'
// let m = doc.match('^(next|last|this)$')
// if (m.found) {
// doc.remove(m)
// return doc.text('reduced')
// }
// but avoid parsing 'day after next'
if (doc.has('(next|last|this)$')) {
return null;
}
var rel = null;
var m = doc.match('^this? (next|upcoming|coming)');
if (m.found) {
rel = 'next';
doc.remove(m);
}
m = doc.match('^this? (last|previous)');
if (m.found) {
rel = 'last';
doc.remove(m);
}
m = doc.match('^(this|current)');
if (m.found) {
rel = 'this';
doc.remove(m);
} // finally, remove it from our text
// doc.remove('^(this|current|next|upcoming|last|previous)')
return rel;
};
var _04Relative = parseRelative;
// 'start of october', 'middle of june 1st'
var parseSection = function parseSection(doc) {
// start of 2019
var m = doc.match('[(start|beginning) of] .', 0);
if (m.found) {
doc.remove(m);
return 'start';
} // end of 2019
m = doc.match('[end of] .', 0);
if (m.found) {
doc.remove(m);
return 'end';
} // middle of 2019
m = doc.match('[(middle|midpoint|center) of] .', 0);
if (m.found) {
doc.remove(m);
return 'middle';
}
return null;
};
var _05Section = parseSection;
var isOffset = /(\-?[0-9]+)h(rs)?/i;
var isNumber = /(\-?[0-9]+)/;
var utcOffset = /utc([\-+]?[0-9]+)/i;
var gmtOffset = /gmt([\-+]?[0-9]+)/i;
var toIana = function toIana(num) {
num = Number(num);
if (num > -13 && num < 13) {
num = num * -1; //it's opposite!
num = (num > 0 ? '+' : '') + num; //add plus sign
return 'Etc/GMT' + num;
}
return null;
};
var parseOffset = function parseOffset(tz) {
// '+5hrs'
var m = tz.match(isOffset);
if (m !== null) {
return toIana(m[1]);
} // 'utc+5'
m = tz.match(utcOffset);
if (m !== null) {
return toIana(m[1]);
} // 'GMT-5' (not opposite)
m = tz.match(gmtOffset);
if (m !== null) {
var num = Number(m[1]) * -1;
return toIana(num);
} // '+5'
m = tz.match(isNumber);
if (m !== null) {
return toIana(m[1]);
}
return null;
};
var parseTimezone = function parseTimezone(doc) {
var m = doc.match('#Timezone+'); //remove prepositions
m = m.remove('(in|for|by|near|at)');
var str = m.text('reduced'); // remove it from our doc, either way
doc.remove('#Timezone+'); // check our list of informal tz names
if (_timezones.hasOwnProperty(str)) {
return _timezones[str];
}
var tz = parseOffset(str);
if (tz) {
return tz;
}
return null;
};
var _06Timezone = parseTimezone;
var Unit = /*#__PURE__*/function () {
function Unit(input, unit, context, keepTime) {
_classCallCheck(this, Unit);
this.unit = unit || 'day';
context = context || {};
var today = {};
if (context.today) {
today = {
date: context.today.date(),
month: context.today.month(),
year: context.today.year()
};
} // set it to the beginning of the given unit
var d = spacetime(input, context.timezone, {
today: today
}); // set to beginning
// if (d.isValid() && keepTime !== true) {
// d = d.startOf(this.unit)
// }
Object.defineProperty(this, 'd', {
enumerable: false,
writable: true,
value: d
});
Object.defineProperty(this, 'context', {
enumerable: false,
writable: true,
value: context
});
} // make a new one
_createClass(Unit, [{
key: "clone",
value: function clone() {
var d = new Unit(this.d, this.unit, this.context);
return d;
}
}, {
key: "log",
value: function log() {
console.log('--');
this.d.log();
console.log('\n');
return this;
}
}, {
key: "applyShift",
value: function applyShift() {
var _this = this;
var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
Object.keys(obj).forEach(function (unit) {
_this.d = _this.d.add(obj[unit], unit);
});
return this;
}
}, {
key: "applyTime",
value: function applyTime(str) {
if (str) {
this.d = this.d.time(str);
} else {
this.d = this.d.startOf('day'); //zero-out time
}
return this;
}
}, {
key: "applyRel",
value: function applyRel(rel) {
if (rel === 'next') {
return this.next();
}
if (rel === 'last') {
return this.last();
}
return this;
}
}, {
key: "applySection",
value: function applySection(section) {
if (section === 'start') {
return this.start();
}
if (section === 'end') {
return this.end();
}
if (section === 'middle') {
return this.middle();
}
return this;
}
}, {
key: "format",
value: function format(fmt) {
return this.d.format(fmt);
}
}, {
key: "start",
value: function start() {
this.d = this.d.startOf(this.unit);
return this;
}
}, {
key: "end",
value: function end() {
this.d = this.d.endOf(this.unit);
return this;
}
}, {
key: "middle",
value: function middle() {
var diff = this.d.diff(this.d.endOf(this.unit));
var minutes = Math.round(diff.minutes / 2);
this.d = this.d.add(minutes, 'minutes');
return this;
} // the millescond before
}, {
key: "before",
value: function before() {
this.d = this.d.minus(1, this.unit);
this.d = this.d.endOf(this.unit);
return this;
} // 'after 2019'
}, {
key: "after",
value: function after() {
this.d = this.d.add(1, this.unit);
this.d = this.d.startOf(this.unit);
return this;
} // tricky: 'next june' 'next tuesday'
}, {
key: "next",
value: function next() {
this.d = this.d.add(1, this.unit);
this.d = this.d.startOf(this.unit);
return this;
} // tricky: 'last june' 'last tuesday'
}, {
key: "last",
value: function last() {
this.d = this.d.minus(1, this.unit);
this.d = this.d.startOf(this.unit);
return this;
}
}]);
return Unit;
}();
var Unit_1 = Unit;
var Day = /*#__PURE__*/function (_Unit) {
_inherits(Day, _Unit);
var _super = _createSuper(Day);
function Day(input, unit, context) {
var _this;
_classCallCheck(this, Day);
_this = _super.call(this, input, unit, context);
_this.unit = 'day';
if (_this.d.isValid()) {
_this.d = _this.d.startOf('day');
}
return _this;
}
return Day;
}(Unit_1); // like 'feb 2'
var CalendarDate = /*#__PURE__*/function (_Day) {
_inherits(CalendarDate, _Day);
var _super2 = _createSuper(CalendarDate);
function CalendarDate(input, unit, context) {
var _this2;
_classCallCheck(this, CalendarDate);
_this2 = _super2.call(this, input, unit, context);
_this2.unit = 'day';
if (_this2.d.isValid()) {
_this2.d = _this2.d.startOf('day');
}
return _this2;
}
_createClass(CalendarDate, [{
key: "next",
value: function next() {
this.d = this.d.add(1, 'year');
return this;
}
}, {
key: "last",
value: function last() {
this.d = this.d.minus(1, 'year');
return this;
}
}]);
return CalendarDate;
}(Day);
var WeekDay = /*#__PURE__*/function (_Day2) {
_inherits(WeekDay, _Day2);
var _super3 = _createSuper(WeekDay);
function WeekDay(input, unit, context) {
var _this3;
_classCallCheck(this, WeekDay);
_this3 = _super3.call(this, input, unit, context);
_this3.unit = 'week'; // is the input just a weekday?
if (typeof input === 'string') {
_this3.d = spacetime(context.today, context.timezone);
_this3.d = _this3.d.day(input); // assume a wednesday in the future
if (_this3.d.isBefore(context.today)) {
_this3.d = _this3.d.add(7, 'days');
}
} else {
_this3.d = input;
}
_this3.weekDay = _this3.d.dayName();
if (_this3.d.isValid()) {
_this3.d = _this3.d.startOf('day');
}
return _this3;
}
_createClass(WeekDay, [{
key: "clone",
value: function clone() {
//overloaded method
return new WeekDay(this.d, this.unit, this.context);
}
}, {
key: "end",
value: function end() {
//overloaded method
this.d = this.d.endOf('day');
return this;
}
}, {
key: "next",
value: function next() {
this.d = this.d.add(7, 'days');
this.d = this.d.day(this.weekDay);
return this;
}
}, {
key: "last",
value: function last() {
this.d = this.d.minus(7, 'days');
this.d = this.d.day(this.weekDay);
return this;
}
}]);
return WeekDay;
}(Day); // like 'haloween'
var Holiday = /*#__PURE__*/function (_CalendarDate) {
_inherits(Holiday, _CalendarDate);
var _super4 = _createSuper(Holiday);
function Holiday(input, unit, context) {
var _this4;
_classCallCheck(this, Holiday);
_this4 = _super4.call(this, input, unit, context);
_this4.unit = 'day';
if (_this4.d.isValid()) {
_this4.d = _this4.d.startOf('day');
}
return _this4;
}
return Holiday;
}(CalendarDate);
var _day = {
Day: Day,
WeekDay: WeekDay,
CalendarDate: CalendarDate,
Holiday: Holiday
};
var AnyMonth = /*#__PURE__*/function (_Unit) {
_inherits(AnyMonth, _Unit);
var _super = _createSuper(AnyMonth);
function AnyMonth(input, unit, context) {
var _this;
_classCallCheck(this, AnyMonth);
_this = _super.call(this, input, unit, context);
_this.unit = 'month'; // set to beginning
if (_this.d.isValid()) {
_this.d = _this.d.startOf(_this.unit);
}
return _this;
}
return AnyMonth;
}(Unit_1); // a specific month, like 'March'
var Month = /*#__PURE__*/function (_Unit2) {
_inherits(Month, _Unit2);
var _super2 = _createSuper(Month);
function Month(input, unit, context) {
var _this2;
_classCallCheck(this, Month);
_this2 = _super2.call(this, input, unit, context);
_this2.unit = 'month'; // set to beginning
if (_this2.d.isValid()) {
_this2.d = _this2.d.startOf(_this2.unit);
}
return _this2;
}
_createClass(Month, [{
key: "next",
value: function next() {
this.d = this.d.add(1, 'year');
this.d = this.d.startOf('month');
return this;
}
}, {
key: "last",
value: function last() {
this.d = this.d.minus(1, 'year');
this.d = this.d.startOf('month');
return this;
}
}]);
return Month;
}(Unit_1);
var AnyQuarter = /*#__PURE__*/function (_Unit3) {
_inherits(AnyQuarter, _Unit3);
var _super3 = _createSuper(AnyQuarter);
function AnyQuarter(input, unit, context) {
var _this3;
_classCallCheck(this, AnyQuarter);
_this3 = _super3.call(this, input, unit, context);
_this3.unit = 'quarter'; // set to beginning
if (_this3.d.isValid()) {
_this3.d = _this3.d.startOf(_this3.unit);
}
return _this3;
}
_createClass(AnyQuarter, [{
key: "last",
value: function last() {
console.log(this.d.format());
this.d = this.d.minus(1, 'quarter');
console.log(this.d.format());
this.d = this.d.startOf(this.unit);
console.log(this.d.format());
return this;
}
}]);
return AnyQuarter;
}(Unit_1);
var Quarter = /*#__PURE__*/function (_Unit4) {
_inherits(Quarter, _Unit4);
var _super4 = _createSuper(Quarter);
function Quarter(input, unit, context) {
var _this4;
_classCallCheck(this, Quarter);
_this4 = _super4.call(this, input, unit, context);
_this4.unit = 'quarter'; // set to beginning
if (_this4.d.isValid()) {
_this4.d = _this4.d.startOf(_this4.unit);
}
return _this4;
}
_createClass(Quarter, [{
key: "next",
value: function next() {
this.d = this.d.add(1, 'year');
this.d = this.d.startOf(this.unit);
return this;
}
}, {
key: "last",
value: function last() {
this.d = this.d.minus(1, 'year');
this.d = this.d.startOf(this.unit);
return this;
}
}]);
return Quarter;
}(Unit_1);
var Season = /*#__PURE__*/function (_Unit5) {
_inherits(Season, _Unit5);
var _super5 = _createSuper(Season);
function Season(input, unit, context) {
var _this5;
_classCallCheck(this, Season);
_this5 = _super5.call(this, input, unit, context);
_this5.unit = 'season'; // set to beginning
if (_this5.d.isValid()) {
_this5.d = _this5.d.startOf(_this5.unit);
}
return _this5;
}
_createClass(Season, [{
key: "next",
value: function next() {
this.d = this.d.add(1, 'year');
this.d = this.d.startOf(this.unit);
return this;
}
}, {
key: "last",
value: function last() {
this.d = this.d.minus(1, 'year');
this.d = this.d.startOf(this.unit);
return this;
}
}]);
return Season;
}(Unit_1);
var Year = /*#__PURE__*/function (_Unit6) {
_inherits(Year, _Unit6);
var _super6 = _createSuper(Year);
function Year(input, unit, context) {
var _this6;
_classCallCheck(this, Year);
_this6 = _super6.call(this, input, unit, context);
_this6.unit = 'year';
if (_this6.d.isValid()) {
_this6.d = _this6.d.startOf('year');
}
return _this6;
}
return Year;
}(Unit_1);
var _year = {
AnyMonth: AnyMonth,
Month: Month,
Quarter: Quarter,
AnyQuarter: AnyQuarter,
Season: Season,
Year: Year
};
var Week = /*#__PURE__*/function (_Unit) {
_inherits(Week, _Unit);
var _super = _createSuper(Week);
function Week(input, unit, context) {
var _this;
_classCallCheck(this, Week);
_this = _super.call(this, input, unit, context);
_this.unit = 'week';
if (_this.d.isValid()) {
_this.d = _this.d.startOf('week');
}
return _this;
}
return Week;
}(Unit_1); //may need some work
var WeekEnd = /*#__PURE__*/function (_Unit2) {
_inherits(WeekEnd, _Unit2);
var _super2 = _createSuper(WeekEnd);
function WeekEnd(input, unit, context) {
var _this2;
_classCallCheck(this, WeekEnd);
_this2 = _super2.call(this, input, unit, context);
_this2.unit = 'week';
if (_this2.d.isValid()) {
_this2.d = _this2.d.day('saturday');
_this2.d = _this2.d.startOf('day');
}
return _this2;
}
_createClass(WeekEnd, [{
key: "start",
value: function start() {
this.d = this.d.day('saturday').startOf('day');
return this;
} // end() {
// this.d = this.d.day('sunday').endOf('day')
// return this
// }
}, {
key: "next",
value: function next() {
this.d = this.d.add(1, this.unit);
this.d = this.d.startOf('weekend');
return this;
}
}, {
key: "last",
value: function last() {
this.d = this.d.minus(1, this.unit);
this.d = this.d.startOf('weekend');
return this;
}
}]);
return WeekEnd;
}(Unit_1);
var _week = {
Week: Week,
WeekEnd: WeekEnd
};
var Hour = /*#__PURE__*/function (_Unit) {
_inherits(Hour, _Unit);
var _super = _createSuper(Hour);
function Hour(input, unit, context) {
var _this;
_classCallCheck(this, Hour);
_this = _super.call(this, input, unit, context, true);
_this.unit = 'hour';
if (_this.d.isValid()) {
_this.d = _this.d.startOf('hour');
}
return _this;
}
return Hour;
}(Unit_1);
var Minute = /*#__PURE__*/function (_Unit2) {
_inherits(Minute, _Unit2);
var _super2 = _createSuper(Minute);
function Minute(input, unit, context) {
var _this2;
_classCallCheck(this, Minute);
_this2 = _super2.call(this, input, unit, context, true);
_this2.unit = 'minute';
if (_this2.d.isValid()) {
_this2.d = _this2.d.startOf('minute');
}
return _this2;
}
return Minute;
}(Unit_1);
var Moment = /*#__PURE__*/function (_Unit3) {
_inherits(Moment, _Unit3);
var _super3 = _createSuper(Moment);
function Moment(input, unit, context) {
var _this3;
_classCallCheck(this, Moment);
_this3 = _super3.call(this, input, unit, context, true);
_this3.unit = 'millisecond';
return _this3;
}
return Moment;
}(Unit_1);
var _time = {
Hour: Hour,
Minute: Minute,
Moment: Moment
};
var units = Object.assign({
Unit: Unit_1
}, _day, _year, _week, _time);
var Day$1 = units.Day,
Moment$1 = units.Moment,
Hour$1 = units.Hour;
var knownWord = {
today: function today(context) {
return new Day$1(context.today, null, context);
},
yesterday: function yesterday(context) {
return new Day$1(context.today.minus(1, 'day'), null, context);
},
tomorrow: function tomorrow(context) {
return new Day$1(context.today.plus(1, 'day'), null, context);
},
eom: function eom(context) {
var d = context.today.endOf('month');
d = d.startOf('day');
return new Day$1(d, null, context);
},
// eod: (context) => {
// let d = context.today.endOf('day')
// d = d.startOf('hour').minus(4, 'hours') //rough
// return new Hour(d, null, context)
// },
eoy: function eoy(context) {
var d = context.today.endOf('year');
d = d.startOf('day');
return new Day$1(d, null, context);
}
};
knownWord.tommorrow = knownWord.tomorrow;
knownWord.tmrw = knownWord.tomorrow;
var today = function today(doc, context, section) {
var unit = null; // is it empty?
if (doc.found === false) {
// do we have just a time?
if (section.time !== null) {
unit = new Moment$1(context.today, null, context); // choose today
} //do we just have a shift?
if (Object.keys(section.shift).length > 0) {
if (section.shift.hour || section.shift.minute) {
unit = new Moment$1(context.today, null, context); // choose now
} else {
unit = new Day$1(context.today, null, context); // choose today
}
}
} // today, yesterday, tomorrow
var str = doc.text('reduced');
if (knownWord.hasOwnProperty(str) === true) {
return knownWord[str](context);
} // day after next
if (str === 'next' && Object.keys(section.shift).length > 0) {
return knownWord.tomorrow(context);
}
return unit;
};
var _01Today = today;
var spacetimeHoliday = createCommonjsModule(function (module, exports) {
(function (global, factory) {
module.exports = factory(spacetime) ;
})(commonjsGlobal, function (spacetime) {
function _interopDefaultLegacy(e) {
return e && _typeof(e) === 'object' && 'default' in e ? e : {
'default': e
};
}
var spacetime__default = /*#__PURE__*/_interopDefaultLegacy(spacetime); //yep,
var jan = 'january';
var feb = 'february';
var mar = 'march';
var apr = 'april';
var may = 'may';
var jun = 'june';
var jul = 'july';
var aug = 'august';
var sep = 'september';
var oct = 'october';
var nov = 'november';
var dec = 'december';
var fixedHolidays = {
'new years eve': [dec, 31],
'new years': [jan, 1],
'new years day': [jan, 1],
'inauguration day': [jan, 20],
'australia day': [jan, 26],
'national freedom day': [feb, 1],
'groundhog day': [feb, 2],
'rosa parks day': [feb, 4],
'valentines day': [feb, 14],
'saint valentines day': [feb, 14],
'st valentines day ': [feb, 14],
'saint patricks day': [mar, 17],
'st patricks day': [mar, 17],
'april fools': [apr, 1],
'april fools day': [apr, 1],
'emancipation day': [apr, 16],
'tax day': [apr, 15],
//US
'labour day': [may, 1],
'cinco de mayo': [may, 5],
'national nurses day': [may, 6],
'harvey milk day': [may, 22],
'victoria day': [may, 24],
juneteenth: [jun, 19],
'canada day': [jul, 1],
'independence day': [jul, 4],
'independents day': [jul, 4],
'bastille day': [jul, 14],
'purple heart day': [aug, 7],
'womens equality day': [aug, 26],
'16 de septiembre': [sep, 16],
'dieciseis de septiembre': [sep, 16],
'grito de dolores': [sep, 16],
halloween: [oct, 31],
'all hallows eve': [oct, 31],
'day of the dead': [oct, 31],
// Ranged holiday [nov, 2],
'dia de muertos': [oct, 31],
// Ranged holiday [nov, 2],
'veterans day': [nov, 11],
'st andrews day': [nov, 30],
'saint andrews day': [nov, 30],
'all saints day': [nov, 1],
'all sts day': [nov, 1],
'armistice day': [nov, 11],
'rememberance day': [nov, 11],
'christmas eve': [dec, 24],
christmas: [dec, 25],
xmas: [dec, 25],
'boxing day': [dec, 26],
'st stephens day': [dec, 26],
'saint stephens day': [dec, 26],
// Fixed religious and cultural holidays
// Catholic + Christian
epiphany: [jan, 6],
'orthodox christmas day': [jan, 7],
'orthodox new year': [jan, 14],
'assumption of mary': [aug, 15],
'all souls day': [nov, 2],
'feast of the immaculate conception': [dec, 8],
'feast of our lady of guadalupe': [dec, 12],
// Kwanzaa
kwanzaa: [dec, 26],
// Ranged holiday [jan, 1],
// Pagan / metal 🤘
imbolc: [feb, 2],
beltaine: [may, 1],
lughnassadh: [aug, 1],
samhain: [oct, 31]
};
var fixedDates = function fixedDates(str, normal, year, tz) {
if (fixedHolidays.hasOwnProperty(str) || fixedHolidays.hasOwnProperty(normal)) {
var arr = fixedHolidays[str] || fixedHolidays[normal] || [];
var s = spacetime__default['default'].now(tz);
s = s.year(year);
s = s.startOf('year');
s = s.month(arr[0]);
s = s.date(arr[1]);
if (s.isValid()) {
return s;
}
}
return null;
};
var _01FixedDates = fixedDates; //these are holidays on the 'nth weekday of month'
var jan$1 = 'january';
var feb$1 = 'february';
var mar$1 = 'march'; // const apr = 'april'
var may$1 = 'may';
var jun$1 = 'june'; // const jul = 'july'
// const aug = 'august'
var sep$1 = 'september';
var oct$1 = 'october';
var nov$1 = 'november'; // const dec = 'december'
var mon = 'monday'; // const tues = 'tuesday'
// const wed = 'wednesday'
var thurs = 'thursday';
var fri = 'friday'; // const sat = 'saturday'
var sun = 'sunday';
var holidays = {
'martin luther king day': [3, mon, jan$1],
//[third monday in january],
'presidents day': [3, mon, feb$1],
//[third monday in february],
'commonwealth day': [2, mon, mar$1],
//[second monday in march],
'mothers day': [2, sun, may$1],
//[second Sunday in May],
'fathers day': [3, sun, jun$1],
//[third Sunday in June],
'labor day': [1, mon, sep$1],
//[first monday in september],
'columbus day': [2, mon, oct$1],
//[second monday in october],
'canadian thanksgiving': [2, mon, oct$1],
//[second monday in october],
thanksgiving: [4, thurs, nov$1],
// [fourth Thursday in November],
'black friday': [4, fri, nov$1] //[fourth friday in november],
// 'memorial day': [may], //[last monday in may],
// 'us election': [nov], // [Tuesday following the first Monday in November],
// 'cyber monday': [nov]
// 'advent': [] // fourth Sunday before Christmas
}; // add aliases
holidays['turday day'] = holidays.thanksgiving;
holidays['indigenous peoples day'] = holidays['columbus day'];
holidays['mlk day'] = holidays['martin luther king day'];
var calendarHolidays = holidays;
var fixedDates$1 = function fixedDates(str, normal, year, tz) {
if (calendarHolidays.hasOwnProperty(str) || calendarHolidays.hasOwnProperty(normal)) {
var arr = calendarHolidays[str] || calendarHolidays[normal] || [];
var s = spacetime__default['default'].now(tz);
s = s.year(year); // [3rd, 'monday', 'january']
s = s.month(arr[2]);
s = s.startOf('month'); // make it january
var month = s.month(); // make it the 1st monday
s = s.day(arr[1]);
if (s.month() !== month) {
s = s.add(1, 'week');
} // make it nth monday
if (arr[0] > 1) {
s = s.add(arr[0] - 1, 'week');
}
if (s.isValid()) {
return s;
}
}
return null;
};
var _02NthWeekday = fixedDates$1; // https://www.timeanddate.com/calendar/determining-easter-date.html
var dates = {
easter: 0,
'ash wednesday': -46,
// (46 days before easter)
'palm sunday': 7,
// (1 week before easter)
'maundy thursday': -3,
// (3 days before easter)
'good friday': -2,
// (2 days before easter)
'holy saturday': -1,
// (1 days before easter)
'easter saturday': -1,
// (1 day before easter)
'easter monday': 1,
// (1 day after easter)
'ascension day': 39,
// (39 days after easter)
'whit sunday': 49,
// / pentecost (49 days after easter)
'whit monday': 50,
// (50 days after easter)
'trinity sunday': 65,
// (56 days after easter)
'corpus christi': 60,
// (60 days after easter)
'mardi gras': -47 //(47 days before easter)
};
dates['easter sunday'] = dates.easter;
dates['pentecost'] = dates['whit sunday'];
dates['whitsun'] = dates['whit sunday'];
var easterHolidays = dates; // by John Dyer
// based on the algorithm by Oudin (1940) from http://www.tondering.dk/claus/cal/easter.php
var calcEaster = function calcEaster(year) {
var f = Math.floor,
// Golden Number - 1
G = year % 19,
C = f(year / 100),
// related to Epact
H = (C - f(C / 4) - f((8 * C + 13) / 25) + 19 * G + 15) % 30,
// number of days from 21 March to the Paschal full moon
I = H - f(H / 28) * (1 - f(29 / (H + 1)) * f((21 - G) / 11)),
// weekday for the Paschal full moon
J = (year + f(year / 4) + I + 2 - C + f(C / 4)) % 7,
// number of days from 21 March to the Sunday on or before the Paschal full moon
L = I - J,
month = 3 + f((L + 40) / 44),
date = L + 28 - 31 * f(month / 4);
month = month === 4 ? 'April' : 'March';
return month + ' ' + date;
};
var calcEaster_1 = calcEaster;
var easterDates = function easterDates(str, normal, year, tz) {
if (easterHolidays.hasOwnProperty(str) || easterHolidays.hasOwnProperty(normal)) {
var days = easterHolidays[str] || easterHolidays[normal] || [];
var date = calcEaster_1(year);
if (!date) {
return null; //no easter for this year
}
var e = spacetime__default['default'](date, tz);
e = e.year(year);
var s = e.add(days, 'day');
if (s.isValid()) {
return s;
}
}
return null;
};
var _03EasterDates = easterDates; // http://www.astropixels.com/ephemeris/soleq2001.html
// years 2000-2100
var exceptions = {
spring: [2003, 2007, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2076, 2077, 2080, 2081, 2084, 2085, 2088, 2089, 2092, 2093, 2096, 2097],
summer: [2021, 2016, 2020, 2024, 2028, 2032, 2036, 2040, 2041, 2044, 2045, 2048, 2049, 2052, 2053, 2056, 2057, 2060, 2061, 2064, 2065, 2068, 2069, 2070, 2072, 2073, 2074, 2076, 2077, 2078, 2080, 2081, 2082, 2084, 2085, 2086, 2088, 2089, 2090, 2092, 2093, 2094, 2096, 2097, 2098, 2099],
fall: [2002, 2003, 2004, 2006, 2007, 2010, 2011, 2014, 2015, 2018, 2019, 2022, 2023, 2026, 2027, 2031, 2035, 2039, 2043, 2047, 2051, 2055, 2059, 2092, 2096],
winter: [2002, 2003, 2006, 2007, 2011, 2015, 2019, 2023, 2027, 2031, 2035, 2039, 2043, 2080, 2084, 2088, 2092, 2096]
};
var winter20th = [2080, 2084, 2088, 2092, 2096];
var calcSeasons = function calcSeasons(year) {
// most common defaults
var res = {
spring: 'March 20 ' + year,
summer: 'June 21 ' + year,
fall: 'Sept 22 ' + year,
winter: 'Dec 21 ' + year
};
if (exceptions.spring.indexOf(year) !== -1) {
res.spring = 'March 19 ' + year;
}
if (exceptions.summer.indexOf(year) !== -1) {
res.summer = 'June 20 ' + year;
}
if (exceptions.fall.indexOf(year) !== -1) {
res.fall = 'Sept 21 ' + year;
} // winter can be 20th, 21st, or 22nd
if (exceptions.winter.indexOf(year) !== -1) {
res.winter = 'Dec 22 ' + year;
}
if (winter20th.indexOf(year) !== -1) {
res.winter = 'Dec 20 ' + year;
}
return res;
};
var seasons = calcSeasons; // these are properly calculated in ./lib/seasons
var dates$1 = {
'spring equinox': 'spring',
'summer solistice': 'summer',
'fall equinox': 'fall',
'winter solstice': 'winter'
}; // aliases
dates$1['march equinox'] = dates$1['spring equinox'];
dates$1['vernal equinox'] = dates$1['spring equinox'];
dates$1['ostara'] = dates$1['spring equinox'];
dates$1['june solstice'] = dates$1['summer solistice'];
dates$1['litha'] = dates$1['summer solistice'];
dates$1['autumn equinox'] = dates$1['fall equinox'];
dates$1['autumnal equinox'] = dates$1['fall equinox'];
dates$1['september equinox'] = dates$1['fall equinox'];
dates$1['sept equinox'] = dates$1['fall equinox'];
dates$1['mabon'] = dates$1['fall equinox'];
dates$1['december solstice'] = dates$1['winter solistice'];
dates$1['dec solstice'] = dates$1['winter solistice'];
dates$1['yule'] = dates$1['winter solistice'];
var astroHolidays = dates$1;
var astroDates = function astroDates(str, normal, year, tz) {
if (astroHolidays.hasOwnProperty(str) || astroHolidays.hasOwnProperty(normal)) {
var season = astroHolidays[str] || astroHolidays[normal];
var seasons$1 = seasons(year);
if (!season || !seasons$1 || !seasons$1[season]) {
return null; // couldn't figure it out
}
var s = spacetime__default['default'](seasons$1[season], tz);
if (s.isValid()) {
return s;
}
}
return null;
};
var _04Astronomical = astroDates;
var dates$2 = {
// Muslim holidays
'isra and miraj': 'april 13',
'lailat al-qadr': 'june 10',
'eid al-fitr': 'june 15',
'id al-Fitr': 'june 15',
'eid ul-Fitr': 'june 15',
ramadan: 'may 16',
// Range holiday
'eid al-adha': 'sep 22',
muharram: 'sep 12',
'prophets birthday': 'nov 21'
};
var lunarHolidays = dates$2;
var dayDiff = -10.64;
var lunarDates = function lunarDates(str, normal, year, tz) {
if (lunarHolidays.hasOwnProperty(str) || lunarHolidays.hasOwnProperty(normal)) {
var date = lunarHolidays[str] || lunarHolidays[normal] || [];
if (!date) {
return null;
} // start at 2018
var s = spacetime__default['default'](date + ' 2018', tz);
var diff = year - 2018;
var toAdd = diff * dayDiff;
s = s.add(toAdd, 'day');
s = s.startOf('day'); // now set the correct year
s = s.year(year);
if (s.isValid()) {
return s;
}
}
return null;
};
var _05LunarDates = lunarDates;
var nowYear = spacetime__default['default'].now().year();
var spacetimeHoliday = function spacetimeHoliday(str, year, tz) {
year = year || nowYear;
str = str || '';
str = String(str);
str = str.trim().toLowerCase();
str = str.replace(/'s/, 's'); // 'mother's day'
var normal = str.replace(/ day$/, '');
normal = normal.replace(/^the /, '');
normal = normal.replace(/^orthodox /, ''); //orthodox good friday
// try easier, unmoving holidays
var s = _01FixedDates(str, normal, year, tz);
if (s !== null) {
return s;
} // try 'nth monday' holidays
s = _02NthWeekday(str, normal, year, tz);
if (s !== null) {
return s;
} // easter-based holidays
s = _03EasterDates(str, normal, year, tz);
if (s !== null) {
return s;
} // solar-based holidays
s = _04Astronomical(str, normal, year, tz);
if (s !== null) {
return s;
} // mostly muslim holidays
s = _05LunarDates(str, normal, year, tz);
if (s !== null) {
return s;
}
return null;
};
var src = spacetimeHoliday;
return src;
});
});
var Holiday$1 = units.Holiday;
var parseHoliday = function parseHoliday(doc, context) {
var unit = null;
var m = doc.match('[<holiday>#Holiday+] [<year>#Year?]');
var year = context.today.year();
if (m.groups('year').found) {
year = Number(m.groups('year').text('reduced')) || year;
}
var str = m.groups('holiday').text('reduced');
var s = spacetimeHoliday(str, year, context.timezone);
if (s !== null) {
// assume the year in the future..
if (s.isBefore(context.today) && year === context.today.year()) {
s = spacetimeHoliday(str, year + 1, context.timezone);
}
unit = new Holiday$1(s, null, context);
}
return unit;
};
var _02Holidays = parseHoliday;
var Week$1 = units.Week,
WeekEnd$1 = units.WeekEnd,
AnyMonth$1 = units.AnyMonth,
AnyQuarter$1 = units.AnyQuarter,
Year$1 = units.Year,
Season$1 = units.Season,
WeekDay$1 = units.WeekDay,
Day$2 = units.Day,
Hour$2 = units.Hour,
Minute$1 = units.Minute,
Moment$2 = units.Moment;
var mapping = {
day: Day$2,
hour: Hour$2,
evening: Hour$2,
second: Moment$2,
milliscond: Moment$2,
instant: Moment$2,
minute: Minute$1,
week: Week$1,
weekend: WeekEnd$1,
month: AnyMonth$1,
quarter: AnyQuarter$1,
year: Year$1,
season: Season$1,
// set aliases
yr: Year$1,
qtr: AnyQuarter$1,
wk: Week$1,
sec: Moment$2,
hr: Hour$2
};
var matchStr = "^(".concat(Object.keys(mapping).join('|'), ")$"); // when a unit of time is spoken of as 'this month' - instead of 'february'
var nextLast = function nextLast(doc, context) {
//this month, last quarter, next year
var m = doc.match(matchStr);
if (m.found === true) {
var str = m.text('reduced');
if (mapping.hasOwnProperty(str)) {
var Model = mapping[str];
if (!Model) {
return null;
}
var unit = new Model(null, str, context);
return unit;
}
} //try this version - 'next friday, last thursday'
m = doc.match('^#WeekDay$');
if (m.found === true) {
var _str = m.text('reduced');
var _unit = new WeekDay$1(_str, null, context);
return _unit;
}
return null;
};
var _03NextLast = nextLast;
var Quarter$1 = units.Quarter,
Season$2 = units.Season,
Year$2 = units.Year;
var fmtToday = function fmtToday(context) {
return {
date: context.today.date(),
month: context.today.month(),
year: context.today.year()
};
};
var parseYearly = function parseYearly(doc, context) {
// support 'summer 2002'
var m = doc.match('(spring|summer|winter|fall|autumn) [<year>#Year?]');
if (m.found) {
var str = doc.text('reduced');
var s = spacetime(str, context.timezone, {
today: fmtToday(context)
});
var unit = new Season$2(s, null, context);
if (unit.d.isValid() === true) {
return unit;
}
} // support 'q4 2020'
m = doc.match('[<q>#FinancialQuarter] [<year>#Year?]');
if (m.found) {
var _str = m.groups('q').text('reduced');
var _s = spacetime(_str, context.timezone, {
today: fmtToday(context)
});
if (m.groups('year')) {
var year = Number(m.groups('year').text()) || context.today.year();
_s = _s.year(year);
}
var _unit = new Quarter$1(_s, null, context);
if (_unit.d.isValid() === true) {
return _unit;
}
} // support '4th quarter 2020'
m = doc.match('[<q>#Value] quarter (of|in)? [<year>#Year?]');
if (m.found) {
var q = m.groups('q').text('reduced');
var _s2 = spacetime("q".concat(q), context.timezone, {
today: fmtToday(context)
});
if (m.groups('year')) {
var _year = Number(m.groups('year').text()) || context.today.year();
_s2 = _s2.year(_year);
}
var _unit2 = new Quarter$1(_s2, null, context);
if (_unit2.d.isValid() === true) {
return _unit2;
}
} // support '2020'
m = doc.match('^#Year$');
if (m.found) {
var _str2 = doc.text('reduced');
var _s3 = spacetime(null, context.timezone, {
today: fmtToday(context)
});
_s3 = _s3.year(_str2);
var _unit3 = new Year$2(_s3, null, context);
if (_unit3.d.isValid() === true) {
return _unit3;
}
}
return null;
};
var _04Yearly = parseYearly;
var Day$3 = units.Day,
CalendarDate$1 = units.CalendarDate,
Month$1 = units.Month,
Moment$3 = units.Moment; // parse things like 'june 5th 2019'
// most of this is done in spacetime
var parseExplicit = function parseExplicit(doc, context) {
var impliedYear = context.today.year(); // 'fifth of june 1992'
// 'june the fifth 1992'
var m = doc.match('[<date>#Value] of? [<month>#Month] [<year>#Year]');
if (!m.found) {
m = doc.match('[<month>#Month] the? [<date>#Value] [<year>#Year]');
}
if (m.found) {
var obj = {
month: m.groups('month').text(),
date: m.groups('date').text(),
year: m.groups('year').text() || impliedYear
};
var _unit = new CalendarDate$1(obj, null, context);
if (_unit.d.isValid() === true) {
return _unit;
}
} // 'march 1992'
m = doc.match('[<month>#Month] of? [<year>#Year]');
if (m.found) {
var _obj = {
month: m.groups('month').text(),
year: m.groups('year').text() || impliedYear
};
var _unit2 = new Month$1(_obj, null, context);
if (_unit2.d.isValid() === true) {
return _unit2;
}
} //no-years
// 'fifth of june'
m = doc.match('[<date>#Value] of? [<month>#Month]'); // 'june the fifth'
if (!m.found) {
m = doc.match('[<month>#Month] the? [<date>#Value]');
}
if (m.found) {
var _obj2 = {
month: m.groups('month').text(),
date: m.groups('date').text(),
year: context.today.year()
};
var _unit3 = new CalendarDate$1(_obj2, null, context); // assume 'feb' in the future
if (_unit3.d.month() < context.today.month()) {
_obj2.year += 1;
_unit3 = new CalendarDate$1(_obj2, null, context);
}
if (_unit3.d.isValid() === true) {
return _unit3;
}
} // support 'december'
if (doc.has('#Month')) {
var _obj3 = {
month: doc.match('#Month').text(),
date: 1,
//assume 1st
year: context.today.year()
};
var _unit4 = new Month$1(_obj3, null, context); // assume 'feb' in the future
if (_unit4.d.month() < context.today.month()) {
_obj3.year += 1;
_unit4 = new Month$1(_obj3, null, context);
}
if (_unit4.d.isValid() === true) {
return _unit4;
}
} // support 'thursday 21st'
m = doc.match('#WeekDay [<date>#Value]');
if (m.found) {
var _obj4 = {
month: context.today.month(),
date: m.groups('date').text(),
year: context.today.year()
};
var _unit5 = new CalendarDate$1(_obj4, null, context);
if (_unit5.d.isValid() === true) {
return _unit5;
}
} // support date-only 'the 21st'
m = doc.match('the [<date>#Value]');
if (m.found) {
var _obj5 = {
month: context.today.month(),
date: m.groups('date').text(),
year: context.today.year()
};
var _unit6 = new CalendarDate$1(_obj5, null, context);
if (_unit6.d.isValid() === true) {
// assume it's forward
if (_unit6.d.isBefore(context.today)) {
_unit6.d = _unit6.d.add(1, 'month');
}
return _unit6;
}
} // parse ISO as a Moment
m = doc.match('/[0-9]{4}-[0-9]{2}-[0-9]{2}t[0-9]{2}:/');
if (m.found) {
var _str = doc.text('reduced');
var _unit7 = new Moment$3(_str, null, context);
if (_unit7.d.isValid() === true) {
return _unit7;
}
}
var str = doc.text('reduced'); // punt it to spacetime, for the heavy-lifting
var unit = new Day$3(str, null, context); // did we find a date?
if (unit.d.isValid() === false) {
return null;
}
return unit;
};
var _05Explicit = parseExplicit;
var Quarter$2 = units.Quarter,
Season$3 = units.Season,
Week$2 = units.Week,
Day$4 = units.Day,
Hour$3 = units.Hour,
Minute$2 = units.Minute,
Month$2 = units.Month,
WeekEnd$2 = units.WeekEnd;
var units$1 = {
day: Day$4,
week: Week$2,
weekend: WeekEnd$2,
month: Month$2,
quarter: Quarter$2,
season: Season$3,
hour: Hour$3,
minute: Minute$2
};
var applyCounter = function applyCounter(unit) {
var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var Unit = units$1[counter.unit];
if (!Unit) {
return unit;
}
var d = unit.d; // support 'first' or 0th
if (counter.dir === 'first' || counter.num === 0) {
d = unit.start().d;
d = d.startOf(counter.unit);
} else if (counter.dir === 'last') {
d = d.endOf(unit.unit);
d = d.startOf(counter.unit);
} else if (counter.num) {
// support 'nth week', eg.
d = d.add(counter.num, counter.unit);
}
var u = new Unit(d, null, unit.context);
if (u.d.isValid() === true) {
return u;
}
return unit; //fallback
};
var addCounter = applyCounter;
var tokens = {
shift: _01Shift,
counter: _02Counter,
time: _03Time,
relative: _04Relative,
section: _05Section,
timezone: _06Timezone
};
var parse = {
today: _01Today,
holiday: _02Holidays,
nextLast: _03NextLast,
yearly: _04Yearly,
explicit: _05Explicit
};
var transform = {
counter: addCounter
};
var parseDate = function parseDate(doc, context) {
// quick normalization
doc.match('[^the] !#Value', 0).remove(); // keep 'the 17th'
//parse-out any sections
var shift = tokens.shift(doc);
var counter = tokens.counter(doc);
var tz = tokens.timezone(doc);
var time = tokens.time(doc, context);
var section = tokens.section(doc, context);
var rel = tokens.relative(doc); //set our new timezone
if (tz) {
context = Object.assign({}, context, {
timezone: tz
});
var iso = context.today.format('iso-short');
context.today = context.today["goto"](context.timezone).set(iso);
}
var unit = null; //'in two days'
unit = unit || parse.today(doc, context, {
shift: shift,
time: time,
rel: rel
}); // 'this haloween'
unit = unit || parse.holiday(doc, context); // 'this month'
unit = unit || parse.nextLast(doc, context); // 'q2 2002'
unit = unit || parse.yearly(doc, context); // 'this june 2nd'
unit = unit || parse.explicit(doc, context); // doc.debug()
if (!unit) {
return null;
} // 2 days after..
if (shift) {
unit.applyShift(shift); // if (shift.hour || shift.minute || shift.second) {
// console.log(shift)
// unit = new Hour(unit.d, null, unit.context)
// }
} // this/next/last
if (rel) {
unit.applyRel(rel);
} // end of
if (section) {
unit.applySection(section);
} // at 5:40pm
if (time) {
unit.applyTime(time);
} // apply counter
if (counter && counter.unit) {
unit = transform.counter(unit, counter);
} // debugging
// console.log('\n\n=-=-=-=-=-=-=-=-=-=-=-=Date-=-=-=-=-=-=-=-=-=-=-=-=-\n')
// console.log(` shift: ${JSON.stringify(shift)}`)
// console.log(` counter: `, counter)
// console.log(` rel: ${rel || '-'}`)
// console.log(` section: ${section || '-'}`)
// console.log(` time: ${time || '-'}`)
// console.log(` str: '${doc.text()}'`)
// console.log(' unit: ', unit, '\n')
// doc.debug()
// console.log('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n')
return unit;
};
var parse_1 = parseDate;
var punt = function punt(unit, context) {
unit = unit.applyShift(context.punt);
return unit;
};
var ranges = [{
// two explicit dates - 'between friday and sunday'
match: 'between [<start>*] and [<end>*]',
parse: function parse(m, context) {
var start = m.groups('start');
start = parse_1(start, context);
var end = m.groups('end');
end = parse_1(end, context);
if (start && end) {
return {
start: start,
end: end.before()
};
}
return null;
}
}, {
// two months, no year - 'june 5 to june 7'
match: '[<from>#Month #Value] (to|through|thru) [<to>#Month #Value] [<year>#Year?]',
parse: function parse(m, context) {
var res = m.groups();
var start = res.from;
if (res.year) {
start = start.append(res.year);
}
start = parse_1(start, context);
if (start) {
var end = res.to;
if (res.year) {
end = end.append(res.year);
}
end = parse_1(end, context); // reverse the order?
if (start.d.isAfter(end.d)) {
var tmp = start;
start = end;
end = tmp;
}
return {
start: start,
end: end.end()
};
}
return null;
}
}, {
// one month, one year, first form - 'january 5 to 7 1998'
match: '[<month>#Month] [<from>#Value] (to|through|thru) [<to>#Value] of? [<year>#Year]',
parse: function parse(m, context) {
var _m$groups = m.groups(),
month = _m$groups.month,
from = _m$groups.from,
to = _m$groups.to,
year = _m$groups.year;
var year2 = year.clone();
var start = from.prepend(month.text()).append(year.text());
start = parse_1(start, context);
if (start) {
var end = to.prepend(month.text()).append(year2);
end = parse_1(end, context);
return {
start: start,
end: end.end()
};
}
return null;
}
}, {
// one month, one year, second form - '5 to 7 of january 1998'
match: '[<from>#Value] (to|through|thru) [<to>#Value of? #Month of? #Year]',
parse: function parse(m, context) {
var to = m.groups('to');
to = parse_1(to, context);
if (to) {
var fromDate = m.groups('to');
var from = to.clone();
from.d = from.d.date(fromDate.text('normal'));
return {
start: from,
end: to.end()
};
}
return null;
}
}, {
// one month, no year - '5 to 7 of january'
match: '[<from>#Value] (to|through|thru) [<to>#Value of? #Month]',
parse: function parse(m, context) {
var to = m.groups('to');
to = parse_1(to, context);
if (to) {
var fromDate = m.groups('from');
var from = to.clone();
from.d = from.d.date(fromDate.text('normal'));
return {
start: from,
end: to.end()
};
}
return null;
}
}, {
// one month, no year - 'january 5 to 7'
match: '[<from>#Month #Value] (to|through|thru) [<to>#Value]',
parse: function parse(m, context) {
var from = m.groups('from');
from = parse_1(from, context);
if (from) {
var toDate = m.groups('to');
var to = from.clone();
to.d = to.d.date(toDate.text('normal'));
return {
start: from,
end: to.end()
};
}
return null;
}
}, {
// 'from A to B'
match: 'from? [<from>*] (to|until|upto|through|thru) [<to>*]',
parse: function parse(m, context) {
var from = m.groups('from');
var to = m.groups('to');
from = parse_1(from, context);
to = parse_1(to, context);
if (from && to) {
return {
start: from,
end: to.end()
};
}
return null;
}
}, // {
// // 'A through B' (inclusive end)
// match: 'from? [<a>*] (through|thru) [<b>*]',
// parse: (m, context) => {
// let from = m.groups('a')
// let to = m.groups('b')
// from = parseDate(from, context)
// to = parseDate(to, context)
// if (from && to) {
// return {
// start: from,
// end: to.end(),
// }
// }
// return null
// },
// },
// {
// // 'A until B' (not inclusive end)
// match: 'from? [<a>*] (to|until|upto) [<b>*]',
// parse: (m, context) => {
// let from = m.groups('a')
// let to = m.groups('b')
// from = parseDate(from, context)
// to = parseDate(to, context)
// if (from && to) {
// return {
// start: from,
// end: to.end(),
// }
// }
// return null
// },
// },
{
// 'before june'
match: '^due? (by|before) [*]',
group: 0,
parse: function parse(m, context) {
var unit = parse_1(m, context);
if (unit) {
var start = new Unit_1(context.today, null, context);
if (start.d.isAfter(unit.d)) {
start = unit.clone().applyShift({
weeks: -2
});
} // end the night before
var end = unit.clone().applyShift({
day: -1
});
return {
start: start,
end: end.end()
};
}
return null;
}
}, {
// 'in june'
match: '^(on|in|at|@) [*]',
group: 0,
parse: function parse(m, context) {
var unit = parse_1(m, context);
if (unit) {
return {
start: unit,
end: unit.clone().end()
};
}
return null;
}
}, {
// 'after june'
match: '^(after|following) [*]',
group: 0,
parse: function parse(m, context) {
var unit = parse_1(m, context);
if (unit) {
unit = unit.after();
return {
start: unit.clone(),
end: punt(unit.clone(), context)
};
}
return null;
}
}, {
// 'in june'
match: '^(on|during|in|during) [*]',
group: 0,
parse: function parse(m, context) {
var unit = parse_1(m, context);
if (unit) {
return {
start: unit,
end: unit.clone().end()
};
}
return null;
}
}];
var parseRange = function parseRange(doc, context) {
// try each template in order
for (var i = 0; i < ranges.length; i += 1) {
var fmt = ranges[i];
var m = doc.match(fmt.match);
if (m.found) {
if (fmt.group !== undefined) {
m = m.groups(fmt.group);
}
var res = fmt.parse(m, context);
if (res !== null) {
// console.log(fmt.match)
return res;
}
}
} //else, try whole thing
var unit = parse_1(doc, context);
if (unit) {
return {
start: unit,
end: unit.clone().end()
};
}
return {
start: null,
end: null
};
};
var _02Ranges = parseRange;
var normalize$1 = function normalize(doc) {
doc = doc.clone();
if (!doc.numbers) {
console.warn("Compromise: compromise-dates cannot find plugin dependency 'compromise-number'");
} else {
// convert 'two' to 2
var num = doc.numbers();
num.toNumber();
num.toCardinal(false); // num.normalize()
} // // expand 'aug 20-21'
doc.contractions().expand(); // // remove adverbs
doc.adverbs().remove(); // // 'week-end'
doc.replace('week end', 'weekend').tag('Date'); // // 'a up to b'
doc.replace('up to', 'upto').tag('Date'); // 'in a few years'
var m = doc.match('in [a few] #Duration');
if (m.found) {
m.groups('0').replaceWith('2');
m.tag('DateShift');
}
return doc;
};
var normalize_1 = normalize$1;
var getDate = function getDate(doc, context) {
// validate context a bit
context = context || {};
context.timezone = context.timezone || 'ETC/UTC';
context.today = spacetime(context.today || null, context.timezone); //turn 'five' into 5..
doc = normalize_1(doc); //interpret 'between [A] and [B]'...
return _02Ranges(doc, context);
};
var find = getDate;
var arr = [['mon', 'monday'], ['tue', 'tuesday'], ['tues', 'tuesday'], ['wed', 'wednesday'], ['thu', 'thursday'], ['thurs', 'thursday'], ['fri', 'friday'], ['sat', 'saturday'], ['sun', 'sunday'], ['jan', 'january'], ['feb', 'february'], ['mar', 'march'], ['apr', 'april'], ['jun', 'june'], ['jul', 'july'], ['aug', 'august'], ['sep', 'september'], ['sept', 'september'], ['oct', 'october'], ['nov', 'november'], ['dec', 'december']];
arr = arr.map(function (a) {
return {
"short": a[0],
"long": a[1]
};
});
var _abbrevs = arr;
var methods$1 = {
/** overload the original json with noun information */
json: function json(options) {
var _this = this;
var n = null;
if (typeof options === 'number') {
n = options;
options = null;
}
options = options || {
terms: false
};
var res = [];
var format = options.format || 'iso';
this.forEach(function (doc) {
var json = doc.json(options)[0];
var obj = find(doc, _this.context);
var start = obj.start ? obj.start.format(format) : null;
var end = obj.end ? obj.end.format(format) : null; // set iso strings to json result
json.date = {
start: start,
end: end
}; // add duration
if (start && end) {
json.date.duration = obj.start.d.diff(obj.end.d); // we don't need these
delete json.date.duration.milliseconds;
delete json.date.duration.seconds;
}
res.push(json);
});
if (n !== null) {
return res[n];
}
return res;
},
/** render all dates according to a specific format */
format: function format(fmt) {
var _this2 = this;
this.forEach(function (doc) {
var obj = find(doc, _this2.context);
var str = '';
if (obj.start) {
str = obj.start.format(fmt);
if (obj.end) {
var end = obj.start.format(fmt);
if (str !== end) {
str += ' to ' + end;
}
}
doc.replaceWith(str, {
keepTags: true,
keepCase: false
});
}
});
return this;
},
/** replace 'Fri' with 'Friday', etc*/
toLongForm: function toLongForm() {
var _this3 = this;
_abbrevs.forEach(function (a) {
_this3.replace(a["short"], a["long"], true);
});
return this;
},
/** replace 'Friday' with 'Fri', etc*/
toShortForm: function toShortForm() {
var _this4 = this;
_abbrevs.forEach(function (a) {
_this4.replace(a["long"], a["short"], true);
});
return this;
}
};
var opts = {
punt: {
weeks: 2
}
};
var addMethods = function addMethods(Doc, world) {
// our new tags
world.addTags(_tags); // add info for the date plugin
world.addWords(words); // run our tagger
world.postProcess(_01Tagger);
/** */
var Dates = /*#__PURE__*/function (_Doc) {
_inherits(Dates, _Doc);
var _super = _createSuper(Dates);
function Dates(list, from, w) {
var _this;
_classCallCheck(this, Dates);
_this = _super.call(this, list, from, w);
_this.context = opts;
return _this;
}
return Dates;
}(Doc); //add-in methods
Object.assign(Dates.prototype, methods$1);
Doc.prototype.dates = function (n) {
var context = {};
if (n && _typeof(n) === 'object') {
context = n;
n = null;
}
context = Object.assign({}, context, opts); // let r = this.clauses()
var dates = this.match('#Date+');
if (typeof n === 'number') {
dates = dates.get(n);
}
var d = new Dates(dates.list, this, this.world);
if (context.today) {
context.today = spacetime(context.today, context.timezone);
}
d.context = context;
return d;
};
};
var src = addMethods;
return src;
})));
//# sourceMappingURL=compromise-dates.js.map
};
BundleModuleCode['nlp/compromise-numbers']=function (module,exports){
/* compromise-numbers 1.1.0 MIT */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.compromiseNumbers = factory());
}(this, (function () { 'use strict';
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
var tens = 'twenty|thirty|forty|fifty|sixty|seventy|eighty|ninety|fourty';
var teens = 'eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen'; // this is a bit of a mess
var findNumbers = function findNumbers(doc, n) {
var match = doc.match('#Value+'); //"50 83"
if (match.has('#NumericValue #NumericValue')) {
//a comma may mean two numbers
if (match.has('#Value @hasComma #Value')) {
match.splitAfter('@hasComma');
} else if (match.has('#NumericValue #Fraction')) {
match.splitAfter('#NumericValue #Fraction');
} else {
match = match.splitAfter('#NumericValue');
}
} //three-length
if (match.has('#Value #Value #Value') && !match.has('#Multiple')) {
//twenty-five-twenty
if (match.has('(' + tens + ') #Cardinal #Cardinal')) {
match = match.splitAfter('(' + tens + ') #Cardinal');
}
} //two-length ones
if (match.has('#Value #Value')) {
//june 21st 1992 is two seperate values
if (match.has('#NumericValue #NumericValue')) {
match = match.splitOn('#Year');
} //sixty fifteen
if (match.has('(' + tens + ') (' + teens + ')')) {
match = match.splitAfter('(' + tens + ')');
} //"72 82"
var _double = match.match('#Cardinal #Cardinal');
if (_double.found && !match.has('(point|decimal)')) {
//not 'two hundred'
if (!_double.has('#Cardinal (#Multiple|point|decimal)')) {
//one proper way, 'twenty one', or 'hundred one'
if (!_double.has('(' + tens + ') #Cardinal') && !_double.has('#Multiple #Value')) {
// double = double.firstTerm()
_double.terms().forEach(function (d) {
match = match.splitOn(d);
});
}
}
} //seventh fifth
if (match.match('#Ordinal #Ordinal').match('#TextValue').found && !match.has('#Multiple')) {
//the one proper way, 'twenty first'
if (!match.has('(' + tens + ') #Ordinal')) {
match = match.splitAfter('#Ordinal');
}
} //fifth five
if (match.has('#Ordinal #Cardinal')) {
match = match.splitBefore('#Cardinal+');
} //five 2017 (support '5 hundred', and 'twenty 5'
if (match.has('#TextValue #NumericValue') && !match.has('(' + tens + '|#Multiple)')) {
match = match.splitBefore('#NumericValue+');
}
} //5-8
if (match.has('#NumberRange')) {
match = match.splitAfter('#NumberRange');
} //grab (n)th result
if (typeof n === 'number') {
match = match.get(n);
}
return match;
};
var find = findNumbers;
//support global multipliers, like 'half-million' by doing 'million' then multiplying by 0.5
var findModifiers = function findModifiers(str) {
var mults = [{
reg: /^(minus|negative)[\s\-]/i,
mult: -1
}, {
reg: /^(a\s)?half[\s\-](of\s)?/i,
mult: 0.5
} // {
// reg: /^(a\s)?quarter[\s\-]/i,
// mult: 0.25
// }
];
for (var i = 0; i < mults.length; i++) {
if (mults[i].reg.test(str) === true) {
return {
amount: mults[i].mult,
str: str.replace(mults[i].reg, '')
};
}
}
return {
amount: 1,
str: str
};
};
var findModifiers_1 = findModifiers;
var data = {
ones: {
zeroth: 0,
first: 1,
second: 2,
third: 3,
fourth: 4,
fifth: 5,
sixth: 6,
seventh: 7,
eighth: 8,
ninth: 9,
zero: 0,
one: 1,
two: 2,
three: 3,
four: 4,
five: 5,
six: 6,
seven: 7,
eight: 8,
nine: 9
},
teens: {
tenth: 10,
eleventh: 11,
twelfth: 12,
thirteenth: 13,
fourteenth: 14,
fifteenth: 15,
sixteenth: 16,
seventeenth: 17,
eighteenth: 18,
nineteenth: 19,
ten: 10,
eleven: 11,
twelve: 12,
thirteen: 13,
fourteen: 14,
fifteen: 15,
sixteen: 16,
seventeen: 17,
eighteen: 18,
nineteen: 19
},
tens: {
twentieth: 20,
thirtieth: 30,
fortieth: 40,
fourtieth: 40,
fiftieth: 50,
sixtieth: 60,
seventieth: 70,
eightieth: 80,
ninetieth: 90,
twenty: 20,
thirty: 30,
forty: 40,
fourty: 40,
fifty: 50,
sixty: 60,
seventy: 70,
eighty: 80,
ninety: 90
},
multiples: {
hundredth: 100,
thousandth: 1000,
millionth: 1e6,
billionth: 1e9,
trillionth: 1e12,
quadrillionth: 1e15,
quintillionth: 1e18,
sextillionth: 1e21,
septillionth: 1e24,
hundred: 100,
thousand: 1000,
million: 1e6,
billion: 1e9,
trillion: 1e12,
quadrillion: 1e15,
quintillion: 1e18,
sextillion: 1e21,
septillion: 1e24,
grand: 1000
}
};
var isValid = function isValid(w, has) {
if (data.ones.hasOwnProperty(w)) {
if (has.ones || has.teens) {
return false;
}
} else if (data.teens.hasOwnProperty(w)) {
if (has.ones || has.teens || has.tens) {
return false;
}
} else if (data.tens.hasOwnProperty(w)) {
if (has.ones || has.teens || has.tens) {
return false;
}
}
return true;
};
var validate = isValid;
var parseDecimals = function parseDecimals(arr) {
var str = '0.';
for (var i = 0; i < arr.length; i++) {
var w = arr[i];
if (data.ones.hasOwnProperty(w) === true) {
str += data.ones[w];
} else if (data.teens.hasOwnProperty(w) === true) {
str += data.teens[w];
} else if (data.tens.hasOwnProperty(w) === true) {
str += data.tens[w];
} else if (/^[0-9]$/.test(w) === true) {
str += w;
} else {
return 0;
}
}
return parseFloat(str);
};
var parseDecimals_1 = parseDecimals;
//parse a string like "4,200.1" into Number 4200.1
var parseNumeric = function parseNumeric(str) {
//remove ordinal - 'th/rd'
str = str.replace(/1st$/, '1');
str = str.replace(/2nd$/, '2');
str = str.replace(/3rd$/, '3');
str = str.replace(/([4567890])r?th$/, '$1'); //remove prefixes
str = str.replace(/^[$€¥£¢]/, ''); //remove suffixes
str = str.replace(/[%$€¥£¢]$/, ''); //remove commas
str = str.replace(/,/g, ''); //split '5kg' from '5'
str = str.replace(/([0-9])([a-z\u00C0-\u00FF]{1,2})$/, '$1');
return str;
};
var parseNumeric_1 = parseNumeric;
var improperFraction = /^([0-9,\. ]+)\/([0-9,\. ]+)$/; //some numbers we know
var casualForms = {
// 'a few': 3,
'a couple': 2,
'a dozen': 12,
'two dozen': 24,
zero: 0
}; // a 'section' is something like 'fifty-nine thousand'
// turn a section into something we can add to - like 59000
var section_sum = function section_sum(obj) {
return Object.keys(obj).reduce(function (sum, k) {
sum += obj[k];
return sum;
}, 0);
}; //turn a string into a number
var parse = function parse(str) {
//convert some known-numbers
if (casualForms.hasOwnProperty(str) === true) {
return casualForms[str];
} //'a/an' is 1
if (str === 'a' || str === 'an') {
return 1;
}
var modifier = findModifiers_1(str);
str = modifier.str;
var last_mult = null;
var has = {};
var sum = 0;
var isNegative = false;
var terms = str.split(/[ -]/);
for (var i = 0; i < terms.length; i++) {
var w = terms[i];
w = parseNumeric_1(w);
if (!w || w === 'and') {
continue;
}
if (w === '-' || w === 'negative') {
isNegative = true;
continue;
}
if (w.charAt(0) === '-') {
isNegative = true;
w = w.substr(1);
} //decimal mode
if (w === 'point') {
sum += section_sum(has);
sum += parseDecimals_1(terms.slice(i + 1, terms.length));
sum *= modifier.amount;
return sum;
} //improper fraction
var fm = w.match(improperFraction);
if (fm) {
var num = parseFloat(fm[1].replace(/[, ]/g, ''));
var denom = parseFloat(fm[2].replace(/[, ]/g, ''));
if (denom) {
sum += num / denom || 0;
}
continue;
} //prevent mismatched units, like 'seven eleven'
if (validate(w, has) === false) {
return null;
} //buildOut section, collect 'has' values
if (/^[0-9\.]+$/.test(w)) {
has['ones'] = parseFloat(w); //not technically right
} else if (data.ones.hasOwnProperty(w) === true) {
has['ones'] = data.ones[w];
} else if (data.teens.hasOwnProperty(w) === true) {
has['teens'] = data.teens[w];
} else if (data.tens.hasOwnProperty(w) === true) {
has['tens'] = data.tens[w];
} else if (data.multiples.hasOwnProperty(w) === true) {
var mult = data.multiples[w]; //something has gone wrong : 'two hundred five hundred'
if (mult === last_mult) {
return null;
} //support 'hundred thousand'
//this one is tricky..
if (mult === 100 && terms[i + 1] !== undefined) {
// has['hundreds']=
var w2 = terms[i + 1];
if (data.multiples[w2]) {
mult *= data.multiples[w2]; //hundredThousand/hundredMillion
i += 1;
}
} //natural order of things
//five thousand, one hundred..
if (last_mult === null || mult < last_mult) {
sum += (section_sum(has) || 1) * mult;
last_mult = mult;
has = {};
} else {
//maybe hundred .. thousand
sum += section_sum(has);
last_mult = mult;
sum = (sum || 1) * mult;
has = {};
}
}
} //dump the remaining has values
sum += section_sum(has); //post-process add modifier
sum *= modifier.amount;
sum *= isNegative ? -1 : 1; //dont return 0, if it went straight-through
if (sum === 0 && Object.keys(has).length === 0) {
return null;
}
return sum;
};
var toNumber = parse;
var parseNumeric$1 = function parseNumeric(str, p) {
str = str.replace(/,/g, ''); //parse a numeric-number (easy)
var arr = str.split(/^([^0-9]*)([0-9.,]*)([^0-9]*)$/);
if (arr && arr[2] && p.terms().length < 2) {
var num = parseFloat(arr[2] || str); //ensure that num is an actual number
if (typeof num !== 'number') {
num = null;
} // strip an ordinal off the suffix
var suffix = arr[3] || '';
if (suffix === 'st' || suffix === 'nd' || suffix === 'rd' || suffix === 'th') {
suffix = '';
} // support M for million, k for thousand
if (suffix === 'm' || suffix === 'M') {
num *= 1000000;
suffix = '';
}
if (suffix === 'k' || suffix === 'k') {
num *= 1000;
suffix = '';
}
return {
prefix: arr[1] || '',
num: num,
suffix: suffix
};
}
return null;
}; // get a numeric value from this phrase
var parseNumber = function parseNumber(p) {
var str = p.text('reduced'); // is it in '3,123' format?
var hasComma = /[0-9],[0-9]/.test(p.text('text')); // parse a numeric-number like '$4.00'
var res = parseNumeric$1(str, p);
if (res !== null) {
res.hasComma = hasComma;
return res;
} //parse a text-numer (harder)
var num = toNumber(str);
return {
hasComma: hasComma,
prefix: '',
num: num,
suffix: ''
};
};
var parse$1 = parseNumber;
// handle 'one bottle', 'two bottles'
var agreeUnits = function agreeUnits(agree, val, obj) {
if (agree === false) {
return;
}
var unit = val.lookAhead('^(#Unit|#Noun)'); // don't do these
if (unit.has('(#Address|#Money|#Percent)') || val.has('#Ordinal')) {
return;
}
if (obj.num === 1) {
unit.nouns().toSingular();
} else if (unit.has('#Singular')) {
unit.nouns().toPlural();
}
};
var _agreeUnits = agreeUnits;
/**
* turn big numbers, like 2.3e+22, into a string with a ton of trailing 0's
* */
var numToString = function numToString(n) {
if (n < 1000000) {
return String(n);
}
var str;
if (typeof n === 'number') {
str = n.toFixed(0);
} else {
str = n;
}
if (str.indexOf('e+') === -1) {
return str;
}
return str.replace('.', '').split('e+').reduce(function (p, b) {
return p + Array(b - p.length + 2).join(0);
});
};
var _toString = numToString; // console.log(numToString(2.5e+22));
/**
* turns an integer/float into.ber, like 'fifty-five'
*/
var tens_mapping = [['ninety', 90], ['eighty', 80], ['seventy', 70], ['sixty', 60], ['fifty', 50], ['forty', 40], ['thirty', 30], ['twenty', 20]];
var ones_mapping = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'];
var sequence = [[1e24, 'septillion'], [1e20, 'hundred sextillion'], [1e21, 'sextillion'], [1e20, 'hundred quintillion'], [1e18, 'quintillion'], [1e17, 'hundred quadrillion'], [1e15, 'quadrillion'], [1e14, 'hundred trillion'], [1e12, 'trillion'], [1e11, 'hundred billion'], [1e9, 'billion'], [1e8, 'hundred million'], [1e6, 'million'], [100000, 'hundred thousand'], [1000, 'thousand'], [100, 'hundred'], [1, 'one']]; //turn number into an array of magnitudes, like [[5, million], [2, hundred]]
var breakdown_magnitudes = function breakdown_magnitudes(num) {
var working = num;
var have = [];
sequence.forEach(function (a) {
if (num >= a[0]) {
var howmany = Math.floor(working / a[0]);
working -= howmany * a[0];
if (howmany) {
have.push({
unit: a[1],
count: howmany
});
}
}
});
return have;
}; //turn numbers from 100-0 into their text
var breakdown_hundred = function breakdown_hundred(num) {
var arr = [];
if (num > 100) {
return arr; //something bad happened..
}
for (var i = 0; i < tens_mapping.length; i++) {
if (num >= tens_mapping[i][1]) {
num -= tens_mapping[i][1];
arr.push(tens_mapping[i][0]);
}
} //(hopefully) we should only have 20-0 now
if (ones_mapping[num]) {
arr.push(ones_mapping[num]);
}
return arr;
};
/** print-out 'point eight nine'*/
var handle_decimal = function handle_decimal(num) {
var names = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
var arr = []; //parse it out like a string, because js math is such shit
var str = _toString(num);
var decimal = str.match(/\.([0-9]+)/);
if (!decimal || !decimal[0]) {
return arr;
}
arr.push('point');
var decimals = decimal[0].split('');
for (var i = 0; i < decimals.length; i++) {
arr.push(names[decimals[i]]);
}
return arr;
};
/** turns an integer into a textual number */
var to_text = function to_text(num) {
// handle zero, quickly
if (num === 0 || num === '0') {
return 'zero'; // no?
} //big numbers, north of sextillion, aren't gonna work well..
//keep them small..
if (num > 1e21) {
num = _toString(num);
}
var arr = []; //handle negative numbers
if (num < 0) {
arr.push('minus');
num = Math.abs(num);
} //break-down into units, counts
var units = breakdown_magnitudes(num); //build-up the string from its components
for (var i = 0; i < units.length; i++) {
var unit_name = units[i].unit;
if (unit_name === 'one') {
unit_name = ''; //put an 'and' in here
if (arr.length > 1) {
arr.push('and');
}
}
arr = arr.concat(breakdown_hundred(units[i].count));
arr.push(unit_name);
} //also support decimals - 'point eight'
arr = arr.concat(handle_decimal(num)); //remove empties
arr = arr.filter(function (s) {
return s;
});
if (arr.length === 0) {
arr[0] = '';
}
return arr.join(' ');
};
var toText = to_text; // console.log(to_text(-1000.8));
/**
* turn a number like 5 into an ordinal like 5th
*/
var numOrdinal = function numOrdinal(num) {
if (!num && num !== 0) {
return null;
} //the teens are all 'th'
var tens = num % 100;
if (tens > 10 && tens < 20) {
return String(num) + 'th';
} //the rest of 'em
var mapping = {
0: 'th',
1: 'st',
2: 'nd',
3: 'rd'
};
var str = _toString(num);
var last = str.slice(str.length - 1, str.length);
if (mapping[last]) {
str += mapping[last];
} else {
str += 'th';
}
return str;
};
var numOrdinal_1 = numOrdinal;
var irregulars = {
one: 'first',
two: 'second',
three: 'third',
five: 'fifth',
eight: 'eighth',
nine: 'ninth',
twelve: 'twelfth',
twenty: 'twentieth',
thirty: 'thirtieth',
forty: 'fortieth',
fourty: 'fourtieth',
fifty: 'fiftieth',
sixty: 'sixtieth',
seventy: 'seventieth',
eighty: 'eightieth',
ninety: 'ninetieth'
};
/**
* convert a javascript number to 'twentieth' format
* */
var textOrdinal = function textOrdinal(num) {
var words = toText(num).split(' '); //convert the last number to an ordinal
var last = words[words.length - 1];
if (irregulars.hasOwnProperty(last)) {
words[words.length - 1] = irregulars[last];
} else {
words[words.length - 1] = last.replace(/y$/, 'i') + 'th';
}
return words.join(' ');
};
var textOrdinal_1 = textOrdinal;
var prefixes = {
'¢': 'cents',
$: 'dollars',
'£': 'pounds',
'¥': 'yen',
'€': 'euros',
'₡': 'colón',
'฿': 'baht',
'₭': 'kip',
'₩': 'won',
'₹': 'rupees',
'₽': 'ruble',
'₺': 'liras'
};
var suffixes = {
'%': 'percent',
s: 'seconds',
cm: 'centimetres',
km: 'kilometres'
};
var _symbols = {
prefixes: prefixes,
suffixes: suffixes
};
var prefixes$1 = _symbols.prefixes;
var suffixes$1 = _symbols.suffixes;
var isCurrency = {
usd: true,
eur: true,
jpy: true,
gbp: true,
cad: true,
aud: true,
chf: true,
cny: true,
hkd: true,
nzd: true,
kr: true,
rub: true
}; // convert $ to 'dollars', etc
var prefixToText = function prefixToText(obj) {
// turn 5% to 'five percent'
if (prefixes$1.hasOwnProperty(obj.prefix)) {
obj.suffix += prefixes$1[obj.prefix];
obj.prefix = '';
} //turn 5km to 'five kilometres'
if (suffixes$1.hasOwnProperty(obj.suffix)) {
obj.suffix = suffixes$1[obj.suffix];
} //uppercase lost case for 'USD', etc
if (isCurrency.hasOwnProperty(obj.suffix)) {
obj.suffix = obj.suffix.toUpperCase();
} // add a space, if it exists
if (obj.suffix) {
obj.suffix = ' ' + obj.suffix;
}
return obj;
}; //business-logic for converting a cardinal-number to other forms
var makeNumber = function makeNumber(obj, isText, isOrdinal) {
var num = String(obj.num);
if (isText) {
obj = prefixToText(obj);
if (isOrdinal) {
//ordinal-text
num = textOrdinal_1(num);
return "".concat(obj.prefix || '').concat(num).concat(obj.suffix || '');
} //cardinal-text
num = toText(num);
return "".concat(obj.prefix || '').concat(num).concat(obj.suffix || '');
} //ordinal-number
if (isOrdinal) {
num = numOrdinal_1(num); // support '5th percent'
obj = prefixToText(obj);
return "".concat(obj.prefix || '').concat(num).concat(obj.suffix || '');
} // support comma format
if (obj.hasComma === true) {
num = obj.num.toLocaleString();
} // cardinal-number
num = _toString(num); // support very large numbers
return "".concat(obj.prefix || '').concat(num).concat(obj.suffix || '');
};
var makeNumber_1 = makeNumber;
var methods = {
/** overloaded json method with additional number information */
json: function json(options) {
var n = null;
if (typeof options === 'number') {
n = options;
options = null;
}
options = options || {
text: true,
normal: true,
trim: true,
terms: true
};
var res = [];
this.forEach(function (doc) {
var json = doc.json(options)[0];
var obj = parse$1(doc);
json.prefix = obj.prefix;
json.number = obj.num;
json.suffix = obj.suffix;
json.cardinal = makeNumber_1(obj, false, false);
json.ordinal = makeNumber_1(obj, false, true);
json.textCardinal = makeNumber_1(obj, true, false);
json.textOrdinal = makeNumber_1(obj, true, true);
res.push(json);
});
if (n !== null) {
return res[n];
}
return res;
},
/** two of what? */
units: function units() {
var m = this.lookAhead('(#Unit|#Noun)+');
m = m.splitAfter('@hasComma').first();
m = m.not('#Pronoun');
return m.first();
},
/** return only ordinal numbers */
isOrdinal: function isOrdinal() {
return this["if"]('#Ordinal');
},
/** return only cardinal numbers*/
isCardinal: function isCardinal() {
return this["if"]('#Cardinal');
},
/** convert to numeric form like '8' or '8th' */
toNumber: function toNumber() {
this.forEach(function (val) {
var obj = parse$1(val);
if (obj.num === null) {
return;
}
var str = makeNumber_1(obj, false, val.has('#Ordinal'));
val.replaceWith(str, true);
val.tag('NumericValue');
});
return this;
},
/** add commas, or nicer formatting for numbers */
toLocaleString: function toLocaleString() {
this.forEach(function (val) {
var obj = parse$1(val);
if (obj.num === null) {
return;
}
obj.num = obj.num.toLocaleString();
var str = makeNumber_1(obj, false, val.has('#Ordinal'));
val.replaceWith(str, true);
});
return this;
},
/** convert to text form - like 'eight' or 'eigth'*/
toText: function toText() {
this.forEach(function (val) {
var obj = parse$1(val);
if (obj.num === null) {
return;
}
var str = makeNumber_1(obj, true, val.has('#Ordinal'));
val.replaceWith(str, true);
val.tag('TextValue');
});
return this;
},
/** convert to cardinal form, like 'eight', or '8' */
toCardinal: function toCardinal(agree) {
var m = this["if"]('#Ordinal');
m.forEach(function (val) {
var obj = parse$1(val);
if (obj.num === null) {
return;
}
var str = makeNumber_1(obj, val.has('#TextValue'), false); // a hack for number-ranges
if (val.has('#NumberRange')) {
var t = val.termList()[0];
if (t.text && t.post === '') {
t.post = ' ';
}
} // change the number text
val.replaceWith(str, true);
val.tag('Cardinal'); // turn unit into plural -> 'seven beers'
_agreeUnits(agree, val, obj);
});
return this;
},
/** convert to ordinal form, like 'eighth', or '8th' */
toOrdinal: function toOrdinal() {
var _this = this;
var m = this["if"]('#Cardinal');
m.forEach(function (val) {
var obj = parse$1(val);
if (obj.num === null) {
return;
}
var str = makeNumber_1(obj, val.has('#TextValue'), true); // a hack for number-ranges
if (val.has('#NumberRange')) {
var t = val.termList()[0];
if (t.text && t.post === '') {
t.post = ' ';
}
} // change the number text
val.replaceWith(str, true);
val.tag('Ordinal'); // turn unit into singular -> 'seventh beer'
var unit = _this.lookAhead('^#Plural');
if (unit.found) {
unit.nouns().toSingular();
}
});
return this;
},
/** return only numbers that are == n */
isEqual: function isEqual(n) {
return this.filter(function (val) {
var num = parse$1(val).num;
return num === n;
});
},
/** return only numbers that are > n*/
greaterThan: function greaterThan(n) {
return this.filter(function (val) {
var num = parse$1(val).num;
return num > n;
});
},
/** return only numbers that are < n*/
lessThan: function lessThan(n) {
return this.filter(function (val) {
var num = parse$1(val).num;
return num < n;
});
},
/** return only numbers > min and < max */
between: function between(min, max) {
return this.filter(function (val) {
var num = parse$1(val).num;
return num > min && num < max;
});
},
/** set these number to n */
set: function set(n, agree) {
if (n === undefined) {
return this; // don't bother
}
if (typeof n === 'string') {
n = toNumber(n);
}
this.forEach(function (val) {
var obj = parse$1(val);
obj.num = n;
if (obj.num === null) {
return;
}
var str = makeNumber_1(obj, val.has('#TextValue'), val.has('#Ordinal'));
val = val.not('#Currency');
val.replaceWith(str, true); // handle plural/singular unit
_agreeUnits(agree, val, obj);
});
return this;
},
add: function add(n, agree) {
if (!n) {
return this; // don't bother
}
if (typeof n === 'string') {
n = toNumber(n);
}
this.forEach(function (val) {
var obj = parse$1(val);
if (obj.num === null) {
return;
}
obj.num += n;
var str = makeNumber_1(obj, val.has('#TextValue'), val.has('#Ordinal'));
val = val.not('#Currency');
val.replaceWith(str, true); // handle plural/singular unit
_agreeUnits(agree, val, obj);
});
return this;
},
/** decrease each number by n*/
subtract: function subtract(n, agree) {
return this.add(n * -1, agree);
},
/** increase each number by 1 */
increment: function increment(agree) {
this.add(1, agree);
return this;
},
/** decrease each number by 1 */
decrement: function decrement(agree) {
this.add(-1, agree);
return this;
},
/** return things like CCXX*/
romanNumerals: function romanNumerals(n) {
var m = this.match('#RomanNumeral').numbers();
if (typeof n === 'number') {
m = m.get(n);
}
return m;
},
/** split-apart suffix and number */
normalize: function normalize() {
var keep = {
'%': true
};
this.forEach(function (val) {
var obj = parse$1(val);
if (obj.num !== null && obj.suffix && keep[obj.suffix] !== true) {
var prefix = obj.prefix || '';
val = val.replaceWith(prefix + obj.num + ' ' + obj.suffix);
return;
}
});
return this;
},
/** retrieve the parsed number */
get: function get(n) {
var arr = [];
this.forEach(function (doc) {
arr.push(parse$1(doc).num);
});
if (n !== undefined) {
return arr[n];
}
return arr;
}
}; // aliases
methods.toNice = methods.toLocaleString;
methods.isBetween = methods.between;
methods.minus = methods.subtract;
methods.plus = methods.add;
methods.equals = methods.isEqual;
var methods_1 = methods;
//from wikipedia's {{infobox currency}}, Dec 2020
var currencies = [{
dem: 'american',
name: 'dollar',
iso: 'usd',
sub: 'cent',
sym: ['$', 'US$', 'U$']
}, {
name: 'euro',
iso: 'eur',
sub: 'cent',
sym: ['€']
}, {
dem: 'british',
name: 'pound',
iso: 'gbp',
sub: 'penny',
alias: {
sterling: true
},
sym: ['£']
}, {
name: 'renminbi',
iso: 'cny',
sub: 'yuán',
plural: 'yuán',
alias: {
yuan: true
},
sym: ['元'] //'¥'
}, {
dem: 'japanese',
name: 'yen',
iso: 'jpy',
sub: 'sen',
sym: ['¥', '円', '圓']
}, // kr
{
dem: 'swedish',
name: 'krona',
iso: 'sek',
sub: 'öre',
alias: {
ore: true,
kronor: true
},
sym: ['kr']
}, {
dem: 'estonian',
name: 'kroon',
iso: 'eek',
sub: 'sent',
sym: ['kr']
}, {
dem: 'norwegian',
name: 'krone',
iso: 'nok',
sub: 'øre',
sym: ['kr']
}, {
dem: 'icelandic',
name: 'króna',
iso: 'isk',
sym: ['kr']
}, {
dem: 'danish',
name: 'krone',
iso: 'dkk',
sub: 'øre',
sym: ['kr.']
}, // {
// dem: 'scandinavian',
// name: 'Monetary Union',
// sub: 'øre',
// sym: ['kr.'],
// },
// 'k'
{
dem: 'zambian',
name: 'kwacha',
iso: 'zmw',
sub: 'ngwee',
sym: ['K']
}, {
dem: 'malawian',
name: 'kwacha',
iso: 'mwk',
sub: 'tambala',
sym: ['K']
}, // misc
{
dem: 'greek',
name: 'drachma',
iso: 'grd',
sub: 'leptοn',
sym: ['Δρχ.', 'Δρ.', '₯']
}, {
dem: 'eastern caribbean',
name: 'dollar',
iso: 'xcd',
sub: 'cent',
sym: ['$']
}, {
dem: 'finnish',
name: 'markka',
iso: 'fim',
sub: 'penni',
sym: ['mk']
}, {
dem: 'polish',
name: 'złoty',
iso: 'pln',
sub: 'grosz',
sym: ['zł']
}, {
dem: 'slovenian',
name: 'tolar',
iso: 'sit',
sub: 'stotin',
sym: []
}, {
dem: 'australian',
name: 'dollar',
iso: 'aud',
sub: 'cent',
sym: ['$', 'A$', 'AU$']
}, {
dem: 'deutsche',
name: 'mark',
iso: 'dem',
sub: 'pfennig',
sym: ['DM']
}, {
dem: 'thai',
name: 'baht',
iso: 'thb',
sub: 'satang',
sym: ['฿']
}, {
dem: 'canadian',
name: 'dollar',
iso: 'cad',
sub: 'cent',
sym: ['$', 'Can$', 'C$', 'CA$', 'CAD']
}, {
dem: 'mexican',
name: 'peso',
iso: 'mxn',
sub: 'centavo',
sym: ['$', 'Mex$']
}, {
dem: 'spanish',
name: 'peseta',
iso: 'esp',
sub: 'céntimo',
sym: ['Pta']
}, {
dem: 'new zealand',
name: 'dollar',
iso: 'nzd',
sub: 'cent',
sym: ['$', 'NZ$']
}, {
dem: 'chilean',
name: 'peso',
iso: 'clp',
sub: 'Centavo',
sym: ['Cifrão', '$']
}, {
dem: 'nigerian',
name: 'naira',
iso: 'ngn',
sub: 'kobo',
sym: ['₦']
}, {
dem: 'austrian',
name: 'schilling',
iso: 'ats',
sub: 'groschen',
sym: ['S', 'öS']
}, {
dem: 'guatemalan',
name: 'quetzal',
iso: 'gtq',
sub: 'centavo',
sym: ['Q']
}, {
dem: 'philippine',
name: 'peso',
iso: 'php',
sub: 'sentimo',
sym: ['₱']
}, {
dem: 'hungarian',
name: 'forint',
iso: 'huf',
sub: 'fillér',
sym: ['Ft']
}, {
dem: 'russian',
name: 'ruble',
iso: 'rub',
sub: 'kopeyka',
sym: ['₽', 'руб', 'р.']
}, {
dem: 'kuwaiti',
name: 'dinar',
iso: 'kwd',
sub: 'fils',
sym: ['د.ك', 'KD']
}, {
dem: 'israeli',
name: 'new shekel',
iso: 'ils',
sub: 'agora',
sym: ['₪']
}, {
dem: 'latvian',
name: 'lats',
iso: 'lvl',
sub: 'santīms',
sym: ['Ls']
}, {
dem: 'kazakhstani',
name: 'tenge',
iso: 'kzt',
sub: 'tıyn',
sym: ['₸']
}, {
dem: 'iraqi',
name: 'dinar',
iso: 'iqd',
sub: 'fils',
sym: ['د.ع']
}, {
dem: 'bahamian',
name: 'dollar',
iso: 'bsd',
sub: 'cent',
sym: ['$', 'B$']
}, {
dem: 'seychellois',
name: 'rupee',
iso: 'scr',
sub: 'cent',
sym: ['SCR', 'SR']
}, {
dem: 'albanian',
name: 'lek',
iso: 'all',
sub: 'qindarkë',
sym: ['L']
}, {
dem: 'bulgarian',
name: 'lev',
iso: 'bgn',
sub: 'stotinka',
sym: ['лв.']
}, {
dem: 'irish',
name: 'pound',
iso: 'iep',
sym: ['£', 'IR£']
}, {
name: 'cfp franc',
iso: 'xpf',
sym: ['f']
}, {
dem: 'south african',
name: 'rand',
iso: 'zar',
sub: 'cent',
sym: ['R']
}, {
dem: 'south korean',
name: 'won',
iso: 'krw',
sub: 'jeon',
plural: 'won',
sym: ['₩']
}, {
dem: 'north korean',
name: 'won',
iso: 'kpw',
sub: 'chon',
plural: 'won',
sym: ['₩']
}, {
dem: 'portuguese',
name: 'escudo',
iso: 'pte',
sub: 'centavo',
sym: []
}, {
dem: 'ghanaian',
name: 'cedi',
iso: 'ghs',
sub: 'pesewa',
sym: ['GH₵']
}, {
dem: 'hong kong',
name: 'dollar',
iso: 'hkd',
sub: '毫',
sym: ['$']
}, {
dem: 'new taiwan',
name: 'dollar',
iso: 'twd',
sub: 'dime',
sym: ['NT$']
}, {
dem: 'east german',
name: 'mark',
iso: 'ddm',
sub: 'pfennig',
sym: ['M']
}, {
dem: 'namibian',
name: 'dollar',
iso: 'nad',
sub: 'cent',
sym: ['$']
}, {
dem: 'malaysian',
name: 'ringgit',
iso: 'myr',
sub: 'sen',
sym: ['RM']
}, {
dem: 'swiss',
name: 'franc',
iso: 'chf',
sym: ['Rp.']
}, {
dem: 'panamanian',
name: 'balboa',
iso: 'pab',
sub: 'centésimo',
sym: ['B/.']
}, {
dem: 'indonesian',
name: 'rupiah',
iso: 'idr',
sub: 'sen',
sym: ['Rp']
}, {
dem: 'brunei',
name: 'dollar',
iso: 'bnd',
sub: 'sen',
sym: ['$', 'B$']
}, {
dem: 'venezuelan',
name: 'bolívar',
iso: 'vef',
sub: 'céntimo',
sym: ['Bs.F', 'Bs.']
}, {
dem: 'macedonian',
name: 'denar',
iso: 'mkd',
sub: 'deni',
sym: ['den']
}, {
dem: 'mauritanian',
name: 'ouguiya',
iso: 'mru',
sub: 'khoums',
sym: ['UM']
}, {
dem: 'argentine',
name: 'peso',
iso: 'ars',
sub: 'centavo',
sym: ['$']
}, {
dem: 'libyan',
name: 'dinar',
iso: 'lyd',
sub: 'dirham',
sym: ['LD', 'ل.د']
}, {
dem: 'jordanian',
name: 'dinar',
iso: 'jod',
sub: 'dirham',
sym: ['د.أ']
}, {
dem: 'french',
name: 'franc',
iso: 'frf',
sub: 'centime',
sym: ['F', 'Fr', 'FF', '₣']
}, {
dem: 'syrian',
name: 'pound',
iso: 'syp',
sub: 'piastre',
sym: ['LS', '£S']
}, {
dem: 'belize',
name: 'dollar',
iso: 'bzd',
sub: 'cent',
sym: ['$']
}, {
dem: 'saudi',
name: 'riyal',
iso: 'sar',
sub: 'halalah',
sym: ['SAR', 'ر.س', ' ﷼']
}, {
dem: 'surinamese',
name: 'dollar',
iso: 'srd',
sub: 'cent',
sym: ['$']
}, {
dem: 'singapore',
name: 'dollar',
iso: 'sgd',
sub: 'cent',
sym: ['S$', '$']
}, {
dem: 'nepalese',
name: 'rupee',
iso: 'npr',
sub: 'Paisa',
sym: ['रु ₨', 'Re']
}, {
dem: 'macanese',
name: 'pataca',
iso: 'mop',
sub: 'ho',
sym: ['MOP$']
}, {
dem: 'nicaraguan',
name: 'córdoba',
iso: 'nio',
sub: 'centavo',
sym: ['C$']
}, {
dem: 'bangladeshi',
name: 'taka',
iso: 'bdt',
sub: 'poysha',
sym: ['৳']
}, {
dem: 'indian',
name: 'rupee',
iso: 'inr',
sub: 'paisa',
sym: ['₹']
}, {
dem: 'maldivian',
name: 'rufiyaa',
iso: 'mvr',
sub: 'laari',
sym: ['Rf', 'MRf', 'MVR', '.ރ ']
}, {
dem: 'sri lankan',
name: 'rupee',
iso: 'lkr',
sub: 'Cents',
sym: ['Rs', 'රු', 'ரூ']
}, {
dem: 'bhutanese',
name: 'ngultrum',
iso: 'btn',
sub: 'chhertum',
sym: ['Nu.']
}, {
dem: 'turkish',
name: 'lira',
iso: 'try',
sub: 'new kuruş',
sym: ['YTL']
}, {
dem: 'serbian',
name: 'dinar',
iso: 'rsd',
sub: 'para',
sym: ['din', 'дин']
}, {
dem: 'bosnia and herzegovina',
name: 'convertible mark',
iso: 'bam',
sub: 'Fening/Pfenig',
sym: ['KM']
}, {
dem: 'botswana',
name: 'pula',
iso: 'bwp',
sub: 'thebe',
sym: ['p']
}, {
dem: 'swazi',
name: 'lilangeni',
iso: 'szl',
sub: 'cent',
sym: ['L', 'E']
}, {
dem: 'lithuanian',
name: 'litas',
iso: 'ltl',
sub: 'centas',
sym: ['Lt', 'ct']
}, {
dem: 'mauritian',
name: 'rupee',
iso: 'mur',
sub: 'cent',
sym: ['₨']
}, {
dem: 'pakistani',
name: 'rupee',
iso: 'pkr',
sub: 'Paisa',
sym: ['₨']
}, {
dem: 'maltese',
name: 'lira',
iso: 'mtl',
sub: 'cent',
sym: ['₤', 'Lm']
}, {
dem: 'cypriot',
name: 'pound',
iso: 'cyp',
sub: 'cent',
sym: ['£']
}, {
dem: 'moldovan',
name: 'leu',
iso: 'mdl',
sub: 'ban',
sym: ['l']
}, {
dem: 'croatian',
name: 'kuna',
iso: 'hrk',
sub: 'lipa',
sym: ['kn']
}, {
dem: 'afghan',
name: 'afghani',
iso: 'afn',
sub: 'pul',
sym: ['؋', 'Af', 'Afs']
}, {
dem: 'ecuadorian',
name: 'sucre',
iso: 'ecs',
sub: 'centavo',
sym: ['S/.']
}, {
dem: 'sierra leonean',
name: 'leone',
iso: 'sll',
sub: 'cent',
sym: ['Le']
} // {
//
// name: 'European Currency Unit',
// iso: 'xeu',
// sym: ['₠'],
// },
// {
//
// name: 'Special drawing rights',
// iso: 'xdr',
// sym: ['SDR'],
// },
// {
//
// name: 'Unidad de Valor Constante',
// iso: 'ecv',
// },
];
var symbols = {};
currencies.forEach(function (o) {
o.sym.forEach(function (str) {
symbols[str] = symbols[str] || o.iso;
});
symbols[o.iso] = symbols[o.iso] || o.iso;
}); // parse 'australian dollars'
var getNamedCurrency = function getNamedCurrency(doc) {
var m = doc.match('#Currency+');
m.nouns().toSingular(); // 'dollars'➔'dollar'
var str = m.text('reduced');
return currencies.find(function (o) {
// 'mexcan peso'
if (str === "".concat(o.dem, " ").concat(o.name)) {
return o;
} // 'CAD'
if (str === o.iso) {
return o;
} // 'cent'
if (str === o.sub) {
return o;
} // 'peso'
if (str === o.name) {
return o;
} // any other alt names
if (o.alias && o.alias[str] === true) {
return o;
}
return false;
});
}; // turn '£' into GBP
var getBySymbol = function getBySymbol(obj) {
// do suffix first, for '$50CAD'
if (obj.suffix && symbols.hasOwnProperty(obj.suffix)) {
return currencies.find(function (o) {
return o.iso === symbols[obj.suffix];
});
} // parse prefix for '£50'
if (obj.prefix && symbols.hasOwnProperty(obj.prefix)) {
return currencies.find(function (o) {
return o.iso === symbols[obj.prefix];
});
}
return null;
};
var parseMoney = function parseMoney(doc) {
var res = parse$1(doc);
var found = getBySymbol(res) || getNamedCurrency(doc) || {};
var sym = '';
if (found && found.sym) {
sym = found.sym[0];
}
return {
num: res.num,
iso: found.iso,
demonym: found.dem,
currency: found.name,
plural: found.plural,
symbol: sym
};
};
var parse$2 = parseMoney;
var titleCase = function titleCase() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
};
var moneyMethods = {
/** which currency is this money in? */
currency: function currency(n) {
var arr = [];
this.forEach(function (doc) {
var found = parse$2(doc);
if (found) {
arr.push(found);
}
});
if (typeof n === 'number') {
return arr[n];
}
return arr;
},
/** overloaded json method with additional number information */
json: function json(options) {
var n = null;
if (typeof options === 'number') {
n = options;
options = null;
}
options = options || {
text: true,
normal: true,
trim: true,
terms: true
};
var res = [];
this.forEach(function (doc) {
var json = doc.json(options)[0];
var obj = parse$2(doc);
json.number = obj.num;
if (obj.iso) {
json.iso = obj.iso.toUpperCase();
json.symbol = obj.symbol;
json.currency = titleCase(obj.demonym) + ' ' + titleCase(obj.currency);
} // 'thirty pounds'
json.textFmt = makeNumber_1(obj, true, false);
if (obj.currency) {
var str = obj.currency;
if (obj.num !== 1) {
str = obj.plural || str + 's';
}
json.textFmt += ' ' + str;
}
res.push(json);
});
if (n !== null) {
return res[n] || {};
}
return res;
}
};
var methods$1 = moneyMethods;
var endS = /s$/;
var slashForm = function slashForm(m) {
var str = m.text('reduced');
var found = str.match(/^([-+]?[0-9]+)\/([-+]?[0-9]+)(st|nd|rd|th)?s?$/);
if (found && found[1] && found[0]) {
return {
numerator: Number(found[1]),
denominator: Number(found[2])
};
}
return null;
}; // parse '4 out of 4'
var textForm1 = function textForm1(m) {
var found = m.match('[<num>#Value+] out of every? [<den>#Value+]');
if (found.found !== true) {
return null;
}
var _found$groups = found.groups(),
num = _found$groups.num,
den = _found$groups.den;
num = num.numbers().get(0);
den = den.numbers().get(0);
if (typeof num === 'number' && typeof den === 'number') {
return {
numerator: num,
denominator: den
};
}
return null;
}; // parse 'a third'
var textForm2 = function textForm2(m) {
var found = m.match('[<num>(#Cardinal|a)+] [<den>#Ordinal+]');
if (found.found !== true) {
return null;
}
var _found$groups2 = found.groups(),
num = _found$groups2.num,
den = _found$groups2.den; // quick-support for 'a third'
if (num.has('a')) {
num = 1;
} else {
num = num.numbers().get(0);
} // turn 'thirds' into third
var str = den.text('reduced');
if (endS.test(str)) {
str = str.replace(endS, '');
den.replaceWith(str);
} // support 'one half' as '1/2'
if (den.has('half')) {
den = 2;
} else {
den = den.numbers().get(0);
}
if (typeof num === 'number' && typeof den === 'number') {
return {
numerator: num,
denominator: den
};
}
return null;
};
var parseFraction = function parseFraction(m) {
return slashForm(m) || textForm1(m) || textForm2(m) || null;
};
var parse$3 = parseFraction;
var methods$2 = {
/** overloaded json method with additional number information */
json: function json(options) {
var n = null;
if (typeof options === 'number') {
n = options;
options = null;
}
options = options || {
text: true,
normal: true,
trim: true,
terms: true
};
var res = [];
this.forEach(function (m) {
var json = m.json(options)[0];
var found = parse$3(m) || {};
json.numerator = found.numerator;
json.denominator = found.denominator;
res.push(json);
});
if (n !== null) {
return res[n] || {};
}
return res;
},
/** change 'four out of 10' to 4/10 */
normalize: function normalize() {
var _this = this;
this.forEach(function (m) {
var found = parse$3(m);
if (found && typeof found.numerator === 'number' && typeof found.denominator === 'number') {
var str = "".concat(found.numerator, "/").concat(found.denominator);
_this.replace(m, str);
}
});
return this;
}
};
var methods_1$1 = methods$2;
var here = 'number-tag';
var multiples = '(hundred|thousand|million|billion|trillion|quadrillion|quintillion|sextillion|septillion)'; //support 'two thirds'
// (do this conservatively)
var ordinals = ['half', 'third', 'fourth', 'quarter', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth', 'hundredth', 'thousandth', 'millionth']; // add plural forms
var len = ordinals.length;
for (var i = 0; i < len; i += 1) {
ordinals.push(ordinals[i] + 's');
}
ordinals = "(".concat(ordinals.join('|'), ")"); // improved tagging for numbers
var tagger = function tagger(doc) {
doc.match(multiples).tag('#Multiple', here); // in the 400s
doc.match('the [/[0-9]+s$/]').tag('#Plural', here); //half a million
doc.match('half a? #Value').tag('Value', 'half-a-value'); //(quarter not ready)
//five and a half
doc.match('#Value and a (half|quarter)').tag('Value', 'value-and-a-half'); //one hundred and seven dollars
doc.match('#Money and #Money #Currency?').tag('Money', 'money-and-money'); // $5.032 is invalid money
doc.match('#Money').not('#TextValue').match('/\\.[0-9]{3}$/').unTag('#Money', 'three-decimal money'); // cleanup currency false-positives
doc.ifNo('#Value').match('#Currency #Verb').unTag('Currency', 'no-currency'); // 6 dollars and 5 cents
doc.match('#Value #Currency [and] #Value (cents|ore|centavos|sens)', 0).tag('Money', here); // maybe currencies
var m = doc.match('[<num>#Value] [<currency>(mark|rand|won|rub|ore)]');
m.group('num').tag('Money', here);
m.group('currency').tag('Currency', here); // fraction - '3 out of 5'
doc.match('#Cardinal+ out of every? #Cardinal').tag('Fraction', here); // fraction - 'a third of a slice'
m = doc.match("[(#Cardinal|a) ".concat(ordinals, "] of (a|an|the)"), 0).tag('Fraction', here); // tag 'thirds' as a ordinal
m.match('.$').tag('Ordinal', 'plural-ordinal');
};
var tagger_1 = tagger;
var tags = {
Fraction: {
isA: ['Value', 'NumericValue']
},
Multiple: {
isA: 'Value'
}
};
var ambig = {
mark: true,
sucre: true,
leone: true,
afghani: true,
rand: true,
"try": true,
mop: true,
won: true,
all: true,
rub: true,
eek: true,
sit: true,
bam: true,
npr: true,
leu: true
};
var lex = {
kronor: 'Currency'
};
currencies.forEach(function (o) {
if (o.iso && !ambig[o.iso]) {
lex[o.iso] = ['Acronym', 'Currency'];
}
var name = o.name;
if (name && !ambig[name]) {
lex[name] = 'Currency';
lex[name + 's'] = 'Currency';
}
if (o.dem) {
var dem = o.dem;
lex["".concat(dem, " ").concat(name)] = 'Currency';
lex["".concat(dem, " ").concat(name, "s")] = 'Currency';
}
});
var lexicon = lex;
/** adds .numbers() method */
var plugin = function plugin(Doc, world) {
// add money words to our lexicon
world.addWords(lexicon); // add tags to our tagset
world.addTags(tags); // additional tagging before running the number-parser
world.postProcess(tagger_1);
/** a list of number values, and their units */
var Numbers = /*#__PURE__*/function (_Doc) {
_inherits(Numbers, _Doc);
var _super = _createSuper(Numbers);
function Numbers() {
_classCallCheck(this, Numbers);
return _super.apply(this, arguments);
}
return Numbers;
}(Doc);
Object.assign(Numbers.prototype, methods_1);
/** a number and a currency */
var Money = /*#__PURE__*/function (_Numbers) {
_inherits(Money, _Numbers);
var _super2 = _createSuper(Money);
function Money() {
_classCallCheck(this, Money);
return _super2.apply(this, arguments);
}
return Money;
}(Numbers);
Object.assign(Money.prototype, methods$1);
var Fraction = /*#__PURE__*/function (_Numbers2) {
_inherits(Fraction, _Numbers2);
var _super3 = _createSuper(Fraction);
function Fraction() {
_classCallCheck(this, Fraction);
return _super3.apply(this, arguments);
}
return Fraction;
}(Numbers);
Object.assign(Fraction.prototype, methods_1$1);
var docMethods = {
/** find all numbers and values */
numbers: function numbers(n) {
var m = find(this, n);
return new Numbers(m.list, this, this.world);
},
/** return '4%' or 'four percent' etc*/
percentages: function percentages(n) {
var m = this.match('#Percent+');
m = m.concat(this.match('[#Cardinal] percent', 0));
if (typeof n === 'number') {
m = m.eq(n);
}
return new Numbers(m.list, this, this.world);
},
/** return '3 out of 5' or '3/5' etc**/
fractions: function fractions(n) {
var m = this.match('#Fraction+');
if (typeof n === 'number') {
m = m.eq(n);
}
return new Fraction(m.list, this, this.world);
},
/** number + currency pair */
money: function money() {
var m = this.splitOn('(#Money|#Currency)+');
m = m["if"]('#Money')["if"]('#Value');
return new Money(m.list, this, this.world);
}
}; // aliases
docMethods.values = docMethods.numbers;
docMethods.percents = docMethods.percentages;
Object.assign(Doc.prototype, docMethods);
return Doc;
};
var src = plugin;
return src;
})));
//# sourceMappingURL=compromise-numbers.js.map
};
BundleModuleCode['nlp/compromise-sentences']=function (module,exports){
/* compromise-sentences 0.1.1 MIT */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.compromiseSentences = factory());
}(this, (function () { 'use strict';
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
var tags = {
// Phrase: {},
NounPhrase: {
// isA: 'Phrase',
notA: ['VerbPhrase', 'AdjectivePhrase'],
color: 'blue'
},
VerbPhrase: {
// isA: 'Phrase',
notA: ['AdjectivePhrase', 'NounPhrase'],
color: 'green'
},
AdjectivePhrase: {
// isA: 'Phrase',
notA: ['VerbPhrase', 'NounPhrase'],
color: 'magenta'
},
Subordinate: {
// isA: 'Phrase',
notA: [] // color: '',
}
};
var tagger = function tagger(doc) {
doc.match('#Noun').tag('NounPhrase');
doc.match('#Verb').tag('VerbPhrase'); // NounPhrase
doc.match('(this|that|those|these)').tag('NounPhrase');
doc.match('#Adjective+ #NounPhrase').tagSafe('NounPhrase');
doc.match('#NounPhrase #Adjective+').tagSafe('NounPhrase'); // numbers
doc.match('#Value #NounPhrase').tag('NounPhrase'); // (determiners)
doc.match('#Determiner #NounPhrase').tag('NounPhrase');
doc.match('#Determiner #Adverb+? #Adjective+ #NounPhrase').tag('NounPhrase');
doc.match('(many|most|all|one|some|plenty) of #NounPhrase').tag('NounPhrase');
doc.match('such a #NounPhrase').tag('NounPhrase'); // VerbPhrase
doc.match('#VerbPhrase #Adverb+').tagSafe('VerbPhrase');
doc.match('#Adverb+ #VerbPhrase').tagSafe('VerbPhrase');
doc.match('#Auxiliary+ #VerbPhrase').tagSafe('VerbPhrase');
doc.match('#VerbPhrase no').tagSafe('VerbPhrase');
doc.match('not #VerbPhrase').tagSafe('VerbPhrase'); // claiming that
doc.match('#VerbPhrase [that]', 0).unTag('NounPhrase'); // (conjunctions)
doc.match('#VerbPhrase #Conjunction #VerbPhrase').tagSafe('VerbPhrase'); // nouns
doc.match('(who|what|which)').tag('NounPhrase'); // Adjective
doc.match('#Adverb+ #Adjective').tagSafe('AdjectivePhrase');
doc.match('#Adjective').tagSafe('AdjectivePhrase'); // missing
doc.match('#Value').tagSafe('NounPhrase');
doc.match('#Date').tagSafe('NounPhrase');
doc.match('#Date at #Date').tagSafe('NounPhrase');
};
var tagger_1 = tagger;
/** add a word to the start of this sentence */
var prepend = function prepend(str) {
this.forEach(function (doc) {
// repair the titlecase
var firstTerms = doc.match('^.');
firstTerms.not('#ProperNoun').toLowerCase(); // actually add the word
firstTerms._prepend(str); // add a titlecase
firstTerms.terms(0).toTitleCase();
});
return this;
};
/** add a word to the end of this sentence */
var append_1 = function append_1(str) {
var hasEnd = /[.?!]\s*$/.test(str);
this.forEach(function (doc) {
var end = doc.match('.$');
var lastTerm = end.termList(0);
var punct = lastTerm.post;
if (hasEnd === true) {
punct = '';
} // add punctuation to the end
end._append(str + punct); // remove punctuation from the former last-term
lastTerm.post = ' ';
});
return this;
};
var append = {
prepend: prepend,
append: append_1
};
// if a clause starts with these, it's not a main clause
var subordinate = "(after|although|as|because|before|if|since|than|that|though|when|whenever|where|whereas|wherever|whether|while|why|unless|until|once)";
var relative = "(that|which|whichever|who|whoever|whom|whose|whomever)"; //try to remove secondary clauses
var mainClause = function mainClause(og) {
var m = og.clone(true);
if (m.length === 1) {
return m;
} // if there's no verb?
m = m["if"]('#Verb');
if (m.length === 1) {
return m;
} // this is a signal for subordinate-clauses
m = m.ifNo(subordinate);
m = m.ifNo('^even (if|though)');
m = m.ifNo('^so that');
m = m.ifNo('^rather than');
m = m.ifNo('^provided that');
if (m.length === 1) {
return m;
} // relative clauses
m = m.ifNo(relative);
if (m.length === 1) {
return m;
}
m = m.ifNo('(despite|during|before|through|throughout)');
if (m.length === 1) {
return m;
} // did we go too far?
if (m.length === 0) {
m = og;
} // choose the first one?
return m.eq(0);
};
var mainClause_1 = mainClause;
var parse = function parse(doc) {
var clauses = doc.clauses();
var main = mainClause_1(clauses);
var nouns = main.match('#Determiner? (#Noun|#Adjective)+')["if"]('#Noun');
var verb = main.verbs().eq(0); // match('(do|will)? not? #Verb+ not?').eq(0)
return {
subject: nouns.eq(0),
verb: verb,
object: verb.lookAhead('.*')
};
};
var parse_1 = parse;
/** overload the original json with noun information */
var json_1 = function json_1(options) {
var n = null;
if (typeof options === 'number') {
n = options;
options = null;
}
options = options || {
text: true,
normal: true,
trim: true,
terms: true
};
var res = [];
this.forEach(function (doc) {
var json = doc._json(options)[0];
var obj = parse_1(doc);
json.subject = obj.subject.json(options)[0];
json.verb = obj.verb.json(options)[0];
json.object = obj.object.json(options)[0];
res.push(json);
});
if (n !== null) {
return res[n];
}
return res;
};
var json = {
json: json_1
};
/** he walks -> he did not walk */
var toNegative = function toNegative() {
this.forEach(function (doc) {
var obj = parse_1(doc);
var vb = obj.verb.clone();
vb = vb.verbs().toNegative();
obj.verb.replaceWith(vb, false);
});
return this;
};
/** he doesn't walk -> he walks */
var toPositive = function toPositive() {
this.forEach(function (doc) {
var obj = parse_1(doc);
var vb = obj.verb.clone();
vb = vb.verbs().toPositive();
obj.verb.replaceWith(vb, false);
});
return this;
};
var negative = {
toNegative: toNegative,
toPositive: toPositive
};
//is this sentence asking a question?
var isQuestion = function isQuestion(doc) {
var endPunct = doc.post();
var clauses = doc.clauses();
if (/\?/.test(endPunct) === true) {
return true;
} // Has ellipsis at the end means it's probably not a question
// e.g., Is this just fantasy...
if (/\.\.$/.test(doc.out('text'))) {
return false;
} // Starts with question word, but has a comma, so probably not a question
// e.g., Why are we caught in a land slide, no escape from reality
if (doc.has('^#QuestionWord') && doc.has('#Comma')) {
return false;
} // Starts with a #QuestionWord
// e.g., What open your eyes look up to the skies and see
if (doc.has('^#QuestionWord')) {
return true;
} // Second word is a #QuestionWord
// e.g., I'm what a poor boy
// case ts.has('^\w+\s#QuestionWord'):
// return true;
// is it, do you - start of sentence
// e.g., Do I need no sympathy
if (doc.has('^(do|does|did|is|was|can|could|will|would|may) #Noun')) {
return true;
} // these are a little more loose..
// e.g., Must I be come easy come easy go
if (doc.has('^(have|must) you')) {
return true;
} // Clause starts with a question word
// e.g., Anyway the wind blows, what doesn't really matter to me
if (clauses.has('^#QuestionWord')) {
return true;
} //is wayne gretskzy alive
if (clauses.has('(do|does|is|was) #Noun+ #Adverb? (#Adjective|#Infinitive)$')) {
return true;
} // Probably not a question
return false;
};
var isQuestion_1 = isQuestion;
/** return sentences ending with '?' */
var isQuestion_1$1 = function isQuestion_1$1() {
return this.filter(function (d) {
return isQuestion_1(d);
});
};
/** return sentences ending with '!' */
var isExclamation = function isExclamation() {
return this.filter(function (doc) {
var term = doc.lastTerm().termList(0);
return term.hasPost('!');
});
};
/** return sentences with neither a question or an exclamation */
var isStatement = function isStatement() {
return this.filter(function (doc) {
var term = doc.lastTerm().termList(0);
return !term.hasPost('?') && !term.hasPost('!');
});
};
/** 'he is.' -> 'he is!' */
var toExclamation = function toExclamation() {
this.post('!');
return this;
};
/** 'he is.' -> 'he is?' */
var toQuestion = function toQuestion() {
this.post('?');
return this;
};
/** 'he is?' -> 'he is.' */
var toStatement = function toStatement() {
this.post('.');
return this;
};
var questions = {
isQuestion: isQuestion_1$1,
isExclamation: isExclamation,
isStatement: isStatement,
toExclamation: toExclamation,
toQuestion: toQuestion,
toStatement: toStatement
};
var useParticiple = function useParticiple(vb) {
if (vb.has('(could|should|would|may|can|must)')) {
return true;
}
return false;
};
/** he walks -> he walked */
var toPastTense = function toPastTense() {
this.forEach(function (doc) {
if (doc.has('#PastTense')) {
return;
}
var obj = parse_1(doc);
var vb = obj.verb.clone(); // support 'he could drive' -> 'he could have driven'
if (useParticiple(vb)) {
vb = vb.verbs().toParticiple();
obj.verb.replaceWith(vb, false);
} else {
// //do a normal conjugation
vb = vb.verbs().toPastTense();
obj.verb.replaceWith(vb, false);
} // // trailing gerund/future/present are okay, but 'walked and eats' is not
if (obj.object && obj.object.found && obj.object.has('#PresentTense')) {
var verbs = obj.object.verbs();
verbs["if"]('#PresentTense').verbs().toPastTense();
}
});
return this;
};
/** he drives -> he has driven */
var toParticiple = function toParticiple() {
this.forEach(function (doc) {
if (doc.has('has #Participle')) {
return;
}
var obj = parse_1(doc);
var vb = obj.verb.clone();
vb = vb.verbs().toParticiple();
obj.verb.replaceWith(vb, false); // trailing gerund/future/present are okay, but 'walked and eats' is not
if (obj.object && obj.object.found && obj.object.has('#PresentTense')) {
var verbs = obj.object.verbs();
verbs["if"]('#PresentTense').verbs().toParticiple();
}
});
return this;
};
/** he walked -> he walks */
var toPresentTense = function toPresentTense() {
this.forEach(function (doc) {
var obj = parse_1(doc);
var isPlural = obj.verb.lookBehind('(i|we) (#Adverb|#Verb)?$').found;
var vb = obj.verb.clone(); // 'i look', not 'i looks'
if (isPlural) {
//quick hack for copula verb - be/am
if (vb.has('(is|was|am|be)')) {
vb = vb.replace('will? (is|was|am|be)', 'am');
} else {
vb = vb.verbs().toInfinitive();
}
} else {
//'he looks'
vb = vb.verbs().toPresentTense();
}
obj.verb.replaceWith(vb, false); // future is okay, but 'walks and ate' -> 'walks and eats'
if (obj.object && obj.object.found && obj.object.has('#PastTense')) {
var verbs = obj.object.verbs();
verbs["if"]('#PastTense').verbs().toPresentTense();
}
});
return this;
};
/**he walked -> he will walk */
var toFutureTense = function toFutureTense() {
this.forEach(function (doc) {
var obj = parse_1(doc);
var vb = obj.verb.clone();
vb = vb.verbs().toFutureTense();
obj.verb.replaceWith(vb, false); //Present is okay, but 'will walk and ate' -> 'will walk and eat'
if (obj.object && obj.object.found && obj.object.has('(#PastTense|#PresentTense)')) {
var verbs = obj.object.verbs();
verbs["if"]('(#PastTense|#PresentTense)').verbs().toInfinitive();
}
});
return this;
};
/** the main noun of the sentence */
var subjects = function subjects() {
return this.map(function (doc) {
var res = parse_1(doc);
return res.subject;
});
};
/** return sentences that are in passive-voice */
var isPassive = function isPassive() {
return this["if"]('was #Adverb? #PastTense #Adverb? by'); //haha
};
var tense = {
toPastTense: toPastTense,
toParticiple: toParticiple,
toPresentTense: toPresentTense,
toFutureTense: toFutureTense,
subjects: subjects,
isPassive: isPassive
};
var phrases_1 = function phrases_1() {
var arr = [];
this.forEach(function (s) {
s = s.splitOn('#VerbPhrase+');
s = s.splitOn('#NounPhrase+');
s = s.splitOn('#AdjectivePhrase+');
arr = arr.concat(s.list);
});
return this.buildFrom(arr);
};
var phrases = {
phrases: phrases_1
};
var methods = Object.assign({}, append, json, negative, questions, tense, phrases);
var plugin = function plugin(Doc, world) {
// our new tags
world.addTags(tags); // run our tagger
world.postProcess(tagger_1);
/** */
var Sentences = /*#__PURE__*/function (_Doc) {
_inherits(Sentences, _Doc);
var _super = _createSuper(Sentences);
function Sentences(list, from, w) {
_classCallCheck(this, Sentences);
list = list.map(function (p) {
return p.clone(true);
});
return _super.call(this, list, from, w);
}
return Sentences;
}(Doc); // add some aliases
methods.questions = methods.isQuestion;
methods.exclamations = methods.isExclamation;
methods.statements = methods.isStatement; // keep backups of these methods
methods._prepend = Sentences.prototype.prepend;
methods._append = Sentences.prototype.append;
methods._json = Sentences.prototype.json;
Object.assign(Sentences.prototype, methods);
/** create a new Sentences object */
Sentences.prototype.buildFrom = function (list) {
list = list.map(function (p) {
return p.clone(true);
});
var doc = new Sentences(list, this, this.world);
return doc;
};
/** create a new Doc object */
Sentences.prototype.toDoc = function () {
return Doc.prototype.buildFrom(this.list);
};
/** overload original sentences() method and return Sentence class**/
Doc.prototype.sentences = function (n) {
var arr = [];
this.list.forEach(function (p) {
arr.push(p.fullSentence());
});
var s = new Sentences(arr, this, this.world);
if (typeof n === 'number') {
s = s.get(n);
}
return s;
};
return Doc;
};
var src = plugin;
return src;
})));
//# sourceMappingURL=compromise-sentences.js.map
};
BundleModuleCode['nlp/efrt']=function (module,exports){
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.efrt = factory());
}(this, function () { 'use strict';
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var commonPrefix = function(w1, w2) {
var len = Math.min(w1.length, w2.length);
while (len > 0) {
var prefix = w1.slice(0, len);
if (prefix === w2.slice(0, len)) {
return prefix
}
len -= 1;
}
return ''
};
/* Sort elements and remove duplicates from array (modified in place) */
var unique = function(a) {
a.sort();
for (var i = 1; i < a.length; i++) {
if (a[i - 1] === a[i]) {
a.splice(i, 1);
}
}
};
var fns = {
commonPrefix: commonPrefix,
unique: unique
};
var Histogram = function() {
this.counts = {};
};
var methods = {
init: function(sym) {
if (this.counts[sym] === undefined) {
this.counts[sym] = 0;
}
},
add: function(sym, n) {
if (n === undefined) {
n = 1;
}
this.init(sym);
this.counts[sym] += n;
},
countOf: function(sym) {
this.init(sym);
return this.counts[sym]
},
highest: function(top) {
var sorted = [];
var keys = Object.keys(this.counts);
for (var i = 0; i < keys.length; i++) {
var sym = keys[i];
sorted.push([sym, this.counts[sym]]);
}
sorted.sort(function(a, b) {
return b[1] - a[1]
});
if (top) {
sorted = sorted.slice(0, top);
}
return sorted
}
};
Object.keys(methods).forEach(function(k) {
Histogram.prototype[k] = methods[k];
});
var histogram = Histogram;
var BASE = 36;
var seq = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var cache = seq.split('').reduce(function(h, c, i) {
h[c] = i;
return h
}, {});
// 0, 1, 2, ..., A, B, C, ..., 00, 01, ... AA, AB, AC, ..., AAA, AAB, ...
var toAlphaCode = function(n) {
if (seq[n] !== undefined) {
return seq[n]
}
var places = 1;
var range = BASE;
var s = '';
for (; n >= range; n -= range, places++, range *= BASE) {}
while (places--) {
var d = n % BASE;
s = String.fromCharCode((d < 10 ? 48 : 55) + d) + s;
n = (n - d) / BASE;
}
return s
};
var fromAlphaCode = function(s) {
if (cache[s] !== undefined) {
return cache[s]
}
var n = 0;
var places = 1;
var range = BASE;
var pow = 1;
for (; places < s.length; n += range, places++, range *= BASE) {}
for (var i = s.length - 1; i >= 0; i--, pow *= BASE) {
var d = s.charCodeAt(i) - 48;
if (d > 10) {
d -= 7;
}
n += d * pow;
}
return n
};
var encoding = {
toAlphaCode: toAlphaCode,
fromAlphaCode: fromAlphaCode
};
var config = {
SYM_SEP: '|',
NODE_SEP: ';',
KEY_VAL: ':',
STRING_SEP: ',',
TERMINAL_PREFIX: '!',
BASE: 36
};
// Return packed representation of Trie as a string.
// Return packed representation of Trie as a string.
//
// Each node of the Trie is output on a single line.
//
// For example Trie("the them there thesis this"):
// {
// "th": {
// "is": 1,
// "e": {
// "": 1,
// "m": 1,
// "re": 1,
// "sis": 1
// }
// }
// }
//
// Would be reperesented as:
//
// th0
// e0is
// !m,re,sis
//
// The line begins with a '!' iff it is a terminal node of the Trie.
// For each string property in a node, the string is listed, along
// with a (relative!) line number of the node that string references.
// Terminal strings (those without child node references) are
// separated by ',' characters.
var nodeLine = function(self, node) {
var line = '',
sep = '';
if (self.isTerminal(node)) {
line += config.TERMINAL_PREFIX;
}
var props = self.nodeProps(node);
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (typeof node[prop] === 'number') {
line += sep + prop;
sep = config.STRING_SEP;
continue
}
if (self.syms[node[prop]._n]) {
line += sep + prop + self.syms[node[prop]._n];
sep = '';
continue
}
var ref = encoding.toAlphaCode(node._n - node[prop]._n - 1 + self.symCount);
// Large reference to smaller string suffix -> duplicate suffix
if (node[prop]._g && ref.length >= node[prop]._g.length && node[node[prop]._g] === 1) {
ref = node[prop]._g;
line += sep + prop + ref;
sep = config.STRING_SEP;
continue
}
line += sep + prop + ref;
sep = '';
}
return line
};
var analyzeRefs = function(self, node) {
if (self.visited(node)) {
return
}
var props = self.nodeProps(node, true);
for (var i = 0; i < props.length; i++) {
var prop = props[i];
var ref = node._n - node[prop]._n - 1;
// Count the number of single-character relative refs
if (ref < config.BASE) {
self.histRel.add(ref);
}
// Count the number of characters saved by converting an absolute
// reference to a one-character symbol.
self.histAbs.add(node[prop]._n, encoding.toAlphaCode(ref).length - 1);
analyzeRefs(self, node[prop]);
}
};
var symbolCount = function(self) {
self.histAbs = self.histAbs.highest(config.BASE);
var savings = [];
savings[-1] = 0;
var best = 0,
sCount = 0;
var defSize = 3 + encoding.toAlphaCode(self.nodeCount).length;
for (var sym = 0; sym < config.BASE; sym++) {
if (self.histAbs[sym] === undefined) {
break
}
savings[sym] =
self.histAbs[sym][1] -
defSize -
self.histRel.countOf(config.BASE - sym - 1) +
savings[sym - 1];
if (savings[sym] >= best) {
best = savings[sym];
sCount = sym + 1;
}
}
return sCount
};
var numberNodes = function(self, node) {
// Topological sort into nodes array
if (node._n !== undefined) {
return
}
var props = self.nodeProps(node, true);
for (var i = 0; i < props.length; i++) {
numberNodes(self, node[props[i]]); //recursive
}
node._n = self.pos++;
self.nodes.unshift(node);
};
var pack = function(self) {
self.nodes = [];
self.nodeCount = 0;
self.syms = {};
self.symCount = 0;
self.pos = 0;
// Make sure we've combined all the common suffixes
self.optimize();
self.histAbs = new histogram();
self.histRel = new histogram();
numberNodes(self, self.root);
self.nodeCount = self.nodes.length;
self.prepDFS();
analyzeRefs(self, self.root);
self.symCount = symbolCount(self);
for (var sym = 0; sym < self.symCount; sym++) {
self.syms[self.histAbs[sym][0]] = encoding.toAlphaCode(sym);
}
for (var i = 0; i < self.nodeCount; i++) {
self.nodes[i] = nodeLine(self, self.nodes[i]);
}
// Prepend symbols
for (var sym = self.symCount - 1; sym >= 0; sym--) {
self.nodes.unshift(
encoding.toAlphaCode(sym) +
config.KEY_VAL +
encoding.toAlphaCode(self.nodeCount - self.histAbs[sym][0] - 1)
);
}
return self.nodes.join(config.NODE_SEP)
};
var pack_1 = pack;
var NOT_ALLOWED = new RegExp('[0-9A-Z,;!:|¦]'); //characters banned from entering the trie
var methods$1 = {
// Insert words from one big string, or from an array.
insertWords: function(words) {
if (words === undefined) {
return
}
if (typeof words === 'string') {
words = words.split(/[^a-zA-Z]+/);
}
for (var i = 0; i < words.length; i++) {
words[i] = words[i].toLowerCase();
}
fns.unique(words);
for (var i = 0; i < words.length; i++) {
if (words[i].match(NOT_ALLOWED) === null) {
this.insert(words[i]);
}
}
},
insert: function(word) {
this._insert(word, this.root);
var lastWord = this.lastWord;
this.lastWord = word;
var prefix = fns.commonPrefix(word, lastWord);
if (prefix === lastWord) {
return
}
var freeze = this.uniqueNode(lastWord, word, this.root);
if (freeze) {
this.combineSuffixNode(freeze);
}
},
_insert: function(word, node) {
var prefix, next;
// Duplicate word entry - ignore
if (word.length === 0) {
return
}
// Do any existing props share a common prefix?
var keys = Object.keys(node);
for (var i = 0; i < keys.length; i++) {
var prop = keys[i];
prefix = fns.commonPrefix(word, prop);
if (prefix.length === 0) {
continue
}
// Prop is a proper prefix - recurse to child node
if (prop === prefix && typeof node[prop] === 'object') {
this._insert(word.slice(prefix.length), node[prop]);
return
}
// Duplicate terminal string - ignore
if (prop === word && typeof node[prop] === 'number') {
return
}
next = {};
next[prop.slice(prefix.length)] = node[prop];
this.addTerminal(next, word = word.slice(prefix.length));
delete node[prop];
node[prefix] = next;
this.wordCount++;
return
}
// No shared prefix. Enter the word here as a terminal string.
this.addTerminal(node, word);
this.wordCount++;
},
// Add a terminal string to node.
// If 2 characters or less, just add with value == 1.
// If more than 2 characters, point to shared node
// Note - don't prematurely share suffixes - these
// terminals may become split and joined with other
// nodes in this part of the tree.
addTerminal: function(node, prop) {
if (prop.length <= 1) {
node[prop] = 1;
return
}
var next = {};
node[prop[0]] = next;
this.addTerminal(next, prop.slice(1));
},
// Well ordered list of properties in a node (string or object properties)
// Use nodesOnly==true to return only properties of child nodes (not
// terminal strings.
nodeProps: function(node, nodesOnly) {
var props = [];
for (var prop in node) {
if (prop !== '' && prop[0] !== '_') {
if (!nodesOnly || typeof node[prop] === 'object') {
props.push(prop);
}
}
}
props.sort();
return props
},
optimize: function() {
this.combineSuffixNode(this.root);
this.prepDFS();
this.countDegree(this.root);
this.prepDFS();
this.collapseChains(this.root);
},
// Convert Trie to a DAWG by sharing identical nodes
combineSuffixNode: function(node) {
// Frozen node - can't change.
if (node._c) {
return node
}
// Make sure all children are combined and generate unique node
// signature for this node.
var sig = [];
if (this.isTerminal(node)) {
sig.push('!');
}
var props = this.nodeProps(node);
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (typeof node[prop] === 'object') {
node[prop] = this.combineSuffixNode(node[prop]);
sig.push(prop);
sig.push(node[prop]._c);
} else {
sig.push(prop);
}
}
sig = sig.join('-');
var shared = this.suffixes[sig];
if (shared) {
return shared
}
this.suffixes[sig] = node;
node._c = this.cNext++;
return node
},
prepDFS: function() {
this.vCur++;
},
visited: function(node) {
if (node._v === this.vCur) {
return true
}
node._v = this.vCur;
return false
},
countDegree: function(node) {
if (node._d === undefined) {
node._d = 0;
}
node._d++;
if (this.visited(node)) {
return
}
var props = this.nodeProps(node, true);
for (var i = 0; i < props.length; i++) {
this.countDegree(node[props[i]]);
}
},
// Remove intermediate singleton nodes by hoisting into their parent
collapseChains: function(node) {
var prop, props, child, i;
if (this.visited(node)) {
return
}
props = this.nodeProps(node);
for (i = 0; i < props.length; i++) {
prop = props[i];
child = node[prop];
if (typeof child !== 'object') {
continue
}
this.collapseChains(child);
// Hoist the singleton child's single property to the parent
if (child._g !== undefined && (child._d === 1 || child._g.length === 1)) {
delete node[prop];
prop += child._g;
node[prop] = child[child._g];
}
}
// Identify singleton nodes
if (props.length === 1 && !this.isTerminal(node)) {
node._g = prop;
}
},
isTerminal: function(node) {
return !!node['']
},
// Find highest node in Trie that is on the path to word
// and that is NOT on the path to other.
uniqueNode: function(word, other, node) {
var props = this.nodeProps(node, true);
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (prop === word.slice(0, prop.length)) {
if (prop !== other.slice(0, prop.length)) {
return node[prop]
}
return this.uniqueNode(word.slice(prop.length), other.slice(prop.length), node[prop])
}
}
return undefined
},
pack: function() {
return pack_1(this)
}
};
/*
A JavaScript implementation of a Trie search datastructure.
Each node of the Trie is an Object that can contain the following properties:
'' - If present (with value == 1), the node is a Terminal Node - the prefix
leading to this node is a word in the dictionary.
numeric properties (value == 1) - the property name is a terminal string
so that the prefix + string is a word in the dictionary.
Object properties - the property name is one or more characters to be consumed
from the prefix of the test string, with the remainder to be checked in
the child node.
'_c': A unique name for the node (starting from 1), used in combining Suffixes.
'_n': Created when packing the Trie, the sequential node number
(in pre-order traversal).
'_d': The number of times a node is shared (it's in-degree from other nodes).
'_v': Visited in DFS.
'_g': For singleton nodes, the name of it's single property.
*/
var Trie = function(words) {
this.root = {};
this.lastWord = '';
this.suffixes = {};
this.suffixCounts = {};
this.cNext = 1;
this.wordCount = 0;
this.insertWords(words);
this.vCur = 0;
};
Object.keys(methods$1).forEach(function(k) {
Trie.prototype[k] = methods$1[k];
});
var trie = Trie;
var isArray = function(input) {
return Object.prototype.toString.call(input) === '[object Array]'
};
var handleFormats = function(input) {
//null
if (input === null || input === undefined) {
return {}
}
//string
if (typeof input === 'string') {
return input.split(/ +/g).reduce(function(h, str) {
h[str] = true;
return h
}, {})
}
//array
if (isArray(input)) {
return input.reduce(function(h, str) {
h[str] = true;
return h
}, {})
}
//object
return input
};
//turn an array into a compressed string
var pack$1 = function(obj) {
obj = handleFormats(obj);
//pivot into categories:
var flat = Object.keys(obj).reduce(function(h, k) {
var val = obj[k];
//array version-
//put it in several buckets
if (isArray(val)) {
for (var i = 0; i < val.length; i++) {
h[val[i]] = h[val[i]] || [];
h[val[i]].push(k);
}
return h
}
//normal string/boolean version
if (h.hasOwnProperty(val) === false) {
//basically h[val]=[] - support reserved words
Object.defineProperty(h, val, {
writable: true,
enumerable: true,
configurable: true,
value: []
});
}
h[val].push(k);
return h
}, {});
//pack each into a compressed string
Object.keys(flat).forEach(function(k) {
var t = new trie(flat[k]);
flat[k] = t.pack();
});
// flat = JSON.stringify(flat, null, 0);
return Object.keys(flat)
.map(function (k) {
return k + ':' + flat[k]
})
.join('|')
// return flat;
};
var pack_1$1 = pack$1;
//the symbols are at the top of the array.
var symbols = function(t) {
//... process these lines
var reSymbol = new RegExp('([0-9A-Z]+):([0-9A-Z]+)');
for (var i = 0; i < t.nodes.length; i++) {
var m = reSymbol.exec(t.nodes[i]);
if (!m) {
t.symCount = i;
break
}
t.syms[encoding.fromAlphaCode(m[1])] = encoding.fromAlphaCode(m[2]);
}
//remove from main node list
t.nodes = t.nodes.slice(t.symCount, t.nodes.length);
};
// References are either absolute (symbol) or relative (1 - based)
var indexFromRef = function(trie, ref, index) {
var dnode = encoding.fromAlphaCode(ref);
if (dnode < trie.symCount) {
return trie.syms[dnode]
}
return index + dnode + 1 - trie.symCount
};
var toArray = function(trie) {
var all = [];
var crawl = function (index, pref) {
var node = trie.nodes[index];
if (node[0] === '!') {
all.push(pref);
node = node.slice(1); //ok, we tried. remove it.
}
var matches = node.split(/([A-Z0-9,]+)/g);
for (var i = 0; i < matches.length; i += 2) {
var str = matches[i];
var ref = matches[i + 1];
if (!str) {
continue
}
var have = pref + str;
//branch's end
if (ref === ',' || ref === undefined) {
all.push(have);
continue
}
var newIndex = indexFromRef(trie, ref, index);
crawl(newIndex, have);
}
};
crawl(0, '');
return all
};
//PackedTrie - Trie traversal of the Trie packed-string representation.
var unpack = function(str) {
var trie = {
nodes: str.split(';'), //that's all ;)!
syms: [],
symCount: 0
};
//process symbols, if they have them
if (str.match(':')) {
symbols(trie);
}
return toArray(trie)
};
var unpack_1 = unpack;
var unpack_1$1 = function(str) {
//turn the weird string into a key-value object again
var obj = str.split('|').reduce(function (h, s) {
var arr = s.split(':');
h[arr[0]] = arr[1];
return h
}, {});
var all = {};
Object.keys(obj).forEach(function(cat) {
var arr = unpack_1(obj[cat]);
//special case, for botched-boolean
if (cat === 'true') {
cat = true;
}
for (var i = 0; i < arr.length; i++) {
var k = arr[i];
if (all.hasOwnProperty(k) === true) {
if (Array.isArray(all[k]) === false) {
all[k] = [all[k], cat];
} else {
all[k].push(cat);
}
} else {
all[k] = cat;
}
}
});
return all
};
// Create a fast symbol lexer from packed string (about 2-5 times slower than unpacked hash table)
var lexer = function (packed) {
var lex={};
var symbols = packed.split(config.SYM_SEP);
function lexit (treestr) {
var levels = treestr.split(';');
// print(levels);
return function (text) {
var scannerOff=0,level=0,jump,shift,startOff=0;
for(var textOff=0;;) {
var current = levels[level];
// print(level,textOff,scannerOff,text[textOff],current[scannerOff],/[0-9A-Z]/.test(current[scannerOff]));
if (current[scannerOff]==undefined) return true; // terminal; all chars consumed
if (current[scannerOff]==',' && text[textOff]==undefined) return true; // terminal; all chars consumed
if (current[scannerOff]==',') scannerOff++;
if (/[0-9A-Z]/.test(current[scannerOff])) {
jump = 0;
// BASE36 encoding !!!
var code='';
while(/[0-9A-Z]/.test(current[scannerOff])) {
code += current[scannerOff++];
}
level += (fromAlphaCode(code)+1); // delta
scannerOff=0;
startOff=textOff;
jump=undefined;
continue;
}
if (current[scannerOff]=='!' && text[textOff]==undefined) return true;
else if (current[scannerOff]=='!') scannerOff++;
if (current[scannerOff]==text[textOff]) {
textOff++;scannerOff++;
} else {
// skip to next pattern on current level (starts after comma or jump number)
while (current[scannerOff]!=undefined && !(/[0-9A-Z]/.test(current[scannerOff])) && current[scannerOff]!=',')
scannerOff++;
if (current[scannerOff]==',') scannerOff++;
else while (current[scannerOff]!=undefined && (/[0-9A-Z]/.test(current[scannerOff]))) scannerOff++;
if (current[scannerOff]==undefined) return false; // no matching; end of pattern list on this level
textOff=startOff;
}
}
return text[textOff]==undefined &&
(current[scannerOff]==undefined||current[scannerOff]==','||current[scannerOff]=='!');
}
}
symbols.forEach(function (line) {
var tokens=line.split(':');
lex[tokens[0]]=lexit(tokens[1]);
});
return lex;
};
var src = createCommonjsModule(function (module) {
var efrt = {
lexer : lexer,
pack: pack_1$1,
unpack: unpack_1$1
};
//and then all-the-exports...
if (typeof self !== 'undefined') {
self.efrt = efrt; // Web Worker
} else if (typeof window !== 'undefined') {
window.efrt = efrt; // Browser
} else if (typeof commonjsGlobal !== 'undefined') {
commonjsGlobal.efrt = efrt; // NodeJS
}
//then for some reason, do this too!
{
module.exports = efrt;
}
});
return src;
}));
};
BundleModuleCode['ml/ml']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2022 BSSLAB
** $CREATED: 8-2-16 by sbosse.
** $VERSION: 1.17.1
**
** $INFO:
**
** JavaScript AIOS Machine Learning API
**
** type algorithm = {'dti','dt','id3','c45','kmeans','knn','knn2','mlp','slp','rl','svm','txt','cnn'}
**
**
** id3: Symbolic Decision Tree algorithm
** -------------------------------------
**
** typeof @options = {
** algorithm='id3',
** data:{x1:number,x2:number,..,y:*} []
** target:string is e.g. 'y'
** features: string [] is e.g. ['x1','x2',..]
** }
**
** ice: decision tree algorithm supporting numbers with eps intervals (hybrid C45/ID3)
** -------------------------------------
**
** General feature variable set:
**
** typeof @options = {
** algorithm='dt',
** data:{x1:number,x2:number,..,y:*} [],
** target:string is e.g. 'y',
** features: string [] is e.g. ['x1','x2',..],
** eps:number is e.g. '5',
** }
**
** dti: interval decision tree algorithm
** -------------------------------------
**
** General feature variable set:
**
** typeof @options = {
** algorithm='dti',
** data:{x1:number,x2:number,..,y:*} []
** target:string is e.g. 'y'
** features: string [] is e.g. ['x1','x2',..]
** eps:number is e.g. '5',
** maxdepth:number,
** }
**
** Or vector feature variables (i.e., features=[0,1,2,...n-1], target=n):
**
** typeof @options = {
** algorithm='dti',
** x:* [] [],
** y:* [],
** eps:number is e.g. '5',
** maxdepth:number,
** }
**
** knn: k-Nearest-Neighbour Algorithm
** ----------------------------------
**
** typeof @options = {
** algorithm='knn',
** x: number [][],
** y: * []
** }
**
** mlp: multi layer perceptron Algorithm
** ----------------------------------
**
** typeof @options = {
** algorithm='mlp',
** x: number [][],
** y: number [] [] | * [],
** hidden_layers?:number [],
** lr?:number,
** epochs?:number,
** labels?:string [],
** features?: string [],
** normalize?,
** verbose?:number
** }
**
**
** cnn: Convolutional Neural Network for numerial (2D) data
** -------------------------------------
**
** General feature variable set:
**
** typeof @options = {
** algorithm='cnn',
** data:{x:[]|[][],y:'a} []
** layers: layer [],
** trainer:trainer,
** }
** type layer =
** {type:'input', out_sx:number, out_sy:number, out_depth:number} | // Input Layer
** {type:'conv', sx:number, filters:number, stride:number, pad:number, activation:string} | // Convolution Layer
** {type:'pool', sx:number, stride:number} | // Pooling Layer
** {type:'softmax', num_classes:number} | // Classifier Layers
** {type:'svm', num_classes:number| // Classifier Layers
** {type:'fc', num_neurons:number, activation:string} // Fully Connected Layer
**
** typeof activation = 'relu'| 'maxout' | 'sigmoid' | 'tanh' ..
**
** type trainer =
** {method: 'sgd', learning_rate:number, momentum: number, batch_size:number, l2_decay:number} |
** {method: 'adadelta', learning_rate:number, eps: number, ro:number, batch_size:number, l2_decay:number} |
** {method: 'adam', learning_rate:number, eps: number, beta1: number, beta2: number, batch_size: number, l2_decay:number} |
** ..
**
** text: text analysis (similarity checking)
** -----------------------------------------
** classify(model,string) -> {match:number [0..1],string:string }
** learn({algorithm:ML.TXT, data:string []]) -> model
** test({algorithm:ML.TXT,string:string}|model,string) -> number [0..1]
** similarity(string,string) -> number [0..1]
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var ICE = Require('ml/ice'); // ICE ID3/C45 eps
var DTI = Require('ml/dti');
var KNN = Require('ml/knn');
var KMN = Require('ml/kmeans');
var SVM = Require('ml/svm');
var MLP = Require('ml/mlp');
var ID3 = Require('ml/id3');
var C45 = Require('ml/C45');
var TXT = Require('ml/text');
var RF = Require('ml/rf');
var RL = Require('ml/rl');
var STAT= Require('ml/stats');
var CNN = Require('ml/cnn');
var ANN = Require('ml/ann');
var PCA = Require('ml/pca');
var current=none;
var Aios=none;
var options = {
version: '1.17.1'
}
// Some definitions
var ML = {
// Algorithms
ANN:'ann', // neataptic NN
C45:'c45',
CNN:'cnn',
ICE:'ice', // ICE ID3/C45 eps
DTI:'dti',
ID3:'id3',
KMN:'kmeans',
KNN:'knn',
KNN2:'knn2',
MLP:'mlp',
RF:'rf', // Random Forest
RL:'rl', // Reinforcement Leerner
SLP:'slp', // Synonym for MLP (but single layer)
SVM:'svm',
TXT:'txt',
// Some Functions
EUCL:'euclidean',
PEAR:'pearson',
// RL agents
DPAgent:'DPAgent',
TDAgent:'TDAgent',
DQNAgent:'DQNAgent',
};
/**
* Computes Log with base-2
* @private
*/
function log2(n) {
return Math.log(n) / Math.log(2);
}
function obj2Array(row,features) {
return features.map(function (attr) { return row[attr] });
}
function objSlice(row,features) {
var o = {};
features.forEach(function (attr) { o[attr]=row[attr] });
return o;
}
// transform [v][] -> v[]
function relax(mat) {
if (Comp.obj.isMatrix(mat) && mat[0].length==1) return mat.map(function (row) { return row[0]})
else return mat;
}
// transform v[] -> [v][]
function wrap(mat) {
if (!Comp.obj.isMatrix(mat)) return mat.map(function (v) { return [v]})
else return mat
}
/* Common data transformation between different formats
**
** 1a. need='xy': data={$x:'a,$y:'b}[] -> {x:{$x} [], y:'b[]}
** 1b. need='xy': data=('a|'b)[][] -> {x:'a [][], y:'b[]}
** 1c. need='xry': data=('a|'b)[][] -> {x:{$x} [], y:'b[]}
** 1c. need='io': data=number[][] -> {input:number, output:number} []
** 1d. need='io': data={$x:number,$y:number}[] -> {input:number, output:number} []
** 2. need='xmy': data={$x:'a,$y:'b}[] -> {x:'a [][], y:'b[]}
** 3. need='d': data={x:'a[][],y:'b[]}} -> {data:{$x:'a,$y:'b}[][]}
** 4. need='dm': data={x:'a[][],y;'b[]} -> {data:('a|'b)[][]}
** 5. need='m': data={$x:'a}[] -> 'a [][]
** 6. need='a': data={$x:'a} -> 'a []
** typeof options = {
** scale: {k:number, off:number, shift:number} is transformation of input data,
** xscale: {k:number, off:number, shift:number} is transformation of input data,
** yscale: {k:number, off:number, shift:number} is transformation of output data,
** features : string [] is feature variable list,
** target: string is output variable,
**
**/
function scale(vrow,scala) {
if (!scala) return vrow;
if (typeof vrow == 'number') {
if (typeof scala.k == 'number')
return scala.shift+(vrow-scala.off)*scala.k
else
return scala.shift+(vrow-scala.off[0])*scala.k[0];
}
if (typeof scala.k == 'number')
return vrow.map(function (col,i) {
return scala.shift+(col-scala.off)*scala.k })
else
return vrow.map(function (col,i) {
return scala.shift+(col-scala.off[i])*scala.k[i] })
}
function unscale(vrow,scala) {
if (!scala) return vrow;
if (typeof vrow == 'number') {
if (typeof scala.k == 'number')
return (vrow-scala.shift)/scala.k+scala.off
else
return (vrow-scala.shift)/scala.k[0]+scala.off[0]
}
}
function preprocess(data,need,options) {
var row,x,y,_data;
options=options||{};
var scala=options.scale || options.xscale;
function array(data) {
return Comp.obj.isArray(data)?data:[data]
}
if (Comp.obj.isArray(data)) {
row=data[0];
switch (need) {
case 'xy':
case 'xry':
if (options.target!=undefined && options.features!=undefined) {
if (Comp.obj.isArray(row) && need=='xy') {
if (Number(options.target)==row.length-1) {
x=data.map(function (row) { return scale(row.slice(0,options.target),scala) });
y=data.map(function (row) { return row[options.target] })
}
} else if (Comp.obj.isObj(row)) {
if (typeof options.target == 'string') {
x=data.map(function (row) { return scale(objSlice(row,options.features),scala) });
y=data.map(function (row) { return row[options.target] });
}
}
}
if (x && y) return {x:x,y:y}
break;
case 'a':
if (Comp.obj.isArray(data) && typeof data[0] != 'object') return {data:data};
if (Comp.obj.isObject(data) && options.features!=undefined) {
return { data:data.map(function (row) {
return scale(objSlice(row,options.features),scala) })};
}
break;
case 'm':
if (Comp.obj.isMatrix(data)) return {data:data};
if (Comp.obj.isObject(row) && options.features!=undefined) {
return { data:data.map(function (row) {
return scale(obj2Array(row,options.features),scala) })};
}
break;
case 'xmy':
if (Comp.obj.isObject(row) && options.features!=undefined && options.target!=undefined) {
return { x:data.map(function (row) {
return scale(obj2Array(row,options.features),scala) }),
y:data.map(function (row) { return row[options.target]})};
}
break;
case 'io':
if (Comp.obj.isArray(row) && options.target!=undefined) {
// number [][]
if (Number(options.target)==row.length-1) {
_data=data.map(function (row) { return { input:scale(row.slice(0,options.target),scala),
output:array(row[options.target]) }});
return _data
}
} else if (Comp.obj.isObject(row) && options.target!=undefined && options.features!=undefined) {
_data=data.map(function (row) { return { input:scale(obj2Array(row,options.features),scala),
output:array(row[options.target]) }});
return _data
}
break;
}
} else if (data.x && data.y) {
if (Comp.obj.isArray(data.x) && Comp.obj.isArray(data.y)) {
row=data.x[0];
switch (need) {
case 'io':
if (Comp.obj.isArray(row)) {
// number [][]
_data=data.x.map(function (row, rowi) { return { input:scale(row,scala),
output:array(data.y[rowi]) }});
return _data
}
if (Comp.obj.isObject(row) && options.features!=undefined) {
_data=data.x.map(function (row, rowi) { return { input:scale(obj2Array(row,options.features),scala),
output:array(data.y[rowi]) }});
return _data
}
break;
case 'xm':
if (Comp.obj.isArray(row)) return data.x;
break;
case 'xmy':
if (Comp.obj.isArray(row)) return { x:data.x, y:data.y};
break;
case 'xmya':
if (Comp.obj.isArray(row)) return { x:data.x, y:data.y.map(array)};
break;
case 'd':
return data.x.map(function (row,rowi) {
var newrow={};
if (options.features && options.target) {
options.features.forEach(function (f,coli) {
newrow[f]=row[coli];
});
newrow[options.target]=data.y[rowi];
} else {
row.forEach(function (col,f) {
newrow[String(f)]=col;
});
newrow[String(row.length)]=data.y[rowi];
}
return newrow;
})
break;
}
}
}
}
// Agent AIOS API
var ml = {
// only RL
action : function (model,arg) {
switch (model.algorithm) {
// Selects and returns next action from set of actions
case ML.RL:
switch (model.kind) {
case ML.DQNAgent:
// arg == state array
return model.actions[RL.DQNAgent.code.act(model,arg)];
break;
case ML.DPAgent:
// arg == state (integer number)
return model.actions[RL.DPAgent.code.act(model,arg)];
break;
case ML.TDAgent:
// arg == state (integer number)
return model.actions[RL.TDAgent.code.act(model,arg)];
break;
}
break;
}
},
/** Classification (prediction): Apply sample data to learned model.
* Returns prediction result.
*
*/
classify: function (model,samples) {
var x,solutions,result;
switch (model.algorithm) {
case ML.ANN:
if (Comp.obj.isArray(samples))
return samples.map(function (sample) {
return model.network.activate(sample)
});
else
return model.network.activate(samples);
case ML.CNN:
if (Comp.obj.isMatrix(samples))
return samples.map(function (sample) {
return CNN.predict(model,sample);
});
else
return CNN.predict(model,samples);
break;
case ML.C45:
// Sample row format: [x1,x2,..,xn]
if (Comp.obj.isMatrix(samples)) {
return samples.map(function (sample) {
return C45.classify(model,sample);
});
} else if (Comp.obj.isArray(samples) && !Comp.obj.isObj(samples[0])) {
return C45.classify(model,samples);
} else if (Comp.obj.isArray(samples) && Comp.obj.isObj(samples[0])) {
return samples.map(function (sample) {
return C45.classify(model,sample);
});
} else if (Comp.obj.isObj(samples)) {
return C45.classify(model,samples);
}
break;
case ML.DT:
case ML.ICE:
if (Comp.obj.isMatrix(samples) ||
Comp.obj.isArray(samples) && Comp.obj.isObj(samples[0]))
return samples.map(function (sample) {
return ICE.predict(model,sample)
});
else
return ICE.predict(model,samples);
case ML.DTI:
if (Comp.obj.isMatrix(samples))
return samples.map(function (sample) {
return DTI.predict(model,sample)
});
else
return DTI.predict(model,samples);
case ML.ID3:
if (Comp.obj.isArray(samples))
return samples.map(function (sample) {
return ID3.predict(model,sample)
});
else
return ID3.predict(model,samples);
case ML.KNN:
if (Comp.obj.isMatrix(samples))
return KNN.predict(model,samples);
else if (Comp.obj.isArray(samples) && Comp.obj.isObj(samples[0]))
return KNN.predict(model,samples.map(function (sample) {
return obj2Array(sample,model.features)}));
else if (Comp.obj.isObj(samples))
return KNN.predict(model,obj2Array(samples,model.features));
else
return KNN.predict(model,samples);
break;
case ML.KNN2:
if (Comp.obj.isMatrix(samples))
return samples.map(function (sample) {
return KNN.predict2(model,sample);
});
else if (Comp.obj.isArray(samples) && Comp.obj.isObj(samples[0]))
return samples.map(function (sample) {
return KNN.predict2(model,obj2Array(sample,model.features))
})
else if (Comp.obj.isObj(samples))
return KNN.predict2(model,obj2Array(samples,model.features));
else
return KNN.predict2(model,samples);
break;
case ML.KMN:
return model.clusters
break;
case ML.RF:
if (model.labels) {
if (Comp.obj.isMatrix(samples)) {
return samples.map(function (sample) {
return model.rfs.map(function (rf) {
return RF.code.predictOne(rf,sample);
}).map(function (v,i) {
return { value:model.labels[i], prob:v }
})
});
} else if (Comp.obj.isArray(samples) && typeof samples[0] == 'number') {
return model.rfs.map(function (rf) {
return RF.code.predictOne(rf,samples);
}).map(function (v,i) {
return { value:model.labels[i], prob:v }
})
} // TODO
} else {
// Sample row format: [x1,x2,..,xn]
if (Comp.obj.isMatrix(samples)) {
return samples.map(function (sample) {
return RF.code.predictOne(model,sample);
});
} else if (Comp.obj.isArray(samples) && typeof samples[0] == 'number') {
return RF.predictOne(model,samples);
} // TODO
}
// preprocess(samples,'m')
break;
case ML.SVM:
if (!model._labels) {
// Single SVM
if (Comp.obj.isMatrix(samples))
return samples.map(function (sample) {
return SVM.code.predict(model,sample);
});
else
return SVM.code.predict(model,samples);
} else {
// Multi SVM
if (Comp.obj.isMatrix(samples))
return samples.map(function (sample) {
solutions=model.svms.map(function (svm,index) {
if (svm.threshold==false)
return SVM.code.predict(svm,sample)
else
return SVM.code.predict(svm,sample);
});
return solutions.map(function (v,i) { return { value:model._labels[i], prob:v } });
});
else {
solutions=model.svms.map(function (svm,index) {
if (svm.threshold==false)
return SVM.code.predict(svm,samples)
else
return SVM.code.predict(svm,samples)==1;
})
return solutions.map(function (v,i) { return { value:model._labels[i], prob:v } });
}
}
break;
case ML.SLP:
case ML.MLP:
if (Comp.obj.isMatrix(samples)) {
x=samples;
if (model.xscale)
x=x.map(function (row) { return scale(row,model.xscale) });
result = model.labels?MLP.code.predict(model,x).map(function (r) {
var o={};
r.forEach(function (v,i) { o[model.labels[i]]=v });
return o;
}):relax(MLP.code.predict(model,x));
} else if (Comp.obj.isArray(samples) && typeof samples[0] == 'number') {
x=samples;
if (model.xscale)
x=scale(x,model.xscale);
result = model.labels?MLP.code.predict(model,[x]).map(function (r) {
var o={};
r.forEach(function (v,i) { o[model.labels[i]]=v });
return o;
})[0]:relax(MLP.code.predict(model,[x])[0]);
} else if (Comp.obj.isArray(samples) && typeof samples[0] == 'object') {
x=samples.map(function (sample) { return model.features.map(function (f) { return sample[f] }) });
if (model.xscale)
x=x.map(function (row) { return scale(row,model.xscale) });
result = model.labels?MLP.code.predict(model,x).map(function (r) {
var o={};
r.forEach(function (v,i) { o[model.labels[i]]=v });
return o;
}):relax(MLP.code.predict(model,x));
} else if (Comp.obj.isObj(samples) && model.features) {
x=model.features.map(function (f) { return samples[f] });
if (model.xscale)
x=scale(x,model.xscale);
result = model.labels?MLP.code.predict(model,[x]).map(function (r) {
var o={};
r.forEach(function (v,i) { o[model.labels[i]]=v });
return o;
})[0]:relax(MLP.code.predict(model,[x])[0]);
}
if (Comp.obj.isArray(result)) {
return model.yscale?result.map(function (y) { return unscale(y,model.yscale) }):result;
} else {
return result;
}
break;
case ML.TXT:
// typeof options = {data: string []}
if (Comp.obj.isArray(samples))
return samples.map(function (sample) { return TXT.classify(model,sample) });
else
return TXT.classify(model,samples);
break;
}
},
compact: function (model) {
switch (model.algorithm) {
case ML.DTI:
default:
return DTI.compactTree(model);
}
},
depth: function (model) {
switch (model.algorithm) {
case ML.DTI:
return DTI.depth(model);
case ML.DT:
case ML.ICE:
return ICE.depth(model);
case ML.C45:
return C45.depth(model);
case ML.ID3:
return ID3.depth(model);
}
},
evaluate: function (model,target,samples) {
switch (model.algorithm) {
case ML.DTI:
default:
return DTI.evaluate(model,target,samples);
}
},
info: function (model) {
switch (model.algorithm) {
case ML.C45:
return C45.info(model);
case ML.DT:
case ML.ICE:
return ICE.info(model);
case ML.ID3:
return ID3.info(model);
}
},
/** Learning: Create a classification model from training data (or an empty model that can be updated)
*
*/
learn: function (options) {
var model,data,data2,x,y,features,featureTypes,test,target,
result,cols,n_ins,n_outs,x,y,xscale,xoffset,xshift,yscale,yoffset,yshift,key,err,
t0=Io.time();
if (options==_) options={};
switch (options.algorithm) {
case ML.ANN:
// typeof options = { x,y,features?,target?,layers:number [], trainerror:number}
data = preprocess(options,'io',options);
model={};
model.algorithm=options.algorithm
if (!options.layers) options.layers=[]
if (data)
model.network = new ANN.Network(options.layers[0],options.layers[options.layers.length-1]);
else throw 'ML.learn.ANN: Invalid options';
model.network.evolve(data,options);
model.time=Io.time()-t0;
return model;
break;
case ML.CNN:
// typeof options = {x:[][],y:[],..}
model = CNN.create(options);
model.algorithm=options.algorithm;
model.time=Io.time()-t0;
return model;
break;
case ML.C45:
// typeof options = {data: {}[], target:string, features: string []} |
// {data: [][], target?:string, features?: string []} |
// {x: number [][], y:[]} |
// {data: {x,y}[] }
var model = C45.create();
if (options.x && options.y) {
features=options.x[0].map(function (col,i) { return String(i) });
featureTypes=options.x[0].map(function (col,i) { return 'number' });
data=options.x.map(function (row,i) { row=row.slice(); row.push(options.y[i]); return row});
target='y';
} else if (options.data && Comp.obj.isMatrix(options.data)) {
data=options.data;
features=options.features||options.data[0].slice(0,-1).map(function (col,i) { return String(i) });
featureTypes=options.data[0].slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' });
target=options.target||'y';
} else if (options.data && Comp.obj.isObj(options.data[0]) && options.data[0].x && options.data[0].y!=undefined) {
data=options.data.map(function (row) { return row.x.concat(row.y) });
features=options.features||options.data[0].x.slice(0,-1).map(function (col,i) { return String(i) });
featureTypes=options.data[0].x.slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' });
target=options.target||'y';
} else if (options.data && Comp.obj.isArray(options.data) && Comp.obj.isObj(options.data[0]) &&
options.target && options.features) {
rowNames=Comp.obj.isArray(options.target)?options.features.concat(options.target):
options.features.concat([options.target]);
data=options.data.map(function (row) { return obj2Array(row,rowNames) })
features=options.features;
featureTypes=data[0].slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' });
target=options.target;
} else throw 'ML.learn.C45: Invalid options';
C45.train(model,{
data: data,
target: target,
features: features,
featureTypes: featureTypes
});
model.algorithm=options.algorithm
model.time=Io.time()-t0;
return model;
break;
case ML.DTI:
// typeof options = {data: {}[], target:string, features: string [], eps;number, maxdepth} |
// {x: number [][], y:[], eps;number, maxdepth}
if (options.eps==_) options.eps=0;
if (options.maxdepth==_) options.maxdepth=20;
if (options.data && options.target && options.features)
model = DTI.create(options);
else if (options.x && options.y) {
if (options.x.length != options.y.length) throw 'ML.learn.DTI: X and Y vector have different length';
data=options.x.map(function (row,i) { row=row.slice(); row.push(options.y[i]); return row});
features=Comp.array.init(data[0].length-1,function (i) { return String(i)});
target=String(data[0].length-1);
// console.log(data,features,target)
model = DTI.create({
data:data,
features:features,
target:target,
eps:options.eps,
maxdepth:options.maxdepth
});
} else throw 'ML.learn.DTI: Invalid options';
model.algorithm=options.algorithm;
model.time=Io.time()-t0;
return model;
case ML.ICE:
case ML.DT:
if (options.eps==_) options.eps=0;
if (options.data && options.target && options.features)
model = ICE.create(options);
else if (options.x && options.y) {
if (options.x.length != options.y.length) throw 'ML.learn.ICE: X and Y vector have different length';
data=options.x.map(function (row,i) { row=row.slice(); row.push(options.y[i]); return row});
features=Comp.array.init(data[0].length-1,function (i) { return String(i)});
target=String(data[0].length-1);
model = ICE.create({
data:data,
features:features,
target:target,
eps:options.eps,
});
} else throw 'ML.learn.ICE: Invalid options';
model.algorithm=options.algorithm;
model.eps=options.eps;
model.time=Io.time()-t0;
return model;
break;
case ML.ID3:
if (options.data && options.target && options.features)
model = ID3.createTree(options.data,options.target,
options.features);
else throw 'ML.learn.ID3: Invalid options';
model.algorithm=options.algorithm
model.time=Io.time()-t0;
return model;
break;
case ML.KNN:
// typeof @options = {data: {}[]|[][], distance?:function|string,k?:number}
// typeof @options = {x:number [][], y:number [],
// distance?:function|string,k?:number}
if (options.features && options.target) target=options.target,features = options.features;
else {
features = [];
if (options.data) {
for(key in options.data[0]) features.push(key);
target = features.pop()
} else if (options.x) {
for(key in options.x[0]) features.push('x'+key);
target='y';
}
}
if (options.data && Comp.obj.isObj(options.data[0])) {
x = options.data.map(function (row) { return obj2Array(row,features) });
y = options.data.map(function (row) { return row[target] })
} else if (options.data && Comp.obj.isMatrix(options.data)) {
x = options.data,map(function (row) { return row.slice(0,row.length-1) });
y = options.data,map(function (row) { return row[row.length-1] });
} else if (options.x && options.y) {
x = options.x;
y = options.y;
}
model = KNN.create(
x,
y,
{
distance:options.distance,
k:options.k
});
model.algorithm = options.algorithm
model.features = features
model.target = target
model.time=Io.time()-t0;
return model;
break;
case ML.KNN2:
// typeof @options = {data: {}[]|[][], distance?:function|string,k?:number}
// typeof @options = {x:number [][], y:number [],
// distance?:function|string,k?:number}
if (options.features && options.target) target=options.target,features = options.features;
else {
features = [];
if (options.data) {
for(key in options.data[0]) features.push(key);
target = features.pop()
} else if (options.x) {
for(key in options.x[0]) features.push('x'+key);
target='y';
}
}
if (options.data && Comp.obj.isObj(options.data[0])) {
x = options.data.map(function (row) { return obj2Array(row,features) });
y = options.data.map(function (row) { return row[target] })
} else if (options.data && Comp.obj.isMatrix(options.data)) {
x = options.data,map(function (row) { return row.slice(0,row.length-1) });
y = options.data,map(function (row) { return row[row.length-1] });
} else if (options.x && options.y) {
x = options.x;
y = options.y;
}
model = KNN.create2(
{
x : x,
y : y,
distance:options.distance,
k:options.k
});
model.algorithm=options.algorithm
model.features = features
model.target = target
model.time=Io.time()-t0;
return model;
break;
case ML.KMN:
if (options.data && Comp.obj.isMatrix(options.data)) {
data=options.data;
}
model = KMN.cluster({
data:data,
k:options.k,
distance:options.distance,
epochs:options.epochs,
})
model.algorithm=options.algorithm
model.data = data
model.time=Io.time()-t0;
return model;
break;
case ML.RF:
var model={};
// Single Binary RF (y={-1,1}) or Multi-RF (y:string is in labels)
// typeof options = {data: {}[], target:string, features: string []} |
// {data: [][], target?:string, features?: string []} |
// {x: number [][], y: {-1,1} []} |
// {data: {x,y}[] }
// {data: {x,y}[], labels: string [] }
if (!options.x || !options.y) throw 'ML.learn.RF: Invalid options';
// data=preprocess(data,'xmy',{features:features,target:target})
data={x:options.x,y:options.y}; // TODO
if (options.labels) {
// multi-RF
model.labels = options.labels;
model.rfs = model.labels.map (function (label) { return RF() });
model.rfs.forEach (function (rf,i) {
var y = data.y.map(function (label) { return label==model.labels[i]?1:-1} );
RF.code.train(rf,options.x,y,{
numTrees:options.numTrees,
maxDepth:options.maxDepth,
numTries:options.numTries,
type:options.weakType,
});
});
} else {
model = RF();
features=options.x[0].map(function (col,i) { return String(i) });
target='y';
RF.code.train(model,
options.x,
options.y,
{
numTrees:options.numTrees,
maxDepth:options.maxDepth,
numTries:options.numTries,
type:options.weakType,
});
}
model.algorithm=options.algorithm
model.time=Io.time()-t0;
return model;
break;
case ML.RL:
// Create learner instance
model = {}
options.environment=checkOptions(options.environment,{});
options.environment.getMaxNumActions=
checkOption(options.environment.getMaxNumActions,
function () { return options.actions.length })
options.environment.getNumStates=
checkOption(options.environment.getNumStates,
function () { return options.states.length })
var allowedActions=checkOption(options.environment.allowedActions, function () { return options.actions });
options.environment.allowedActions=
// Ensure that allowedActions return number array!
function (state) {
return allowedActions(state).map(function (a) {
return options.actions.indexOf(a)
})
}
var nextState = options.environment.nextState;
if (nextState) {
options.environment.nextState = function (state,action) {
return nextState(state,options.actions[action])
}
}
switch (options.kind) {
case ML.DQNAgent:
model = RL.DQNAgent(
options.environment,
{
alpha:options.alpha,gamma:options.gamma,epsilon:options.epsilon,
experience_add_every:options.experience_add_every,
experience_size:options.experience_size,
learning_steps_per_iteration:options.learning_steps_per_iteration,
tderror_clamp:options.tderror_clamp,
num_hidden_units:options.num_hidden_units,
update:options.update,
}
)
break;
case ML.DPAgent:
model = RL.DPAgent(
options.environment,
{alpha:options.alpha,beta:options.beta,gamma:options.gamma,
epsilon:options.epsilon,lambda:options.lambda}
)
break;
case ML.TDAgent:
model = RL.TDAgent(
options.environment,
// specs
{alpha:options.alpha,beta:options.beta,gamma:options.gamma,
epsilon:options.epsilon,lambda:options.lambda,
replacing_traces:options.replacing_traces,
smooth_policy_update:options.smooth_policy_update,
update:options.update,
planN:options.planN}
)
break;
}
model.algorithm = options.algorithm;
model.kind = options.kind;
if (options.actions) model.actions = options.actions;
if (options.states) model.states = options.states;
if (options.rewards) model.rewards = options.rewards;
return model;
break;
case ML.SLP:
case ML.MLP:
// typeof options = {x: number [][],
// y: number number [][] | string [],
// hidden_layers?:[],epochs?:number,
// labels?:string [], features?: string [],
// regression?,
// normalize?, bipolar?, eps?:number | number [], verbose?}
//
// y and MLP(learn) requires [[p1,p2,..],[p1,p2,..],..] with 0>=p>=1
// p:label probability
x=options.x;
if (Comp.obj.isArray(options.x) && typeof options.x[0] == 'number')
x=wrap(options.x);
else if (!Comp.obj.isMatrix(options.x) && Comp.obj.isArray(options.x) && typeof options.x[0] == 'object' && options.features) {
x=options.x.map(function (o) {
return options.features.map(function (f) { return o[f] });
});
}
if (Comp.obj.isMatrix(options.y))
y=options.y;
else if (Comp.obj.isArray(options.y) && typeof options.y[0] == 'number')
y=wrap(options.y);
else if (Comp.obj.isArray(options.y) && options.labels) {
y=options.y.map(function (l1) {
return options.labels.map(function (l2) {
return l1==l2?1:0;
});
});
} else throw 'ML.learn.MLP: invalid options';
if (options.normalize) {
// normalize each variable independently!?
var max=x[0].map(function (col) { return col}),
min=x[0].map(function (col) { return col});
x.forEach(function (row) { row.forEach(function (col,i) {
max[i]=Math.max(max[i],col);
min[i]=Math.min(min[i],col) }) });
xshift=options.bipolar?-1:0;
xscale=max.map(function (x,i) { return (xshift?2:1)/((x-min[i])==0?1:x-min[i])});
xoffset=min;
x=x.map(function (row) { return row.map(function (col,i) { return xshift+(col-xoffset[i])*xscale[i] }) });
if (options.regression) {
// scale y, too, [0,1]
max=y[0].map(function (col) { return col});
min=y[0].map(function (col) { return col});
y.forEach(function (row) { row.forEach(function (col,i) {
max[i]=Math.max(max[i],col);
min[i]=Math.min(min[i],col) }) });
yshift=options.bipolar?-1:0;
yscale=max.map(function (x,i) { return (yshift?2:1)/((x-min[i])==0?1:x-min[i])});
yoffset=min;
y=y.map(function (row) { return row.map(function (col,i) { return yshift+(col-yoffset[i])*yscale[i] }) });
}
}
model = MLP({
input : x,
output : y,
n_ins : x[0].length,
n_outs : y[0].length,
hidden_layer_sizes:options.algorithm==ML.SLP?[]:(options.hidden_layers||[])
});
model.algorithm=options.algorithm;
model.labels=options.labels;
model.features=options.features;
model.xscale=options.normalize?{k:xscale,off:xoffset,shift:xshift}:undefined;
model.yscale=options.normalize&&options.regression?{k:yscale,off:yoffset,shift:yshift}:undefined;
model.nOutputs=y[0].length;
MLP.code.set(model,'log level',options.verbose||0); // 0 : nothing, 1 : info, 2 : warning.
if (options.epochs) MLP.code.train(model,{
epochs : options.epochs
});
model.time=Io.time()-t0;
return model;
break;
case ML.SVM:
// typeof options = {x: number [][],
// y: ({-1,1}|string) [],
// labels?:string|number [],
// threshold?:number|false,
// C?:numer,tol?:number,max_passes?:number,alpha_tol?:number,kernel?:{}}
// If classes then multi-SVM (one for each class to be separated)!
if (!options.labels) {
model = SVM({
x:options.x,
y:options.y,
threshold:options.threshold,
});
model.algorithm=options.algorithm
SVM.code.train(model,{
C:options.C||1.0,
tol:options.tol||1e-4,
max_passes:options.max_passes||20,
alpha_tol:options.alpha_tol||1e-5,
kernel:options.kernel
});
} else {
model={};
model.algorithm=options.algorithm;
model._labels=options.labels;
model.svms=options.labels.map(function (cl) {
return SVM({
x:options.x,
y:options.y.map(function (y) { return y==cl?1:-1 }),
threshold:options.threshold,
});
});
model.svms.forEach(function (svm) {
SVM.code.train(svm,{
C:options.C||1.0,
tol:options.tol||1e-4,
max_passes:options.max_passes||20,
alpha_tol:options.alpha_tol||1e-5,
kernel:options.kernel
});
});
// Create one SVM for each class
// Transform y vector
}
model.time=Io.time()-t0;
return model;
break;
case ML.TXT:
// typeof options = {data: string []}
model = TXT.create(options.data,{
});
model.algorithm=options.algorithm
return model;
break;
}
},
// add noise to numerical data to create synthetic data
noise: function (data,noise) {
if (Comp.obj.isMatrix(data)) {
return data.map(function (row) {
return row.map(function (v,i) {
if (typeof noise == 'number')
return v+(Math.random()-0.5)*noise;
else return v+(Math.random()-0.5)*noise[i]
})
})
} else if (Comp.obj.isArray(data) && Comp.obj.isObject(data[0])) {
return data.map(function (row) {
var o={};
for (var p in row) {
if (typeof noise == 'number')
o[p] = row[p]+(Math.random()-0.5)*noise;
else o[p] = row[p]+(Math.random()-0.5)*noise[p]
}
return o;
})
}
},
preprocess:preprocess,
print: function (model,indent,compact) {
switch (model.algorithm) {
case ML.DTI:
return DTI.print(model,indent,compact);
case ML.DT:
case ML.ICE:
return ICE.print(model,indent);
case ML.C45:
return C45.print(model,indent);
case ML.ID3:
return ID3.print(model,indent);
}
},
// Only text module
similarity : TXT.similarity,
stats : STAT,
// Check model consistency
test: function (model,samples) {
var x,y,data,res,p=0.0;
switch (model.algorithm) {
case ML.ANN:
data=preprocess(samples,'xmya',{features:model.features,target:model.target});
// TODO
break;
case ML.C45:
// Sample row format: [x1,x2,..,y]
if (Comp.obj.isMatrix(samples)) {
samples.forEach(function (sample) {
x=sample.slice(0,sample.length-1);
y=sample[sample.length-1];
res= C45.classify(model,x);
if (res==y) p += 1;
});
return p/samples.length;
} else if (Comp.obj.isArray(samples)) {
x=samples.slice(0,samples.length-1);
y=samples[samples.length-1];
res = C45.classify(model,x);
return res==y?1.0:0.0
} else if (Comp.obj.isObj(samples) && model.features) {
}
break;
case ML.TXT:
var model = model.string?{ data : [model.string] }:model;
if (Comp.obj.isArray(samples))
return samples.map(function (sample) {
return TXT.classify(model,sample).match
});
else
return TXT.classify(model,samples).match;
break;
}
},
/** Update a learned model
*
*/
update: function (model,options) {
switch (model.algorithm||options.algorithm) {
case ML.CNN:
break;
case ML.DTI:
// typeof @options = {data: number [][], target:string, features: string [], eps?:number, maxdepth?:number} |
// {x: number [][], y:[], eps?:number, maxdepth?:number}
if (options.eps==_) options.eps=0;
if (options.maxdepth==_) options.maxdepth=20;
if (options.data && options.target && options.features)
model = DTI.update(model,options);
else if (options.x && options.y) {
if (options.x.length != options.y.length) throw 'ML.update.DTI: X and Y vector have different length';
data=options.x.slice();
data=data.map(function (row,i) {row.push(options.y[i]); return row});
features=Comp.array.init(data[0].length-1,function (i) { return String(i)});
target=String(data[0].length-1);
console.log(data,features,target)
model = DTI.update(model,{
data:data,
features:features,
target:target,
eps:options.eps,
maxdepth:options.maxdepth
});
} else throw 'ML.update.DTI: Invalid options';
model.algorithm=options.algorithm;
return model;
break;
case ML.MLP:
return MLP.code.train(model,{
epochs : options.epochs||1
});
break;
case ML.RL:
switch (model.kind) {
case ML.DQNAgent:
return RL.DQNAgent.code.learn(model,options);
break;
case ML.DPAgent:
return RL.DPAgent.code.learn(model,options);
break;
case ML.TDAgent:
return RL.TDAgent.code.learn(model,options);
break;
}
break;
}
},
ML:ML,
};
ICE.ml=ml;
CNN.ml=ml;
ml.predict=ml.classify;
ml.learner=ml.learn;
ml.train=function (model,options) {
if (model.algorithm) return ml.update(model,options);
else return ml.learn(model);
}
ml.best=ml.stats.utils.best;
module.exports = {
agent:ml,
classify:ml.classify,
column:ml.column,
compact:ml.compact,
depth:ml.depth,
entropy:STAT.entropy,
entropyN:STAT.entropyN,
entropyDep:STAT.entropyDep,
entropyT:STAT.entropyT,
evaluate:ml.evaluate,
info:ml.info,
learn:ml.learn,
noise:ml.noise,
options:options,
pca:PCA,
preprocess:preprocess,
print:ml.print,
stats:STAT,
test:ml.test,
unique:ml.unique,
update:ml.update,
ML:ML,
current:function (module) { current=module.current; Aios=module; }
}
};
BundleModuleCode['ml/ice']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Ankit Kuwadekar, Stefan Bosse
** $INITIAL: (C) 2014, Ankit Kuwadekar
** $MODIFIED: (C) 2006-2018 bLAB by sbosse
** $VERSION: 1.3.2
**
** $INFO:
**
** ICE: C45/ID3 Decision Tree Algorithm supporting feature variables with eps intervals
**
** Portable model
**
** New:
** typeof eps = number | [epsx1:number,epsx2:number,..]
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios=none;
var that;
/**
* Map of valid tree node types
* @constant
* @static
*/
var NODE_TYPES = {
RESULT: 'result',
FEATURE: 'feature',
FEATURE_VALUE: 'feature_value'
};
var NL ='\n'
/**
* Creates a new tree
*/
function createTree(data, target, features, eps) {
var ml = that.ml;
var targets = ml.stats.unique(ml.stats.utils.column(data, target));
if (targets.length == 1) {
return {
type: NODE_TYPES.RESULT,
name: targets[0],
};
}
if (features.length == 0) {
var topTarget = ml.stats.mostCommon(targets);
return {
type: NODE_TYPES.RESULT,
name: topTarget,
};
}
var split = ml.stats.splitEps(data,features,target,targets,eps);
var bestFeature = split.feature;
var index = features.indexOf(bestFeature);
var remainingFeatures = split.remainingFeatures;
var remainingEps =
typeof eps == 'number'?eps:remainingFeatures.map(function (v) { return eps[features.indexOf(v)] });
var possibleValues = split.possibleValues;
var node = {
type: NODE_TYPES.FEATURE,
name: bestFeature,
index: index,
eps: that.ml.stats.utils.selectEps(eps,index)
};
node.vals = split.choices.map(function (c) {
var child_node = {
val : c.val,
eps : that.ml.stats.utils.selectEps(eps,index),
type: NODE_TYPES.FEATURE_VALUE
};
child_node.child = createTree(c.data, target, remainingFeatures, remainingEps);
return child_node;
})
return node;
}
function depth(model) {
switch (model.type) {
case NODE_TYPES.RESULT: return 1;
case NODE_TYPES.FEATURE:
return 1+Comp.array.max(model.vals.map(function (val) {
return depth(val);
}));
case NODE_TYPES.FEATURE_VALUE:
return 1+depth(model.child);
}
return 0;
}
function info(model) {
var vl = vars(model);
return {
depth:depth(model),
nodes:vl.length,
vars:vl.unique(),
}
}
function predictEps(model,sample,prob,eps) {
var root = model;
if (!prob) prob=1;
while (root.type !== NODE_TYPES.RESULT) {
var attr = root.name;
var sampleVal = sample[attr];
// kNN approximation
var childNode = null;
root.vals.forEach(function(node) {
var fit=Math.abs(node.val-sampleVal);
if (!childNode || fit < childNode.fit) childNode={fit:fit,node:node};
});
if (childNode){
// with fit quality propagation
prob = prob * (1-Math.abs(childNode.fit/that.ml.stats.utils.selectEps(eps,root.index))/4)
root = childNode.node.child;
} else {
root = root.vals[0].child;
}
}
return {value:root.name,prob:prob};
};
function printModel(model,indent) {
var line='',sep;
if (indent==undefined) indent=0;
if (!model) return '';
var sp = function () {var s=''; for(var i=0;i<indent;i++) s+=' '; return s};
switch (model.type) {
case NODE_TYPES.RESULT:
return sp()+'-> '+model.name+NL;
case NODE_TYPES.FEATURE:
line=sp()+'$'+model.name+'?'+NL;
model.vals.forEach(function (v) {
line += printModel(v,indent+2);
});
return line;
case NODE_TYPES.FEATURE_VALUE:
line=sp()+'=['+(model.val-model.eps)+','+(model.val+model.eps)+']'+NL;
return line+printModel(model.child,indent+2);
}
return 'model?';
}
function vars(model) {
switch (model.type) {
case NODE_TYPES.RESULT: return [];
case NODE_TYPES.FEATURE:
return [model.name].concat(Comp.array.flatten(model.vals.map(vars)));
case NODE_TYPES.FEATURE_VALUE:
return vars(model.child);
}
return [];
}
that = module.exports = {
create: function (options) {
return createTree(options.data,
options.target,
options.features,
options.eps)
},
depth:depth,
info:info,
ml:{},
predict:function (model,sample) {
return predictEps(model,sample,1,model.eps)
},
print:printModel,
}
};
BundleModuleCode['ml/dti']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2018 bLAB
** $CREATED: 03-03-16 by sbosse.
** $VERSION: 1.4.2
**
** $INFO:
**
** Interval Decision Tree Learner
**
** Modified ID3-based Decision Tree Algorithm that wraps all data with 2-eps intervals and uses
** interval instead single value arithmetic for entropy calculation and feature selection.
** The classification bases on a nearest-neighbourhood look-up of best matching results.
**
** Two different algorithms are supported:
**
** 1. Static (using learn), the DTI learner using attribute selection based on entropy.
** The training data must be available in advance.
** 2. Dynamic (using update), the DTI learrner using attribute selection based on significance.
** The training data is applied sequentielly (stream learning) updating the model.
**
** Though in principle the both algrotihms can be mixed (first static, then dynamic updating),
** the resulting model will have poor classification quality. Either use static or only dynamic
** (stream) learning.
**
** Portable model
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios=none;
var min = Comp.pervasives.min;
var max = Comp.pervasives.max;
/**
* Map of valid tree node types
* @constant
* @static
*/
var NODE_TYPES = {
RESULT: 'result',
FEATURE: 'feature',
FEATURE_VALUE: 'feature_value'
};
function Result(key) {
return {
type:NODE_TYPES.RESULT,
name:key
}
}
function Feature(name,vals) {
return {
type:NODE_TYPES.FEATURE,
name:name,
vals:vals
}
}
// A value can be a scalar or a range {a,b} object
function Value(val,child) {
return {
type:NODE_TYPES.FEATURE_VALUE,
val:val,
child:child
}
}
/** Add a new training set with optional data set merging and value interval expansion.
*
*/
function add_training_set(data,set,merge) {
if (merge) {
// Merge a data set with an existing for a specific key; create value ranges
} else
data.push(set);
}
/**
* Computes Log with base-2
* @private
*/
function log2(n) {
return Math.log(n) / Math.log(2);
}
function results(model) {
var line='',sep;
if (!model) return '';
switch (model.type) {
case NODE_TYPES.RESULT:
return model.name;
case NODE_TYPES.FEATURE:
sep='';
line='';
Comp.array.iter(model.vals,function (v) {
line += sep+results(v);
sep=',';
});
return line;
case NODE_TYPES.FEATURE_VALUE:
return results(model.child);
}
return 'result?';
}
/**
* Finds element with highest occurrence in a list
* @private
*/
function mostCommon(list) {
var elementFrequencyMap = {};
var largestFrequency = -1;
var mostCommonElement = null;
list.forEach(function(element) {
var elementFrequency = (elementFrequencyMap[element] || 0) + 1;
elementFrequencyMap[element] = elementFrequency;
if (largestFrequency < elementFrequency) {
mostCommonElement = element;
largestFrequency = elementFrequency;
}
});
return mostCommonElement;
}
function addVal(v1,v2) {
if (v1.a!=undefined) {
if (v2.a!=undefined) return {a:v1.a+v2.a,b:v1.b+v2.b};
else return {a:v1.a+v2,b:v2.b+v2};
} else if (v2.a!=undefined) return {a:v2.a+v1,b:v2.b+v1};
else return v1+v2;
}
function lowerBound(v) {
if (v.a==undefined) return v; else return v.a;
}
function upperBound(v) {
if (v.b==undefined) return v; else return v.b;
}
function equal(v1,v2) {
return (v1==v2 ||
(upperBound(v1) == upperBound(v2) &&
(lowerBound(v1) == lowerBound(v2))))
}
function overlap(v1,v2) {
return (upperBound(v1) >= lowerBound(v2) && upperBound(v1) <= upperBound(v2)) ||
(upperBound(v2) >= lowerBound(v1) && upperBound(v2) <= upperBound(v1))
}
function containsVal(vl,v) {
for (var i in vl) {
var v2=vl[i];
if (overlap(v,v2)) return true;
}
return false;
}
function centerVal(v) {
if (v.a==undefined) return v; else return (v.a+v.b)/2;
}
function distanceVal (v1,v2) {
return Math.abs(centerVal(v1)-centerVal(v2));
}
function Bounds(vl,v) {
if (vl.length==0) return {a:v,b:v};
else if (v==undefined) return {a:Min(vl),b:Max(vl)};
else return {a:Min([Min(vl),v]),b:Max([Max(vl),v])};
}
function Min(vals) {
var min=none;
Comp.array.iter(vals, function (val) {
if (min==none) min=(val.a==undefined?val:val.a);
else min=val.a==undefined?(val<min?val:min):(val.a<min?val.a:min);
});
return min;
}
function Max(vals) {
var max=none;
Comp.array.iter(vals,function (val) {
if (max==none) max=(val.b==undefined?val:val.b);
else max=(val.b==undefined?(val>max?val:max):(val.b>max?val.a:max));
});
return max;
}
// Return interval of a value x with a<=x_center-eps, b>=x_center+eps
function epsVal(x,eps) {
if (x.a == undefined) return {a:x-eps,b:x+eps};
else if ((x.b-x.a) < 2*eps) return {a:centerVal(x)-eps,b:centerVal(x)+eps};
else return x;
}
/** Filter out unique values that are spaced at least by eps
*
*/
function uniqueEps(data,eps) {
var results=[];
Comp.array.iter(data,function (x) {
var found;
if (!results.length) results.push(x);
else {
Comp.array.iter(results,function (y,i) {
if (found) return;
found = Math.abs(centerVal(x)-centerVal(y))<eps;
if (found) // create new overlapping value with +-eps extensions
results[i]={a:Min([x,y])-eps,b:Max([x,y])+eps}
});
if (!found) results.push(x);
}
});
return results;
}
/** Compact tree, merge nodes and intervals.
** adjust=true: Adjust overlapping feature variable value intervals!!!
*/
function compactTree(model,adjust) {
var i,j,vi,vj,_vals,merged;
function target(model) {
var line;
switch (model.type) {
case NODE_TYPES.RESULT:
return model.name;
case NODE_TYPES.FEATURE:
line = model.name+'?'+target;
Comp.array.iter(model.vals,function (v) {
line += target(v);
});
return line;
case NODE_TYPES.FEATURE_VALUE:
line='='+(model.val.a==undefined?model.val:'['+model.val.a+','+model.val.b+']')+NL;
return line+target(model.child);
}
}
if (!model) return model;
switch (model.type) {
case NODE_TYPES.RESULT:
return model;
break;
case NODE_TYPES.FEATURE:
_vals=[];
// 1. Merge
for (i in model.vals) {
vi=model.vals[i];
assert((vi.type==NODE_TYPES.FEATURE_VALUE)||'vi.type==NODE_TYPES.FEATURE_VALUE');
merged=false;
loopj: for(j in _vals) {
vj=_vals[j];
if (target(vi.child)==target(vj.child)) {
merged=true;
vj.val={a:Min([vi.val,vj.val]),b:Max([vi.val,vj.val])}
break loopj;
}
}
if (!merged) {
_vals.push(vi);
vi.child=compactTree(vi.child);
}
}
// 2. Adjust overlapping value intervals!
if (adjust) {
// TODO: approach too simple!!!!
for (i in _vals) {
i=Comp.pervasives.int_of_string(i);
if (_vals[i+1]) {
if (upperBound(_vals[i].val) > lowerBound(_vals[i+1].val)) {
if (_vals[i].val.b) _vals[i].val.b=lowerBound(_vals[i+1].val)-1;
else _vals[i+1].val.a=upperBound(_vals[i].val)+1;
}
}
}
}
model.vals=_vals;
return model;
break;
case NODE_TYPES.FEATURE_VALUE:
return model;
break;
}
}
/** Creates a new tree from training data (data)
*
* data is {x1:v1,x2:v2,..,y:vn} []
* target is classification key name
* features is ['x1','x2,',..] w/o target variable
* eps is interval applied to all data values
*
*/
function createTree(data, target, features, options) {
var _newS,child_node,bounds;
var targets = Comp.array.unique(Comp.array.pluck(data, target));
// console.log(targets)
if (options.maxdepth==undefined) options.maxdepth=1;
if (options.maxdepth==0) return Result('-');
// console.log(data);
// console.log(features);
//Aios.aios.log('createTree:'+targets.length);
//try {Aios.aios.CP();} catch (e) {throw 'DTI.createTree: '+options.maxdepth };
if (Aios) Aios.aios.CP();
if (targets.length == 1) return Result(targets[0]);
if (features.length == 0) {
var topTarget = mostCommon(targets);
return Result(topTarget)
}
var bestFeatures = getBestFeatures(data, target, features, options.eps);
var bestFeature = bestFeatures[0];
var remainingFeatures = Comp.array.filtermap(bestFeatures,function (feat) {
if (feat.name!=bestFeature.name) return feat.name;
else return none;
});
/*
var possibleValues = Comp.array.sort(Comp.array.pluck(data, bestFeature.name), function (x,y) {
if (upperBound(x) < lowerBound(y)) return -1; else return 1; // increasing value order
});
*/
var possibleValues = getPossibleVals(data,bestFeature.name);
var vals=[];
//console.log(bestFeatures);
//console.log(possibleValues);
var partitions=partitionVals(possibleValues,options.eps);
// Aios.aios.log(partitions);
//console.log(bestFeatures);
//console.log(possibleValues);
if (partitions.length==1) {
// no further 2*eps separation possible, find best feature by largest distance
// resort best feature list with respect to value deviation
bestFeatures.sort(function (ef1,ef2) {
if (ef1.d > ef2.d) return -1; else return 1;
});
bestFeature = bestFeatures[0];
possibleValues = getPossibleVals(data,bestFeature.name);
Comp.array.iter(mergeVals(possibleValues),function (val,i) {
_newS = data.filter(function(x) {
// console.log(x[bestFeature.name],val,overlap(val,x[bestFeature.name]))
return overlap(val,x[bestFeature.name]);
});
child_node = Value(val);
options.maxdepth--;
child_node.child = createTree(_newS, target, remainingFeatures, options);
//console.log(_newS);
vals.push(child_node);
})
} else Comp.array.iter(partitions,function (partition,i) {
_newS = data.filter(function(x) {
// console.log(x[bestFeature.name],v,overlap(x[bestFeature.name],v))
return containsVal(partition,x[bestFeature.name]);
});
bounds = Bounds(partition);
child_node = Value(options.eps==0?{a:bounds.a,b:bounds.b}:{a:bounds.a-options.eps,b:bounds.b+options.eps});
options.maxdepth--;
child_node.child = createTree(_newS, target, remainingFeatures, options);
//console.log(_newS);
vals.push(child_node);
});
return Feature(bestFeature.name,vals);
}
/** Return the depth of the tree
*
*/
function depth(model) {
switch (model.type) {
case NODE_TYPES.RESULT: return 0;
case NODE_TYPES.FEATURE:
return 1+Comp.array.max(model.vals,function (val) {
return depth(val);
});
case NODE_TYPES.FEATURE_VALUE:
return depth(model.child);
}
return 0;
}
/** Computes entropy of a list with 2-epsilon intervals
*
*/
function entropyEps(vals,eps) {
// TODO: overlapping value intervals
var uniqueVals = Comp.array.unique(vals);
var probs = uniqueVals.map(function(x) {
return probEps(x, vals, eps)
});
var logVals = probs.map(function(p) {
return -p * log2(p)
});
return logVals.reduce(function(a, b) {
return a + b
}, 0);
}
function entropyEps2(vals,eps) {
// TODO: overlapping value intervals
var uniqueVals = uniqueEps(vals,eps);
var probs = uniqueVals.map(function(x) {
return probEps2(x, vals, eps)
});
var logVals = probs.map(function(p) {
return -p * log2(p)
});
return logVals.reduce(function(a, b) {
return a + b
}, 0);
}
function getBestFeatures(data,target,features,eps) {
var bestfeatures=[];
function deviation(vals) {
var n = vals.length;
var mu=Comp.array.sum(vals,function (val) {
return (lowerBound(val)+upperBound(val))/2;
})/n;
var dev=Comp.array.sum(vals,function (val) {
return Math.pow(((lowerBound(val)+upperBound(val))/2)-mu,2);
})/n;
return dev;
}
for (var feature in features) {
if (features[feature]==undefined) throw 'DTI.getBestFeatures: invalid feature vector';
var vals=Comp.array.pluck(data, features[feature]).map(function (val) {return val==undefined?0:val});
var e = entropyEps(vals,eps);
var d = deviation(vals);
var min = Min(vals);
var max = Max(vals);
bestfeatures.push({e:e,d:d,range:{a:min,b:max},name:features[feature]});
}
bestfeatures.sort(function (ef1,ef2) {
if (ef1.e > ef2.e) return -1; else return 1;
});
return bestfeatures;
}
/** Find in one data set the most significant feature variable (i.e., with highest value)
*/
function getSignificantFeature(data,features) {
var f,sig;
for (f in features) {
if (sig==undefined || sig.val < data[features[f]]) sig={name:features[f],val:data[features[f]]};
}
return sig;
}
function getPossibleVals(data,feature) {
return Comp.array.sort(Comp.array.pluck(data, feature), function (x,y) {
if (upperBound(x) < lowerBound(y)) return -1; else return 1; // increasing value order
});
}
/** Merge values and intervals
*/
function mergeVals(vals) {
var _vals,
merged,i,j;
for (i in vals) {
var vi = vals[i];
if (!_vals) _vals=[vi];
else {
// Find overlapping values and merge
merged=false;
loopj: for (j in _vals) {
var vj = _vals[j];
if (equal(vi,vj)) {
merged=true;
break loopj;
}
else if (overlap(vi,vj)) {
merged=true;
_vals[j]={a:Min([vi,vj]),b:Max([vi,vj])};
break loopj;
}
}
if (!merged) _vals.push(vi);
}
}
//Aios.aios.log(_vals);
return _vals||[];
}
/**
* Predicts class for sample
*/
function nearestVal(vals,sample,fun) {
var best=none;
for (var v in vals) {
var d=fun?distanceVal(fun(vals[v]),sample):distanceVal(vals[v],sample);
if (best==none)
best={v:vals[v],d:d};
else if (best.d > d)
best={v:vals[v],d:d};
}
if (best) return best.v;
else return none;
}
/** Parttition an ordered set of values
* Each partition of values has at least 2*eps distance to the next partition.
*
*/
function partitionVals(vals,eps) {
var last=none;
var partitions=[];
var partition=[];
for(var i in vals) {
var val0=vals[i];
var val1=vals[i-1];
if (val1==undefined) partition.push(val0);
else if ( upperBound(val0) < upperBound(addVal(val1,2*eps))) partition.push(val0);
else {
partitions.push(partition);
partition=[val0];
}
}
if (partition.length>0) partitions.push(partition);
return partitions;
}
/** Make a predicition with sample data
*
*/
function predict(model,sample) {
var root = model;
while (root && root.type !== NODE_TYPES.RESULT) {
var attr = root.name;
var sampleVal = sample[attr];
var childNode = nearestVal(root.vals,sampleVal,function (node) {
return node.val;
});
if (childNode){
root = childNode.child;
} else {
root = none;
}
}
if (root) return root.name||root.val;
else return none;
};
/** Print the tree
*
*/
function print(model,indent, compact) {
var line='',sep;
if (compact) return results(model);
if (indent==undefined) indent=0;
if (!model) return '';
var sp = function () {return Comp.string.create(indent);};
switch (model.type) {
case NODE_TYPES.RESULT:
return sp()+'-> '+model.name+NL;
case NODE_TYPES.FEATURE:
line=sp()+'$'+model.name+'?'+NL;
Comp.array.iter(model.vals,function (v) {
line += print(v,indent+2);
});
return line;
case NODE_TYPES.FEATURE_VALUE:
line=sp()+'='+(model.val.a==undefined?model.val:'['+model.val.a+','+model.val.b+']')+NL;
return line+print(model.child,indent+2);
}
return 'model?';
}
/**
* Computes probability of of a given value existing in a given list
* with additional 2*epsilon interval, only applicable to numerical values.
*/
function probEps(value, list, eps) {
// TODO: ranges
var occurrences = Comp.array.filter(list, function(element) {
return (element >= (value-eps)) && (element <= (value+eps));
});
var numOccurrences = occurrences.length;
var numElements = list.length;
return numOccurrences / numElements;
}
function probEps2(value, list, eps) {
// TODO: ranges
var occurrences = Comp.array.filter(list, function(element) {
return overlap(epsVal(value), epsVal(element));
});
var numOccurrences = occurrences.length;
var numElements = list.length;
return numOccurrences / numElements;
}
/** Incremental update of the model with new training set(s). Can be executed with an empty model.
* The current tree can be week for a new training set (new target).
* This can result in a classification of the new target with insignificant variables.
* Therefore, the last tree node must be exapnded with an additional strong (most significant)
* variable of the new data set (but it is still a heuristic for future updates).
*/
function updateTree(model,data, target, features, options) {
var eps = options.eps,
maxdepth = options.maxdepth,
verbose = options.verbose;
var featuresINm={}, // All current tree feature variables and their value interval
results=[], // All current tree result leafs
set,i,v,feature,remainingFeatures,exists,sigFeature;
// 1. Analysis of existing model
var analyze = function (model,feature) {
var feature2;
if (!model) return;
switch (model.type) {
case NODE_TYPES.RESULT:
if (!Comp.array.contains(results,model.name)) results.push(model.name);
break;
case NODE_TYPES.FEATURE:
feature2={name:model.name};
if (!featuresINm[model.name]) featuresINm[model.name]=feature2;
Comp.array.iter(model.vals,function (v) { analyze(v,featuresINm[model.name]) });
break;
case NODE_TYPES.FEATURE_VALUE:
if (!feature.val) feature.val={
a:(model.val.a==undefined?model.val:model.val.a),
b:(model.val.a==undefined?model.val:model.val.b)
}; else {
feature.val.a=min(feature.val.a,
(model.val.a==undefined?model.val:model.val.a));
feature.val.b=max(feature.val.b,
(model.val.a==undefined?model.val:model.val.b));
}
analyze(model.child);
break;
}
}
analyze(model);
// console.log(featuresINm);
// console.log(results);
exists=Comp.array.contains(results,data[target]);
// 2a. Empty model, add first training set with two significant feature variable nodes
function init(set) {
set=data[i];
sigFeature1=getSignificantFeature(set,features);
remainingFeatures=Comp.array.filter(features,function (feat) {
return sigFeature1.name!=feat;
});
sigFeature2=getSignificantFeature(set,remainingFeatures);
featuresINm[sigFeature1.name]={name:sigFeature1.name,
val:{a:sigFeature1.val-eps,b:sigFeature1.val+eps}};
featuresINm[sigFeature2.name]={name:sigFeature2.name,
val:{a:sigFeature2.val-eps,b:sigFeature2.val+eps}};
results.push(set[target]);
model=Feature(sigFeature1.name,[
Value({a:set[sigFeature1.name]-eps,b:set[sigFeature1.name]+eps},
Feature(sigFeature2.name,[
Value({a:sigFeature2.val-eps,b:sigFeature2.val+eps},
Result(set[target]))
]))]);
return model;
}
remainingFeatures=Comp.array.filter(features,function (feat) {
return !featuresINm[feat];
});
// 2b. Update the tree with the new training set
var update = function (model,set,feature) {
var feature2,p;
if (!model) return;
switch (model.type) {
case NODE_TYPES.RESULT:
if (model.name != set[target] && verbose)
console.log('Cannot insert new training set '+set[target]+' in tree. No more separating variables!');
break;
case NODE_TYPES.FEATURE:
// console.log(set[target]+': '+ model.name+'='+set[model.name]);
if (set[model.name]<(featuresINm[model.name].val.a-eps) ||
set[model.name]>(featuresINm[model.name].val.b+eps)) {
// add new training set; done
// the current decision tree can be week, thus add another strong variable node, too!
sigFeature=getSignificantFeature(set,remainingFeatures);
featuresINm[sigFeature.name]={name:sigFeature.name,
val:{a:sigFeature.val-eps,b:sigFeature.val+eps}};
featuresINm[model.name].val.a=min(featuresINm[model.name].val.a,set[model.name]-eps);
featuresINm[model.name].val.b=max(featuresINm[model.name].val.b,set[model.name]+eps);
if (!Comp.array.contains(results,set[target])) results.push(set[target]);
model.vals.push(Value({a:set[model.name]-eps,b:set[model.name]+eps},
Feature(sigFeature.name,[
Value({a:sigFeature.val-eps,b:sigFeature.val+eps},
Result(set[target]))
])));
model.vals=Comp.array.sort(model.vals,function (v1,v2) {return (lowerBound(v1.val)<lowerBound(v2.val))?-1:1});
} else {
// go deeper, but extend the interval of the best matching child node with new data variable
Comp.array.iter_break(model.vals,function (fv) {
// console.log(model.name,fv.val,overlap(fv.val,{a:set[model.name]-eps,b:set[model.name]+eps}))
if (overlap(fv.val,{a:set[model.name]-eps,b:set[model.name]+eps})) {
fv.val.a=min(lowerBound(fv.val),set[model.name]-eps);
fv.val.b=max(upperBound(fv.val),set[model.name]+eps);
update(fv,set,model.name);
return true;
} else return false;
});
}
break;
case NODE_TYPES.FEATURE_VALUE:
update(model.child,set);
break;
}
}
for (i in data) {
set=data[i];
if (model==undefined || model.type==undefined)
model=init(set);
else
update(model,set);
}
return model;
}
module.exports = {
NODE_TYPES:NODE_TYPES,
compactTree:compactTree,
create:function (options) {
// type options = {data number [][], target:string, features: string [], eps;number, maxdepth}
return createTree(options.data,options.target,options.features,options)
},
depth:depth,
entropy:entropyEps,
evaluate:function evaluate(model,target,samples){},
predict:predict,
print:print,
results:results,
update:function (model,options) {
// type options = {data number [][], target:string, features: string [], eps:number, maxdepth}
return updateTree(model,options.data,options.target,options.features,options)
},
current:function (module) { current=module.current; Aios=module;}
};
};
BundleModuleCode['ml/knn']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Ankit Kuwadekar, Stefan Bosse
** $INITIAL: (C) 2014, Ankit Kuwadekar
** $MODIFIED: (C) 2006-2019 bLAB by sbosse
** $VERSION: 1.2.1
**
** $INFO:
**
** KNN: k-nearest-neighbour Algorithm
** A General purpose k-nearest neighbor classifier algorithm based on the
** k-d tree Javascript library develop by Ubilabs.
**
** Portable models (KNN/KNN2)
**
** $ENDOFINFO
*/
var options = {
version:'1.2.1'
}
var Comp = Require('com/compat');
var math = Require('ml/math');
var euclideanDistance = math.euclidean;
/*
* Original code from:
*
* k-d Tree JavaScript - V 1.01
*
* https://github.com/ubilabs/kd-tree-javascript
*
* @author Mircea Pricop <pricop@ubilabs.net>, 2012
* @author Martin Kleppe <kleppe@ubilabs.net>, 2012
* @author Ubilabs http://ubilabs.net, 2012
* @license MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
function Node(obj, dimension, parent) {
var N = {}
N.obj = obj;
N.left = null;
N.right = null;
N.parent = parent;
N.dimension = dimension;
return N;
}
/* KDTree
*
*/
function KDTree(points, metric) {
// if (!(this instanceof KDTree)) return new KDTree(points, metric);
// If points is not an array, assume we're loading a pre-built tree
var K ={}
if (!Array.isArray(points)) {
K.dimensions = points.dimensions;
K.root = points;
restoreParent(K.root);
} else {
K.dimensions = new Array(points[0].length);
for (var i = 0; i < K.dimensions.length; i++) {
K.dimensions[i] = i;
}
K.root = buildTree(points, 0, null, K.dimensions);
}
K.metric = metric;
return K;
}
// Convert to a JSON serializable structure; this just requires removing
// the `parent` property
KDTree.code = {
nearest : function(K, point, maxNodes, maxDistance) {
var metric = K.metric;
var dimensions = K.dimensions;
var i;
var bestNodes = BinaryHeap(
function (e) {
return -e[1];
}
);
function nearestSearch(node) {
var dimension = dimensions[node.dimension];
var ownDistance = metric(point, node.obj);
var linearPoint = {};
var bestChild,
linearDistance,
otherChild,
i;
function saveNode(node, distance) {
BinaryHeap.code.push(bestNodes,[node, distance]);
if (BinaryHeap.code.size(bestNodes) > maxNodes) {
BinaryHeap.code.pop(bestNodes);
}
}
for (i = 0; i < dimensions.length; i += 1) {
if (i === node.dimension) {
linearPoint[dimensions[i]] = point[dimensions[i]];
} else {
linearPoint[dimensions[i]] = node.obj[dimensions[i]];
}
}
linearDistance = metric(linearPoint, node.obj);
if (node.right === null && node.left === null) {
if (BinaryHeap.code.size(bestNodes) < maxNodes || ownDistance < BinaryHeap.code.peek(bestNodes)[1]) {
saveNode(node, ownDistance);
}
return;
}
if (node.right === null) {
bestChild = node.left;
} else if (node.left === null) {
bestChild = node.right;
} else {
if (point[dimension] < node.obj[dimension]) {
bestChild = node.left;
} else {
bestChild = node.right;
}
}
nearestSearch(bestChild);
if (BinaryHeap.code.size(bestNodes) < maxNodes || ownDistance < BinaryHeap.code.peek(bestNodes)[1]) {
saveNode(node, ownDistance);
}
if (BinaryHeap.code.size(bestNodes) < maxNodes || Math.abs(linearDistance) < BinaryHeap.code.peek(bestNodes)[1]) {
if (bestChild === node.left) {
otherChild = node.right;
} else {
otherChild = node.left;
}
if (otherChild !== null) {
nearestSearch(otherChild);
}
}
}
if (maxDistance) {
for (i = 0; i < maxNodes; i += 1) {
BinaryHeap.code.push(bestNodes,[null, maxDistance]);
}
}
if (K.root) {
nearestSearch(K.root);
}
var result = [];
for (i = 0; i < Math.min(maxNodes, bestNodes.content.length); i += 1) {
if (bestNodes.content[i][0]) {
result.push([bestNodes.content[i][0].obj, bestNodes.content[i][1]]);
}
}
return result;
}
}
function buildTree(points, depth, parent, dimensions) {
var dim = depth % dimensions.length;
if (points.length === 0) {
return null;
}
if (points.length === 1) {
return Node(points[0], dim, parent);
}
points.sort(function (a, b) { a[dimensions[dim]] - b[dimensions[dim]]});
var median = Math.floor(points.length / 2);
var node = Node(points[median], dim, parent);
node.left = buildTree(points.slice(0, median), depth + 1, node, dimensions);
node.right = buildTree(points.slice(median + 1), depth + 1, node, dimensions);
return node;
}
function restoreParent(root) {
if (root.left) {
root.left.parent = root;
restoreParent(root.left);
}
if (root.right) {
root.right.parent = root;
restoreParent(root.right);
}
}
/** BinaryHeap
*
*/
// Binary heap implementation from:
// http://eloquentjavascript.net/appendix2.html
function BinaryHeap (scoreFunction) {
var B={}
//if (!(this instanceof BinaryHeap)) return new BinaryHeap (scoreFunction);
B.content = [];
B.scoreFunction = scoreFunction;
return B;
}
BinaryHeap.code = {
push : function(B,element) {
// Add the new element to the end of the array.
B.content.push(element);
// Allow it to bubble up.
BinaryHeap.code.bubbleUp(B,B.content.length - 1);
},
pop : function(B) {
// Store the first element so we can return it later.
var result = B.content[0];
// Get the element at the end of the array.
var end = B.content.pop();
// If there are any elements left, put the end element at the
// start, and let it sink down.
if (B.content.length > 0) {
B.content[0] = end;
BinaryHeap.code.sinkDown(B,0);
}
return result;
},
peek : function(B) {
return B.content[0];
},
size : function(B) {
return B.content.length;
},
bubbleUp : function(B,n) {
// Fetch the element that has to be moved.
var element = B.content[n];
// When at 0, an element can not go up any further.
while (n > 0) {
// Compute the parent element's index, and fetch it.
var parentN = Math.floor((n + 1) / 2) - 1;
var parent = B.content[parentN];
// Swap the elements if the parent is greater.
if (B.scoreFunction(element) < B.scoreFunction(parent)) {
B.content[parentN] = element;
B.content[n] = parent;
// Update 'n' to continue at the new position.
n = parentN;
} else { // Found a parent that is less, no need to move it further.
break;
}
}
},
sinkDown : function(B,n) {
// Look up the target element and its score.
var length = B.content.length;
var element = B.content[n];
var elemScore = B.scoreFunction(element);
while (true) {
// Compute the indices of the child elements.
var child2N = (n + 1) * 2;
var child1N = child2N - 1;
// This is used to store the new position of the element,
// if any.
var swap = null;
// If the first child exists (is inside the array)...
if (child1N < length) {
// Look it up and compute its score.
var child1 = B.content[child1N];
var child1Score = B.scoreFunction(child1);
// If the score is less than our element's, we need to swap.
if (child1Score < elemScore) {
swap = child1N;
}
}
// Do the same checks for the other child.
if (child2N < length) {
var child2 = B.content[child2N];
var child2Score = B.scoreFunction(child2);
if (child2Score < (swap === null ? elemScore : child1Score)) {
swap = child2N;
}
}
// If the element needs to be moved, swap it, and continue.
if (swap !== null) {
B.content[n] = B.content[swap];
B.content[swap] = element;
n = swap;
} else {
// Otherwise, we are done.
break;
}
}
}
}
/** KNN
*
*/
/**
** typeof @dataset = number [] []
** typeof @labels = number []
** typeof @options = { distance?:function, k?:number }
*/
function KNN(dataset, labels, options) {
var L = {}
if (!options) options={};
if (dataset === true) {
var model = labels;
L.kdTree = KDTree(model.kdTree, options);
L.k = model.k;
L.classes = new Set(model.classes);
L.isEuclidean = model.isEuclidean;
return L;
}
var classes = new Set(labels);
var distance = getDistanceFunction(options.distance),
k = options.k||classes.size + 1;
var points = new Array(dataset.length);
for (var i = 0; i < points.length; ++i) {
points[i] = dataset[i].slice();
}
for (i = 0; i < labels.length; ++i) {
points[i].push(labels[i]);
}
L.kdTree = KDTree(points, distance);
L.k = k;
L.distance = distance;
L.classes = classes;
L.isEuclidean = distance === euclideanDistance;
return L;
}
/**
* Predicts the output given the matrix to predict.
* @param {Array} dataset
* @return {Array} predictions
*/
KNN.code = {
predict : function(L,dataset) {
if (Array.isArray(dataset)) {
if (typeof dataset[0] === 'number') {
return getSinglePrediction(L, dataset);
} else if (Array.isArray(dataset[0]) && typeof dataset[0][0] === 'number') {
var predictions = new Array(dataset.length);
for (var i = 0; i < dataset.length; i++) {
predictions[i] = getSinglePrediction(L, dataset[i]);
}
return predictions;
}
}
throw new TypeError('dataset to predict must be an array or a matrix');
}
}
function getSinglePrediction(knn, currentCase) {
var nearestPoints = KDTree.code.nearest(knn.kdTree, currentCase, knn.k);
var pointsPerClass = {};
var predictedClassMin = null;
var predictedClassMax = null;
var predictedClassDistance = 0;
var maxPoints = -1;
var minDistance = 1E30;
var lastElement = nearestPoints[0][0].length - 1;
//for (var element of knn.classes) {
// pointsPerClass[element] = 0;
//}
forof(knn.classes,function (element) {
pointsPerClass[element] = 0;
});
for (var i = 0; i < nearestPoints.length; ++i) {
var currentClass = nearestPoints[i][0][lastElement];
var currentPoints = ++pointsPerClass[currentClass];
// Either use majority of points matching a class or the nearest points
if (currentPoints > maxPoints) {
predictedClassMax = currentClass;
predictedClassDistance = predictedClassDistance+nearestPoints[i][1];
maxPoints = currentPoints;
}
if (nearestPoints[i][1] < minDistance) {
predictedClassMin = currentClass;
minDistance = nearestPoints[i][1];
}
}
predictedClassDistance /= maxPoints;
return maxPoints>2?predictedClassMax:predictedClassMin;
}
/** Create a simple KNN (2)
*
* typeof @options = {x:number [] [],y: number []}
*
*/
var KNN2 = function (options) {
var model={}
// if (!(this instanceof KNN2)) return new KNN2(options);
model.x = options.x;
model.y = options.y;
model.target = options.y;
model.k = options.k || 3
model.distance = getDistanceFunction(options.distance);
model.weightf = getWeightedFunction(options.weightf);
return model
}
/** Make a prediction
*
*/
KNN2.code = {
predict : function (model,data) {
var x = data;
var k = model.k;
var weightf = model.weightf;
var distance = model.distance;
var distanceList = [];
var i;
for(i=0; i<model.x.length; i++)
distanceList.push([distance(x,model.x[i]),i]);
distanceList.sort(function(a,b) {return a[0]-b[0];});
var avg = 0.0;
var totalWeight = 0, weight;
for(i=0; i<k; i++) {
var dist = distanceList[i][0];
var idx = distanceList[i][1];
weight = weightf(dist);
avg += weight * model.y[idx];
totalWeight += weight;
}
avg /= totalWeight;
return avg;
}
}
function getWeightedFunction(options) {
if(typeof options === 'undefined') {
return function(x) {
var sigma = 10.0;
return Math.exp(-1.*x*x/(2*sigma*sigma));
}
} else if(typeof options === 'function') {
return options;
} else if(options === 'gaussian') {
return function(x) {
var sigma = options.sigma;
return Math.exp(-1.*x*x/(2*sigma*sigma));
}
} else if(options === 'none') {
return function(dist) {
return 1.0;
}
}
}
function getDistanceFunction(options) {
if(typeof options === 'undefined') {
return math.euclidean;
} else if (typeof options === 'function') {
return options;
} else if (options === 'euclidean') {
return math.euclidean;
} else if (options === 'pearson') {
return math.pearson;
} else
throw new TypeError('distance opions invalid: '+options);;
}
module.exports={
create : KNN,
predict : KNN.code.predict,
create2 : KNN2,
predict2 : KNN2.code.predict,
}
};
BundleModuleCode['ml/math']=function (module,exports){
/**
* Created by joonkukang on 2014. 1. 12..
*/
var m = module.exports;
m.randn = function() {
// generate random guassian distribution number. (mean : 0, standard deviation : 1)
var v1, v2, s;
do {
v1 = 2 * Math.random() - 1; // -1.0 ~ 1.0 까지의 값
v2 = 2 * Math.random() - 1; // -1.0 ~ 1.0 까지의 값
s = v1 * v1 + v2 * v2;
} while (s >= 1 || s == 0);
s = Math.sqrt( (-2 * Math.log(s)) / s );
return v1 * s;
}
m.shape = function(mat) {
var row = mat.length;
var col = mat[0].length;
return [row,col];
};
m.addVec = function(vec1, vec2) {
if(vec1.length === vec2.length) {
var result = [];
var i;
for(i=0;i<vec1.length;i++)
result.push(vec1[i]+vec2[i]);
return result;
} else {
throw new Error("Length Error : not same.")
}
}
m.minusVec = function(vec1,vec2) {
if(vec1.length === vec2.length) {
var result = [];
var i;
for(i=0;i<vec1.length;i++)
result.push(vec1[i]-vec2[i]);
return result;
} else {
throw new Error("Length Error : not same.")
}
};
m.addMatScalar = function(mat,scalar) {
var row = m.shape(mat)[0];
var col = m.shape(mat)[1];
var i , j,result = [];
for(i=0 ; i<row ; i++) {
var rowVec = [];
for(j=0 ; j<col ; j++) {
rowVec.push(mat[i][j] + scalar);
}
result.push(rowVec);
}
return result;
}
m.addMatVec = function(mat,vec) {
if(mat[0].length === vec.length) {
var result = [];
var i;
for(i=0;i<mat.length;i++)
result.push(m.addVec(mat[i],vec));
return result;
} else {
throw new Error("Length Error : not same.")
}
}
m.minusMatVec = function(mat,vec) {
if(mat[0].length === vec.length) {
var result = [];
var i;
for(i=0;i<mat.length;i++)
result.push(m.minusVec(mat[i],vec));
return result;
} else {
throw new Error("Length Error : not same.")
}
}
m.addMat = function (mat1, mat2) {
if ((mat1.length === mat2.length) && (mat1[0].length === mat2[0].length)) {
var result = new Array(mat1.length);
for (var i = 0; i < mat1.length; i++) {
result[i] = new Array(mat1[i].length);
for (var j = 0; j < mat1[i].length; j++) {
result[i][j] = mat1[i][j] + mat2[i][j];
}
}
return result;
} else {
throw new Error('Matrix mismatch.');
}
};
m.minusMat = function(mat1, mat2) {
if ((mat1.length === mat2.length) && (mat1[0].length === mat2[0].length)) {
var result = new Array(mat1.length);
for (var i = 0; i < mat1.length; i++) {
result[i] = new Array(mat1[i].length);
for (var j = 0; j < mat1[i].length; j++) {
result[i][j] = mat1[i][j] - mat2[i][j];
}
}
return result;
} else {
throw new Error('Matrix mismatch.');
}
}
m.transpose = function (mat) {
var result = new Array(mat[0].length);
for (var i = 0; i < mat[0].length; i++) {
result[i] = new Array(mat.length);
for (var j = 0; j < mat.length; j++) {
result[i][j] = mat[j][i];
}
}
return result;
};
m.dotVec = function (vec1, vec2) {
if (vec1.length === vec2.length) {
var result = 0;
for (var i = 0; i < vec1.length; i++) {
result += vec1[i] * vec2[i];
}
return result;
} else {
throw new Error("Vector mismatch");
}
};
m.outerVec = function (vec1,vec2) {
var mat1 = m.transpose([vec1]);
var mat2 = [vec2];
return m.mulMat(mat1,mat2);
};
m.mulVecScalar = function(vec,scalar) {
var i, result = [];
for(i=0;i<vec.length;i++)
result.push(vec[i]*scalar);
return result;
};
m.mulMatScalar = function(mat,scalar) {
var row = m.shape(mat)[0];
var col = m.shape(mat)[1];
var i , j,result = [];
for(i=0 ; i<row ; i++) {
var rowVec = [];
for(j=0 ; j<col ; j++) {
rowVec.push(mat[i][j] * scalar);
}
result.push(rowVec);
}
return result;
};
m.mulMatElementWise = function(mat1, mat2) {
if (mat1.length === mat2.length && mat1[0].length === mat2[0].length) {
var result = new Array(mat1.length);
for (var x = 0; x < mat1.length; x++) {
result[x] = new Array(mat1[0].length);
}
for (var i = 0; i < result.length; i++) {
for (var j = 0; j < result[i].length; j++) {
result[i][j] = mat1[i][j] * mat2[i][j]
}
}
return result;
} else {
throw new Error("Matrix shape error : not same");
}
};
m.mulMat = function (mat1, mat2) {
if (mat1[0].length === mat2.length) {
var result = new Array(mat1.length);
for (var x = 0; x < mat1.length; x++) {
result[x] = new Array(mat2[0].length);
}
var mat2_T = m.transpose(mat2);
for (var i = 0; i < result.length; i++) {
for (var j = 0; j < result[i].length; j++) {
result[i][j] = m.dotVec(mat1[i],mat2_T[j]);
}
}
return result;
} else {
throw new Error("Array mismatch");
}
};
m.sumVec = function(vec) {
var sum = 0;
var i = vec.length;
while (i--) {
sum += vec[i];
}
return sum;
};
m.sumMat = function(mat) {
var sum = 0;
var i = mat.length;
while (i--) {
for(var j=0;j<mat[0].length;j++)
sum += mat[i][j];
}
return sum;
};
m.sumMatAxis = function(mat,axis) {
// default axis 0;
// axis 0 : mean of col vector . axis 1 : mean of row vector
if(axis === 1) {
var row = m.shape(mat)[0];
var i ;
var result = [];
for(i=0 ; i<row; i++)
result.push(m.sumVec(mat[i]));
return result;
} else {
mat_T = m.transpose(mat);
return m.sumMatAxis(mat_T,1);
}
};
m.meanVec = function(vec) {
return 1. * m.sumVec(vec) / vec.length;
};
m.meanMat = function(mat) {
var row = mat.length;
var col = mat[0].length;
return 1. * m.sumMat(mat) / (row * col);
};
m.meanMatAxis = function(mat,axis) {
// default axis 0;
// axis 0 : mean of col vector . axis 1 : mean of row vector
if(axis === 1) {
var row = m.shape(mat)[0];
var i ;
var result = [];
for(i=0 ; i<row; i++)
result.push(m.meanVec(mat[i]));
return result;
} else {
mat_T = m.transpose(mat);
return m.meanMatAxis(mat_T,1);
}
};
m.squareVec = function(vec) {
var squareVec = [];
var i;
for(i=0;i<vec.length;i++) {
squareVec.push(vec[i]*vec[i]);
}
return squareVec;
};
m.squareMat = function(mat) {
var squareMat = [];
var i;
for(i=0;i<mat.length;i++) {
squareMat.push(m.squareVec(mat[i]));
}
return squareMat;
};
m.minVec = function(vec) {
var min = vec[0];
var i = vec.length;
while (i--) {
if (vec[i] < min)
min = vec[i];
}
return min;
};
m.maxVec = function(vec) {
var max = vec[0];
var i = vec.length;
while (i--) {
if (vec[i] > max)
max = vec[i];
}
return max;
}
m.minMat = function(mat) {
var min = mat[0][0];
var i = mat.length;
while (i--) {
for(var j=0;j<mat[0].length;j++) {
if(mat[i][j] < min)
min = mat[i][j];
}
}
return min;
};
m.maxMat = function(mat) {
var max = mat[0][0];
var i = mat.length;
while (i--) {
for(var j=0;j<mat[0].length;j++) {
if(mat[i][j] < max)
max = mat[i][j];
}
}
return max;
};
m.zeroVec = function(n) {
var vec = [];
while(vec.length < n)
vec.push(0);
return vec;
};
m.zeroMat = function(row,col) {
var mat = [];
while(mat.length < row)
mat.push(m.zeroVec(col));
return mat;
};
m.oneVec = function(n) {
var vec = [];
while(vec.length < n)
vec.push(1);
return vec;
};
m.oneMat = function(row,col) {
var mat = [];
while(mat.length < row)
mat.push(m.oneVec(col));
return mat;
};
m.randVec = function(n,lower,upper) {
lower = (typeof lower !== 'undefined') ? lower : 0;
upper = (typeof upper !== 'undefined') ? upper : 1;
var vec = [];
while(vec.length < n)
vec.push(lower + (upper-lower) * Math.random());
return vec;
};
m.randMat = function(row,col,lower,upper) {
lower = (typeof lower !== 'undefined') ? lower : 0;
upper = (typeof upper !== 'undefined') ? upper : 1;
var mat = [];
while(mat.length < row)
mat.push(m.randVec(col,lower,upper));
return mat;
};
m.randnVec = function(n,mean,sigma) {
var vec = [];
while(vec.length < n)
vec.push(mean+sigma* m.randn());
return vec;
};
m.randnMat = function(row,col,mean,sigma) {
var mat = [];
while(mat.length < row)
mat.push(m.randnVec(col,mean,sigma));
return mat;
};
m.identity = function (n) {
var result = new Array(n);
for (var i = 0; i < n ; i++) {
result[i] = new Array(n);
for (var j = 0; j < n; j++) {
result[i][j] = (i === j) ? 1 : 0;
}
}
return result;
};
m.sigmoid = function(x) {
var sigmoid = (1. / (1 + Math.exp(-x)))
if(sigmoid ==1) {
// console.warn("Something Wrong!! Sigmoid Function returns 1. Probably javascript float precision problem?\nSlightly Controlled value to 1 - 1e-14")
sigmoid = 0.99999999999999; // Javascript Float Precision Problem.. This is a limit of javascript.
} else if(sigmoid ==0) {
// console.warn("Something Wrong!! Sigmoid Function returns 0. Probably javascript float precision problem?\nSlightly Controlled value to 1e-14")
sigmoid = 1e-14;
}
return sigmoid; // sigmoid cannot be 0 or 1;;
};
m.dSigmoid = function(x){
a = m.sigmoid(x);
return a * (1. - a);
};
m.probToBinaryMat = function(mat) {
var row = m.shape(mat)[0];
var col = m.shape(mat)[1];
var i,j;
var result = [];
for(i=0;i<row;i++) {
var rowVec = [];
for(j=0;j<col;j++) {
if(Math.random() < mat[i][j])
rowVec.push(1);
else
rowVec.push(0);
}
result.push(rowVec);
}
return result;
};
m.activateVec = function(vec,activation) {
var i, result = [];
for(i=0;i<vec.length;i++)
result.push(activation(vec[i]));
return result;
};
m.activateMat = function(mat,activation) {
var row = m.shape(mat)[0];
var col = m.shape(mat)[1];
var i, j,result = [];
for(i=0;i<row;i++) {
var rowVec = [];
for(j=0;j<col;j++)
rowVec.push(activation(mat[i][j]));
result.push(rowVec);
}
return result;
};
m.activateTwoVec = function(vec1, vec2,activation) {
if (vec1.length === vec2.length) {
var result = new Array(vec1.length);
for (var i = 0; i < result.length; i++) {
result[i] = activation(vec1[i],vec2[i]);
}
return result;
} else {
throw new Error("Matrix shape error : not same");
}
};
m.activateTwoMat = function(mat1, mat2,activation) {
if (mat1.length === mat2.length && mat1[0].length === mat2[0].length) {
var result = new Array(mat1.length);
for (var x = 0; x < mat1.length; x++) {
result[x] = new Array(mat1[0].length);
}
for (var i = 0; i < result.length; i++) {
for (var j = 0; j < result[i].length; j++) {
result[i][j] = activation(mat1[i][j],mat2[i][j]);
}
}
return result;
} else {
throw new Error("Matrix shape error : not same");
}
};
m.fillVec = function(n,value) {
var vec = [];
while(vec.length < n)
vec.push(value);
return vec;
};
m.fillMat = function(row,col,value) {
var mat = [];
while(mat.length < row) {
var rowVec = [];
while(rowVec.length < col)
rowVec.push(value);
mat.push(rowVec);
}
return mat;
};
m.softmaxVec = function(vec) {
var max = m.maxVec(vec);
var preSoftmaxVec = m.activateVec(vec,function(x) {return Math.exp(x - max);})
return m.activateVec(preSoftmaxVec,function(x) {return x/ m.sumVec(preSoftmaxVec)})
};
m.softmaxMat = function(mat) {
var result=[], i;
for(i=0 ; i<mat.length ; i++)
result.push(m.softmaxVec(mat[i]));
return result;
};
m.randInt = function(min,max) {
var rand = Math.random() * (max - min + 0.9999) + min
return Math.floor(rand);
}
m.normalizeVec = function(vec) {
var i;
var newVec = [],tot = 0;
for(i=0; i<vec.length; i++)
tot += vec[i];
for(i=0; i<vec.length;i++)
newVec.push(1.*vec[i]/tot);
return newVec;
};
m.euclidean = function(x1,x2) {
var i;
var distance = 0;
for(i=0 ; i<x1.length; i++) {
var dx = x1[i] - x2[i];
distance += dx * dx;
}
return Math.sqrt(distance);
};
m.pearson = function(x, y)
{
var xy = [];
var x2 = [];
var y2 = [];
for(var i=0; i<x.length; i++)
{
xy.push(x[i] * y[i]);
x2.push(x[i] * x[i]);
y2.push(y[i] * y[i]);
}
var sum_x = 0;
var sum_y = 0;
var sum_xy = 0;
var sum_x2 = 0;
var sum_y2 = 0;
for(var i=0; i<x.length; i++)
{
sum_x += x[i];
sum_y += y[i];
sum_xy += xy[i];
sum_x2 += x2[i];
sum_y2 += y2[i];
}
var step1 = (x.length * sum_xy) - (sum_x * sum_y);
var step2 = (x.length * sum_x2) - (sum_x * sum_x);
var step3 = (x.length * sum_y2) - (sum_y * sum_y);
var step4 = Math.sqrt(step2 * step3);
var answer = step1 / step4;
return answer;
};
m.getNormVec = function(vec) {
var i;
var sqsum = 0;
for(i=0; i<vec.length; i++)
sqsum += vec[i] * vec[i];
return Math.sqrt(sqsum);
}
m.gaussian = function(x, sigma) {
sigma = sigma || 10.0;
return Math.exp(-1.*x*x/(2*sigma*sigma));
}
m.meanVecs = function(vecs) {
var sum = m.zeroVec(vecs[0].length);
var i;
for(i=0; i<vecs.length; i++)
sum = m.addVec(sum,vecs[i]);
return m.activateVec(sum,function(x) {return 1.*x/vecs.length;});
};
m.covarianceVecs = function(vecs) {
var mat = m.zeroMat(vecs[0].length,vecs[0].length);
var meanVec = m.meanVecs(vecs);
var i;
for(i=0; i<vecs.length; i++) {
var a = m.minusVec(vecs[i],meanVec);
mat = m.addMat(mat, m.mulMat(m.transpose([a]),[a]));
}
return m.activateMat(mat,function(x) { return 1.*x/(vecs.length-1);});
};
m.shuffle = function(arr){
var o = [];
for(var i=0;i<arr.length;i++)
o.push(arr[i]); // deep copy
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
m.range = function(start, end, step) {
var ret = [];
if(typeof step === "undefined")
step = 1;
if(typeof end === "undefined") {
end = start;
start = 0;
}
for(var i=start;i<end;i+=step)
ret.push(i);
return ret;
};
// For CRBM
/*
m.phi = function(mat,vec,low,high) {
var i;
var result = [];
for(i=0;i<mat.length;i++) {
result.push(m.activateTwoVec(mat[i],vec,function(x,y){return low+(high-low)* m.sigmoid(x*y);}))
}
return result;
}
*/
};
BundleModuleCode['ml/kmeans']=function (module,exports){
/**
* Created by joonkukang on 2014. 1. 16..
*/
var math = Require('ml/math')
var Kmeans = module.exports;
Kmeans.cluster = function(options) {
var data = options['data'];
var k = options['k'];
var distance = getDistanceFunction(options['distance']);
var epochs = options['epochs'];
var init_using_data = options['init_using_data'];
if(typeof init_using_data === "undefined");
init_using_data = true;
var means = getRandomMeans(data,k, init_using_data);
var epoch, i, j, l;
var clusters = [];
for(i=0 ; i<k ; i++)
clusters.push([]);
for(epoch=0 ; epoch<epochs ; epoch++) {
clusters = [];
for(i=0 ; i<k ; i++)
clusters.push([]);
// Find which centroid is the closest for each row
for(i=0 ; i<data.length ; i++) {
var bestmatch = 0;
for(j=0 ; j<k ; j++) {
if(distance(means[j],data[i]) < distance(means[bestmatch],data[i])) bestmatch = j;
}
clusters[bestmatch].push(i);
}
// Move the centroids to the average of their members
for(i=0 ; i<k ; i++) {
var avgs = [];
for(j=0 ; j<data[0].length ; j++)
avgs.push(0.0);
if(clusters[i].length > 0) {
for(j=0 ; j<clusters[i].length ; j++) {
for(l=0 ; l<data[0].length ; l++) {
avgs[l] += data[clusters[i][j]][l];
}
}
for(j=0 ; j<data[0].length ; j++) {
avgs[j] /= clusters[i].length;
}
means[i] = avgs;
}
}
}
return {
clusters : clusters,
means : means
};
}
var getRandomMeans = function(data,k, init_using_data) {
var clusters = [];
if(init_using_data) {
var cluster_index = math.range(data.length);
cluster_index = math.shuffle(cluster_index);
for(i=0 ; i<k ; i++) {
clusters.push(data[cluster_index[i]]);
}
} else {
var i,j;
var ranges = [];
for(i=0 ; i<data[0].length ; i++) {
var min = data[0][i] , max = data[0][i];
for(j=0 ; j<data.length ; j++) {
if(data[j][i] < min) min = data[j][i];
if(data[j][i] > max) max = data[j][i];
}
ranges.push([min,max]);
}
for(i=0 ; i<k ; i++) {
var cluster = [];
for(j=0 ; j<data[0].length;j++) {
cluster.push(Math.random() * (ranges[j][1] - ranges[j][0]) + ranges[j][0]);
}
clusters.push(cluster);
}
}
return clusters;
}
function getDistanceFunction(options) {
if(typeof options === 'undefined') {
return math.euclidean;
} else if (typeof options === 'function') {
return options;
} else if (options['type'] === 'euclidean') {
return math.euclidean;
} else if (options['type'] === 'pearson') {
return math.pearson;
}
}
};
BundleModuleCode['ml/svm']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: joonkukang, Stefan Bosse
** $INITIAL: (C) 2014, joonkukang
** $MODIFIED: (C) 2006-2018 bLAB by sbosse
** $VERSION: 1.1.3
**
** $INFO:
**
** Support Vector Machine Algrotihm
**
** 1. References : http://cs229.stanford.edu/materials/smo.pdf . simplified smo algorithm
** 2. https://github.com/karpathy/svmjs
**
** Portable model
**
** $ENDOFINFO
*/
var math = Require('ml/math');
/**
* type options = {x: number [] [], y: number []}
*/
var SVM = function (options) {
var L = {};
L.x = options.x;
L.y = options.y;
return L
};
SVM.code = {
train : function (L,options) {
var self = L;
var C = options.C || 1.0;
var tol = options.tol || 1e-4;
var maxPasses = options.max_passes || 20;
var alphatol = options.alpha_tol || 1e-5;
L.options={kernel:options.kernel,iterations:maxPasses,alpha_tol:alphatol, C:C, tol:tol };
self.kernel = getKernel(options.kernel);
self.alphas = math.zeroVec(self.x.length);
self.b = 0;
var passes = 0, i;
var count=0;
while(passes < maxPasses) {
var numChangedAlphas = 0;
for(i=0; i<self.x.length; i++) {
var E_i = SVM.code.f(self,self.x[i]) - self.y[i];
if((self.y[i] * E_i < -tol && self.alphas[i] < C) || (self.y[i] * E_i > tol && self.alphas[i] >0)) {
// Randomly selects j (i != j)
var j = math.randInt(0,self.x.length-1);
if(i==j) j = (j+1) % self.x.length;
var E_j = SVM.code.f(self,self.x[j]) - self.y[j];
var alpha_i_old = self.alphas[i], alpha_j_old = self.alphas[j];
// Compute L,H
var L,H;
if(self.y[i] !== self.y[j]) {
L = Math.max(0, self.alphas[j] - self.alphas[i]);
H = Math.min(C, C + self.alphas[j] - self.alphas[i]);
} else {
L = Math.max(0, self.alphas[j] + self.alphas[i] - C);
H = Math.min(C, self.alphas[j] + self.alphas[i]);
}
if(L === H)
continue;
// Compute ETA
var ETA = 2 * self.kernel(self.x[i],self.x[j]) - self.kernel(self.x[i],self.x[i]) - self.kernel(self.x[j],self.x[j]);
if(ETA >= 0)
continue;
// Clip new value to alpha_j
self.alphas[j] -= 1.*self.y[j] * (E_i - E_j) / ETA;
if(self.alphas[j] > H)
self.alphas[j] = H;
else if(self.alphas[j] < L)
self.alphas[j] = L;
if(Math.abs(self.alphas[j] - alpha_j_old) < alphatol)
continue;
// Clip new value to alpha_i
self.alphas[i] += self.y[i] * self.y[j] * (alpha_j_old - self.alphas[j]);
// update b
var b1 = self.b - E_i - self.y[i] * (self.alphas[i] - alpha_i_old) * self.kernel(self.x[i],self.x[i])
- self.y[j] * (self.alphas[j] - alpha_j_old) * self.kernel(self.x[i],self.x[j]);
var b2 = self.b - E_j - self.y[i] * (self.alphas[i] - alpha_i_old) * self.kernel(self.x[i],self.x[j])
- self.y[j] * (self.alphas[j] - alpha_j_old) * self.kernel(self.x[j],self.x[j]);
if(0 < self.alphas[i] && self.alphas[i] < C)
self.b = b1;
else if(0 < self.alphas[j] && self.alphas[j] < C)
self.b = b2;
else
self.b = (b1+b2)/2.0;
numChangedAlphas ++ ;
} // end-if
} // end-for
if(numChangedAlphas == 0)
passes++;
else
passes = 0;
}
},
predict : function(L,x) {
var self = L;
this.kernel = getKernel(L.options.kernel); // update kernel
if(SVM.code.f(L,x) >= 0)
return 1;
else
return -1;
},
f : function(L,x) {
var self = L;
var f = 0, j;
for(j=0; j<self.x.length; j++)
f += self.alphas[j] * self.y[j] * self.kernel(self.x[j],x);
f += self.b;
return f;
}
}
function getKernel (options) {
if(typeof options === 'undefined') {
return function(x,y) {
var sigma = 1.0;
return Math.exp(-1.*Math.pow(math.getNormVec(math.minusVec(x,y)),2)/(2*sigma*sigma));
}
} else if (typeof options === 'function') {
return options;
} else if (options['type'] === 'gaussian') {
return function(x,y) {
var sigma = options['sigma'];
return Math.exp(-1.*Math.pow(math.getNormVec(math.minusVec(x,y)),2)/(2*sigma*sigma));
}
} else if (options['type'] === 'linear') {
return function(x,y) {
return math.dotVec(x,y);
}
} else if (options['type'] === 'polynomial') {
return function(x,y) {
var c = options['c'];
var d = options['d'];
return Math.pow(math.dotVec(x,y) + c, d);
}
} else if (options['type'] === 'rbf') {
return function(v1, v2) {
var s=0;
var sigma = options.sigma||options.rbfsigma || 0.5;
for(var q=0;q<v1.length;q++) { s += (v1[q] - v2[q])*(v1[q] - v2[q]); }
return Math.exp(-s/(2.0*sigma*sigma));
}
}
}
var SVM2 = function (options) {
var L = {};
L.data = options.x;
L.labels = options.y;
L.threshold=checkOption(options.threshold,0);
return L
};
SVM2.code = {
// data is NxD array of floats. labels are 1 or -1.
train: function(L, options) {
var data = L.data,labels=L.labels;
// parameters
options = options || {};
var C = options.C || 1.0; // C value. Decrease for more regularization
var tol = options.tol || 1e-4; // numerical tolerance. Don't touch unless you're pro
var alphatol = options.alphatol || options.alpha_tol || 1e-7; // non-support vectors for space and time efficiency are truncated. To guarantee correct result set this to 0 to do no truncating. If you want to increase efficiency, experiment with setting this little higher, up to maybe 1e-4 or so.
var maxiter = options.maxiter || 10000; // max number of iterations
var numpasses = options.numpasses || options.max_passes || 10; // how many passes over data with no change before we halt? Increase for more precision.
// instantiate kernel according to options. kernel can be given as string or as a custom function
var kernel = linearKernel;
L.kernelType = "linear";
L.options={kernel:options.kernel};
if("kernel" in options) {
if (typeof options.kernel == 'object') {
kernel = getKernel(options.kernel);
L.kernelType=options.kernel.type;
L.rbfSigma = options.kernel.sigma || options.kernel.rbfsigma;
} else if (typeof options.kernel == 'function') {
// assume kernel was specified as a function. Let's just use it
L.kernelType = "custom";
kernel = options.kernel;
}
}
L.options.C=C;
L.options.tol=tol;
L.options.alphatol=alphatol;
L.options.iterations=numpasses;
// initializations
L.kernel = kernel;
L.N = data.length; var N = L.N;
L.D = data[0].length; var D = L.D;
L.alpha = zeros(N);
L.b = 0.0;
L.usew_ = false; // internal efficiency flag
// Cache kernel computations to avoid expensive recomputation.
// This could use too much memory if N is large.
if (options.memoize) {
L.kernelResults = new Array(N);
for (var i=0;i<N;i++) {
L.kernelResults[i] = new Array(N);
for (var j=0;j<N;j++) {
L.kernelResults[i][j] = kernel(data[i],data[j]);
}
}
}
// run SMO algorithm
var iter = 0;
var passes = 0;
while(passes < numpasses && iter < maxiter) {
var alphaChanged = 0;
for(var i=0;i<N;i++) {
var Ei= SVM2.code.marginOne(L, data[i]) - labels[i];
if( (labels[i]*Ei < -tol && L.alpha[i] < C)
|| (labels[i]*Ei > tol && L.alpha[i] > 0) ){
// alpha_i needs updating! Pick a j to update it with
var j = i;
while(j === i) j= randi(0, L.N);
var Ej= SVM2.code.marginOne(L, data[j]) - labels[j];
// calculate L and H bounds for j to ensure we're in [0 C]x[0 C] box
ai= L.alpha[i];
aj= L.alpha[j];
var Lb = 0; var Hb = C;
if(labels[i] === labels[j]) {
Lb = Math.max(0, ai+aj-C);
Hb = Math.min(C, ai+aj);
} else {
Lb = Math.max(0, aj-ai);
Hb = Math.min(C, C+aj-ai);
}
if(Math.abs(Lb - Hb) < 1e-4) continue;
var eta = 2*SVM2.code.kernelResult(L, i,j) - SVM2.code.kernelResult(L, i,i) - SVM2.code.kernelResult(L, j,j);
if(eta >= 0) continue;
// compute new alpha_j and clip it inside [0 C]x[0 C] box
// then compute alpha_i based on it.
var newaj = aj - labels[j]*(Ei-Ej) / eta;
if(newaj>Hb) newaj = Hb;
if(newaj<Lb) newaj = Lb;
if(Math.abs(aj - newaj) < 1e-4) continue;
L.alpha[j] = newaj;
var newai = ai + labels[i]*labels[j]*(aj - newaj);
L.alpha[i] = newai;
// update the bias term
var b1 = L.b - Ei - labels[i]*(newai-ai)*SVM2.code.kernelResult(L, i,i)
- labels[j]*(newaj-aj)*SVM2.code.kernelResult(L, i,j);
var b2 = L.b - Ej - labels[i]*(newai-ai)*SVM2.code.kernelResult(L, i,j)
- labels[j]*(newaj-aj)*SVM2.code.kernelResult(L, j,j);
L.b = 0.5*(b1+b2);
if(newai > 0 && newai < C) L.b= b1;
if(newaj > 0 && newaj < C) L.b= b2;
alphaChanged++;
} // end alpha_i needed updating
} // end for i=1..N
iter++;
//console.log("iter number %d, alphaChanged = %d", iter, alphaChanged);
if(alphaChanged == 0) passes++;
else passes= 0;
} // end outer loop
// if the user was using a linear kernel, lets also compute and store the
// weights. This will speed up evaluations during testing time
if(L.kernelType === "linear") {
// compute weights and store them
L.w = new Array(L.D);
for(var j=0;j<L.D;j++) {
var s= 0.0;
for(var i=0;i<L.N;i++) {
s+= L.alpha[i] * labels[i] * data[i][j];
}
L.w[j] = s;
L.usew_ = true;
}
} else {
// okay, we need to retain all the support vectors in the training data,
// we can't just get away with computing the weights and throwing it out
// But! We only need to store the support vectors for evaluation of testing
// instances. So filter here based on L.alpha[i]. The training data
// for which L.alpha[i] = 0 is irrelevant for future.
var newdata = [];
var newlabels = [];
var newalpha = [];
for(var i=0;i<L.N;i++) {
//console.log("alpha=%f", L.alpha[i]);
if(L.alpha[i] > alphatol) {
newdata.push(L.data[i]);
newlabels.push(L.labels[i]);
newalpha.push(L.alpha[i]);
}
}
// store data and labels
L.data = newdata;
L.labels = newlabels;
L.alpha = newalpha;
L.N = L.data.length;
// console.log("filtered training data from %d to %d support vectors.", data.length, L.data.length);
}
var trainstats = {};
trainstats.iters= iter;
trainstats.passes= passes;
return trainstats;
},
// inst is an array of length D. Returns margin of given example
// this is the core prediction function. All others are for convenience mostly
// and end up calling this one somehow.
marginOne: function(L,inst) {
var f = L.b;
// if the linear kernel was used and w was computed and stored,
// (i.e. the svm has fully finished training)
// the internal class variable usew_ will be set to true.
if(L.usew_) {
// we can speed this up a lot by using the computed weights
// we computed these during train(). This is significantly faster
// than the version below
for(var j=0;j<L.D;j++) {
f += inst[j] * L.w[j];
}
} else {
for(var i=0;i<L.N;i++) {
f += L.alpha[i] * L.labels[i] * L.kernel(inst, L.data[i]);
}
}
return f;
},
predict: function(L,inst) {
L.kernel=getKernel(L.options.kernel); // update kernel
var result = SVM2.code.marginOne(L,inst);
if (L.threshold===false) return result;
else return result > L.threshold ? 1 : -1;
},
// data is an NxD array. Returns array of margins.
margins: function(L,data) {
// go over support vectors and accumulate the prediction.
var N = data.length;
var margins = new Array(N);
for(var i=0;i<N;i++) {
margins[i] = SVM2.code.marginOne(L,data[i]);
}
return margins;
},
kernelResult: function(L, i, j) {
if (L.kernelResults) {
return L.kernelResults[i][j];
}
return L.kernel(L.data[i], L.data[j]);
},
// data is NxD array. Returns array of 1 or -1, predictions
predictN: function(L,data) {
L.kernel=getKernel(L.options.kernel); // update kernel
var margs = SVM2.code.margins(L, data);
for(var i=0;i<margs.length;i++) {
if (L.threshold!=false)
margs[i] = margs[i] > L.threshold ? 1 : -1;
}
return margs;
},
// THIS FUNCTION IS NOW DEPRECATED. WORKS FINE BUT NO NEED TO USE ANYMORE.
// LEAVING IT HERE JUST FOR BACKWARDS COMPATIBILITY FOR A WHILE.
// if we trained a linear svm, it is possible to calculate just the weights and the offset
// prediction is then yhat = sign(X * w + b)
getWeights: function(L) {
// DEPRECATED
var w= new Array(L.D);
for(var j=0;j<L.D;j++) {
var s= 0.0;
for(var i=0;i<L.N;i++) {
s+= L.alpha[i] * L.labels[i] * L.data[i][j];
}
w[j]= s;
}
return {w: w, b: L.b};
},
toJSON: function(L) {
if(L.kernelType === "custom") {
console.log("Can't save this SVM because it's using custom, unsupported kernel...");
return {};
}
json = {}
json.N = L.N;
json.D = L.D;
json.b = L.b;
json.kernelType = L.kernelType;
if(L.kernelType === "linear") {
// just back up the weights
json.w = L.w;
}
if(L.kernelType === "rbf") {
// we need to store the support vectors and the sigma
json.rbfSigma = L.rbfSigma;
json.data = L.data;
json.labels = L.labels;
json.alpha = L.alpha;
}
return json;
},
fromJSON: function(L,json) {
this.N = json.N;
this.D = json.D;
this.b = json.b;
this.kernelType = json.kernelType;
if(this.kernelType === "linear") {
// load the weights!
this.w = json.w;
this.usew_ = true;
this.kernel = linearKernel; // this shouldn't be necessary
}
else if(this.kernelType == "rbf") {
// initialize the kernel
this.rbfSigma = json.rbfSigma;
this.kernel = makeRbfKernel(this.rbfSigma);
// load the support vectors
this.data = json.data;
this.labels = json.labels;
this.alpha = json.alpha;
} else {
console.log("ERROR! unrecognized kernel type." + this.kernelType);
}
}
}
// Kernels
function makeRbfKernel(sigma) {
return function(v1, v2) {
var s=0;
for(var q=0;q<v1.length;q++) { s += (v1[q] - v2[q])*(v1[q] - v2[q]); }
return Math.exp(-s/(2.0*sigma*sigma));
}
}
function linearKernel(v1, v2) {
var s=0;
for(var q=0;q<v1.length;q++) { s += v1[q] * v2[q]; }
return s;
}
// Misc utility functions
// generate random floating point number between a and b
function randf(a, b) {
return Math.random()*(b-a)+a;
}
// generate random integer between a and b (b excluded)
function randi(a, b) {
return Math.floor(Math.random()*(b-a)+a);
}
// create vector of zeros of length n
function zeros(n) {
var arr= new Array(n);
for(var i=0;i<n;i++) { arr[i]= 0; }
return arr;
}
module.exports = SVM2
};
BundleModuleCode['ml/mlp']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: joonkukang, Stefan Bosse
** $INITIAL: (C) 2014, joonkukang
** $MODIFIED: (C) 2006-2022 bLAB by sbosse
** $VERSION: 1.3.2
**
** $INFO:
**
** Multilayer Perceptron Artificial Neural Network
**
** References : http://cs229.stanford.edu/materials/smo.pdf . simplified smo algorithm
**
** Portable model
**
** $ENDOFINFO
*/
/**
*/
var math = Require('ml/math');
var HiddenLayer = Require('ml/HiddenLayer');
var MLP = function (settings) {
var L = {}
var self = L;
self.x = settings.input||settings.x;
self.y = settings.output||settings.y;
self.sigmoidLayers = [];
self.nLayers = settings.hidden_layer_sizes.length;
self.settings = {
'log level' : 1, // 0 : nothing, 1 : info, 2: warn
hidden_layers : settings.hidden_layer_sizes
};
var i;
for(i=0 ; i<self.nLayers+1 ; i++) {
var inputSize, layerInput;
if(i == 0)
inputSize = settings.n_ins;
else
inputSize = settings.hidden_layer_sizes[i-1];
if(i == 0)
layerInput = self.x;
else
layerInput = HiddenLayer.code.sampleHgivenV(self.sigmoidLayers[self.sigmoidLayers.length-1]);
var sigmoidLayer;
if(i == self.nLayers) {
sigmoidLayer = HiddenLayer({
'input' : layerInput,
'n_in' : inputSize,
'n_out' : settings.n_outs,
'activation' : math.sigmoid,
'W' : (typeof settings.w_array === 'undefined')? undefined : settings.w_array[i],
'b' : (typeof settings.b_array === 'undefined')? undefined : settings.b_array[i]
});
} else {
sigmoidLayer = HiddenLayer({
'input' : layerInput,
'n_in' : inputSize,
'n_out' : settings.hidden_layer_sizes[i],
'activation' : math.sigmoid,
'W' : (typeof settings.w_array === 'undefined')? undefined : settings.w_array[i],
'b' : (typeof settings.b_array === 'undefined')? undefined : settings.b_array[i]
});
}
self.sigmoidLayers.push(sigmoidLayer);
}
return L
};
MLP.code = {
train : function(L,settings) { try {
var self = L;
var t0=Date.now();
settings=settings||{}
if (settings.input||settings.x) self.x = settings.input||settings.x;
if (settings.output||settings.y) self.y = settings.output||settings.y;
var epochs = 1000;
if(typeof settings.epochs !== 'undefined')
epochs = settings.epochs;
self.settings.iterations=epochs;
var epoch;
var currentProgress = 1;
for(epoch=0 ; epoch < epochs ; epoch++) {
// Feed Forward
var i;
var layerInput = [];
layerInput.push(self.x);
for(i=0; i<self.nLayers+1 ; i++) {
layerInput.push(HiddenLayer.code.output(self.sigmoidLayers[i],layerInput[i]));
}
var output = layerInput[self.nLayers+1];
// Back Propagation
var delta = new Array(self.nLayers + 1);
delta[self.nLayers] = math.mulMatElementWise(math.minusMat(self.y, output),
math.activateMat(HiddenLayer.code.linearOutput(self.sigmoidLayers[self.nLayers],layerInput[self.nLayers]), math.dSigmoid));
/*
self.nLayers = 3 (3 hidden layers)
delta[3] : ouput layer
delta[2] : 3rd hidden layer, delta[0] : 1st hidden layer
*/
for(i = self.nLayers - 1; i>=0 ; i--) {
delta[i] = math.mulMatElementWise(HiddenLayer.code.backPropagate(self.sigmoidLayers[i+1],delta[i+1]),
math.activateMat(HiddenLayer.code.linearOutput(self.sigmoidLayers[i],layerInput[i]), math.dSigmoid));
}
// Update Weight, Bias
for(var i=0; i<self.nLayers+1 ; i++) {
var deltaW = math.activateMat(math.mulMat(math.transpose(layerInput[i]),delta[i]),function(x){return 1. * x / self.x.length;})
var deltaB = math.meanMatAxis(delta[i],0);
self.sigmoidLayers[i].W = math.addMat(self.sigmoidLayers[i].W,deltaW);
self.sigmoidLayers[i].b = math.addVec(self.sigmoidLayers[i].b,deltaB);
}
if(self.settings['log level'] > 0) {
var progress = (1.*epoch/epochs)*100;
if(progress > currentProgress) {
console.log("MLP",progress.toFixed(0),"% Completed.");
currentProgress+=8;
}
}
}
var crossentropy = MLP.code.getReconstructionCrossEntropy(L);
if(self.settings['log level'] > 0)
console.log("MLP Final Cross Entropy : ",crossentropy);
var t1=Date.now();
return {
time:t1-t0,
epochs:epochs,
loss:crossentropy,
}; } catch (e) { console.log (e) }
},
getReconstructionCrossEntropy : function(L) {
var self = L;
var reconstructedOutput = MLP.code.predict(L,self.x);
var a = math.activateTwoMat(self.y,reconstructedOutput,function(x,y){
return x*Math.log(y);
});
var b = math.activateTwoMat(self.y,reconstructedOutput,function(x,y){
return (1-x)*Math.log(1-y);
});
var crossEntropy = -math.meanVec(math.sumMatAxis(math.addMat(a,b),1));
return crossEntropy
},
predict : function(L,x) {
var self = L;
var output = x;
for(i=0; i<self.nLayers+1 ; i++) {
output = HiddenLayer.code.output(self.sigmoidLayers[i],output);
}
return output;
},
set : function(L,property,value) {
var self = L;
self.settings[property] = value;
}
}
module.exports = MLP
};
BundleModuleCode['ml/HiddenLayer']=function (module,exports){
/**
* Created by joonkukang on 2014. 1. 12..
*/
var math = Require('ml/math');
var HiddenLayer = module.exports = function (settings) {
var L = {}
var self = L;
self.input = settings['input'];
if(typeof settings['W'] === 'undefined') {
var a = 1. / settings['n_in'];
settings['W'] = math.randMat(settings['n_in'],settings['n_out'],-a,a);
}
if(typeof settings['b'] === 'undefined')
settings['b'] = math.zeroVec(settings['n_out']);
if(typeof settings['activation'] === 'undefined')
settings['activation'] = math.sigmoid;
self.W = settings['W'];
self.b = settings['b'];
self.activation = settings['activation'];
return L;
}
HiddenLayer.code = {
output : function(L,input) {
var self = L;
if(typeof input !== 'undefined')
self.input = input;
var linearOutput = math.addMatVec(math.mulMat(self.input,self.W),self.b);
return math.activateMat(linearOutput,self.activation);
},
linearOutput : function(L,input) { // returns the value before activation.
var self = L;
if(typeof input !== 'undefined')
self.input = input;
var linearOutput = math.addMatVec(math.mulMat(self.input,self.W),self.b);
return linearOutput;
},
backPropagate : function (L,input) { // example+num * n_out matrix
var self = L;
if(typeof input === 'undefined')
throw new Error("No BackPropagation Input.")
var linearOutput = math.mulMat(input, math.transpose(self.W));
return linearOutput;
},
sampleHgivenV : function(L,input) {
var self = L;
if(typeof input !== 'undefined')
self.input = input;
var hMean = HiddenLayer.code.output(self);
var hSample = math.probToBinaryMat(hMean);
return hSample;
}
}
};
BundleModuleCode['ml/id3']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Ankit Kuwadekar, Stefan Bosse
** $INITIAL: (C) 2014, Ankit Kuwadekar
** $MODIFIED: (C) 2006-2018 bLAB by sbosse
** $VERSION: 1.3.1
**
** $INFO:
**
** ID3 Decision Tree Algorithm supporting categorical values only
** Portable model
**
** New
** predict with nn selection
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios=none;
/**
* Map of valid tree node types
* @constant
* @static
*/
var NODE_TYPES = {
RESULT: 'result',
FEATURE: 'feature',
FEATURE_VALUE: 'feature_value'
};
function isEqual(a,b) { return a==b }
/**
* Predicts class for sample
*/
function predict(model,sample) {
var root = model;
while (root.type !== NODE_TYPES.RESULT) {
var attr = root.name;
var sampleVal = sample[attr];
var childNode = Comp.array.min(root.vals, function(node) {
if (typeof node.value == 'number' && typeof sampleVal == 'number')
return Math.pow(node.value - sampleVal,2);
else
return node.value == sampleVal? 0:1;
});
if (childNode){
root = childNode.child;
} else {
root = root.vals[0].child;
}
}
return root.value;
};
/**
* Evalutes prediction accuracy on samples
*/
function evaluate(model,target,samples) {
var total = 0;
var correct = 0;
Comp.array.iter(samples, function(s) {
total++;
var pred = predict(model,s);
var actual = s[target];
if (isEqual(pred,actual)) {
correct++;
}
});
return correct / total;
};
/**
* Creates a new tree
*/
function createTree(data, target, features) {
var targets = Comp.array.unique(Comp.array.pluck(data, target));
if (targets.length == 1) {
return {
type: NODE_TYPES.RESULT,
value: targets[0],
name: targets[0],
// alias: targets[0] + randomUUID()
};
}
if (features.length == 0) {
var topTarget = mostCommon(targets);
return {
type: NODE_TYPES.RESULT,
value: topTarget,
name: topTarget,
// alias: topTarget + randomUUID()
};
}
var bestFeature = maxGain(data, target, features);
var remainingFeatures = Comp.array.without(features, bestFeature);
var possibleValues = Comp.array.unique(Comp.array.pluck(data, bestFeature));
var node = {
name: bestFeature,
// alias: bestFeature + randomUUID()
};
node.type = NODE_TYPES.FEATURE;
node.vals = Comp.array.map(possibleValues, function(v) {
var _newS = data.filter(function(x) {
return x[bestFeature] == v
});
var child_node = {
value: v,
// alias: v + randomUUID(),
type: NODE_TYPES.FEATURE_VALUE
};
child_node.child = createTree(_newS, target, remainingFeatures);
return child_node;
});
return node;
}
/**
* Computes Max gain across features to determine best split
* @private
*/
function maxGain(data, target, features) {
var gains=[];
var maxgain= Comp.array.max(features, function(element) {
var g = gain(data, target, element);
gains.push(element+':'+g);
return g;
});
return maxgain;
}
/**
* Computes entropy of a list
* @private
*/
function entropy(vals) {
var uniqueVals = Comp.array.unique(vals);
var probs = uniqueVals.map(function(x) {
return prob(x, vals)
});
var logVals = probs.map(function(p) {
return -p * log2(p)
});
return logVals.reduce(function(a, b) {
return a + b
}, 0);
}
/**
* Computes gain
* @private
*/
function gain(data, target, feature) {
var attrVals = Comp.array.unique(Comp.array.pluck(data, feature));
var setEntropy = entropy(Comp.array.pluck(data, target));
var setSize = data.length;
var entropies = attrVals.map(function(n) {
var subset = data.filter(function(x) {
return x[feature] === n
});
return (subset.length / setSize) * entropy(Comp.array.pluck(subset, target));
});
// var entropyData = entropyV(Comp.array.pluck(data, feature),eps);
// console.log('Feat '+feature+':'+entropyData);
var sumOfEntropies = entropies.reduce(function(a, b) {
return a + b
}, 0);
return setEntropy - sumOfEntropies;
}
/**
* Computes probability of of a given value existing in a given list
* @private
*/
function prob(value, list) {
var occurrences = Comp.array.filter(list, function(element) {
return element === value
});
var numOccurrences = occurrences.length;
var numElements = list.length;
return numOccurrences / numElements;
}
/**
* Computes Log with base-2
* @private
*/
function log2(n) {
return Math.log(n) / Math.log(2);
}
/**
* Finds element with highest occurrence in a list
* @private
*/
function mostCommon(list) {
var elementFrequencyMap = {};
var largestFrequency = -1;
var mostCommonElement = null;
list.forEach(function(element) {
var elementFrequency = (elementFrequencyMap[element] || 0) + 1;
elementFrequencyMap[element] = elementFrequency;
if (largestFrequency < elementFrequency) {
mostCommonElement = element;
largestFrequency = elementFrequency;
}
});
return mostCommonElement;
}
/**
* Generates random UUID
* @private
*/
function randomUUID() {
return "_r" + Math.random().toString(32).slice(2);
}
function depth(model) {
switch (model.type) {
case NODE_TYPES.RESULT: return 1;
case NODE_TYPES.FEATURE:
return 1+Comp.array.max(model.vals.map(function (val) {
return depth(val);
}));
case NODE_TYPES.FEATURE_VALUE:
return 1+depth(model.child);
}
return 0;
}
function info(model) {
var vl = vars(model);
return {
depth:depth(model),
nodes:vl.length,
vars:vl.unique(),
}
}
function print(model,indent) {
var NL = '\n',
line='',sep,
sp = function () {return Comp.string.create(indent);};
if (indent==undefined) indent=0;
switch (model.type) {
case NODE_TYPES.RESULT:
return ' -> '+model.name;
case NODE_TYPES.FEATURE:
line=NL+sp()+'($'+model.name+'?'+NL;
sep='';
Comp.array.iter(model.vals,function (v) {
line += sep+print(v,indent+2)+NL;
sep='';
});
return line+sp()+')';
case NODE_TYPES.FEATURE_VALUE:
return sp()+model.value+':'+print(model.child,indent+2);
}
return 0;
}
function vars(model) {
switch (model.type) {
case NODE_TYPES.RESULT: return [];
case NODE_TYPES.FEATURE:
return [model.name].concat(Comp.array.flatten(model.vals.map(vars)));
case NODE_TYPES.FEATURE_VALUE:
return vars(model.child);
}
return [];
}
module.exports = {
NODE_TYPES:NODE_TYPES,
createTree:createTree,
depth:depth,
entropy:entropy,
evaluate:evaluate,
info:info,
predict:predict,
print:print,
current:function (module) { current=module.current; Aios=module;}
};
};
BundleModuleCode['ml/C45']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: ?, Stefan Bosse
** $INITIAL: (C) ?
** $MODIFIED: (C) 2006-2018 bLAB by sbosse
** $VERSION: 1.1.6
**
** $INFO:
**
** C45 Decision Tree ML Algorithm
**
** Portable model
**
** $ENDOFINFO
*/
'use strict';
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios=none;
var NODE_TYPES = {
RESULT: 'result',
FEATURE_NUMBER: 'feature_number', // Number value node (cut split)
FEATURE_VALUE: 'feature_value', // Category value
FEATURE_CATEGORY: 'feature_category' // Symbolic variable node (split)
};
function unique(col) {
var u = {}, a = [];
for(var i = 0, l = col.length; i < l; ++i){
if(u.hasOwnProperty(col[i])) {
continue;
}
a.push(col[i]);
u[col[i]] = 1;
}
return a;
}
function find(col, pred) {
var value;
col.forEach(function(item) {
var result = pred(item);
if (result) {
value = item;
}
});
return value;
}
function max(array, fn) {
var max = -Infinity;
var index;
for (var i = 0; i < array.length; i++) {
var result = fn(array[i]);
if (result >= max) {
max = result;
index = i;
}
}
return typeof index !== 'undefined' ? array[index] : max;
}
function sortBy(col, fn) {
col = [].slice.call(col);
return col.sort(fn);
}
var C45 = {
create: function () {
return {
features : [],
targets: [],
model: null
}
},
/**
* train
*
* @param {object} options
* @param {array} options.data - training data
* @param {string} options.target - class label
* @param {array} options.features - features names
* @param {array} options.featureTypes - features type (ie 'category', 'number')
*/
train: function(model,options) {
var data = options.data,
target = options.target,
features = options.features,
featureTypes = options.featureTypes;
featureTypes.forEach(function(f) {
if (['number','category'].indexOf(f) === -1) {
throw new Error('C4.5: Unrecognized option!');
}
});
var targets = unique(data.map(function(d) {
return d[d.length-1];
}));
model.features = features;
model.targets = targets;
// model is the generated tree structure
model.model = C45._c45(model, data, target, features, featureTypes, 0);
},
_c45: function(model, data, target, features, featureTypes, depth) {
var targets = unique(data.map(function(d) {
return d[d.length-1];
}));
if (!targets.length) {
return {
type: 'result',
value: 'none data',
name: 'none data'
};
}
if (targets.length === 1) {
return {
type: 'result',
value: targets[0],
name: targets[0]
};
}
if (!features.length) {
var topTarget = C45.mostCommon(targets);
return {
type: 'result',
value: topTarget,
name: topTarget
};
}
var bestFeatureData = C45.maxGain(model, data, target, features, featureTypes);
var bestFeature = bestFeatureData.feature;
var remainingFeatures = features.slice(0);
remainingFeatures.splice(features.indexOf(bestFeature), 1);
if (featureTypes[model.features.indexOf(bestFeature)] === 'category') {
var possibleValues = unique(data.map(function(d) {
return d[model.features.indexOf(bestFeature)];
}));
var node = {
name: bestFeature,
type: 'feature_category',
values: possibleValues.map(function(v) {
var newData = data.filter(function(x) {
return x[model.features.indexOf(bestFeature)] === v;
});
var childNode = {
name: v,
type: 'feature_value',
child: C45._c45(model, newData, target, remainingFeatures, featureTypes, depth+1)
};
return childNode;
})
};
} else if (featureTypes[model.features.indexOf(bestFeature)] === 'number') {
var possibleValues = unique(data.map(function(d) {
return d[model.features.indexOf(bestFeature)];
}));
var node = {
name: bestFeature,
type: 'feature_number',
cut: bestFeatureData.cut,
values: []
};
var newDataRight = data.filter(function(x) {
return parseFloat(x[model.features.indexOf(bestFeature)]) > bestFeatureData.cut;
});
var childNodeRight = {
name: bestFeatureData.cut.toString(),
type: 'feature_value',
child: C45._c45(model, newDataRight, target, remainingFeatures, featureTypes, depth+1)
};
node.values.push(childNodeRight);
var newDataLeft = data.filter(function(x) {
return parseFloat(x[model.features.indexOf(bestFeature)]) <= bestFeatureData.cut;
});
var childNodeLeft = {
name: bestFeatureData.cut.toString(),
type: 'feature_value',
child: C45._c45(model, newDataLeft, target, remainingFeatures, featureTypes, depth+1),
};
node.values.push(childNodeLeft);
}
return node;
},
classify: function (model,sample) {
// root is feature (attribute) containing all sub values
var childNode, featureName, sampleVal;
var root = model.model;
if (typeof root === 'undefined') {
callback(new Error('model is undefined'));
}
while (root.type != NODE_TYPES.RESULT) {
if (root.type == NODE_TYPES.FEATURE_NUMBER) {
// feature number attribute
featureName = root.name;
sampleVal = parseFloat(sample[featureName]);
if (sampleVal <= root.cut) {
childNode = root.values[1];
} else {
childNode = root.values[0];
}
} else if (root.type == NODE_TYPES.FEATURE_CATEGORY) {
// feature category attribute
featureName = root.name;
sampleVal = sample[featureName];
// sub value , containing n childs
childNode = find(root.values, function(x) {
return x.name === sampleVal;
});
}
// non trained feature
if (typeof childNode === 'undefined') {
return 'unknown';
}
root = childNode.child;
}
return root.value;
},
conditionalEntropy: function(model, data, feature, cut, target) {
var subset1 = data.filter(function(x) {
return parseFloat(x[model.features.indexOf(feature)]) <= cut;
});
var subset2 = data.filter(function(x) {
return parseFloat(x[model.features.indexOf(feature)]) > cut;
});
var setSize = data.length;
return subset1.length/setSize * C45.entropy(model,
subset1.map(function(d) {
return d[d.length-1];
})
) + subset2.length/setSize*C45.entropy(model,
subset2.map(function(d) {
return d[d.length-1];
})
);
},
count: function(target, targets) {
return targets.filter(function(t) {
return t === target;
}).length;
},
entropy: function(model, vals) {
var uniqueVals = unique(vals);
var probs = uniqueVals.map(function(x) {
return C45.prob(x, vals);
});
var logVals = probs.map(function(p) {
return -p * C45.log2(p);
});
return logVals.reduce(function(a, b) {
return a + b;
}, 0);
},
gain: function(model, data, target, features, feature, featureTypes) {
var setEntropy = C45.entropy(model, data.map(function(d) {
return d[d.length-1];
}));
if (featureTypes[model.features.indexOf(feature)] === 'category') {
var attrVals = unique(data.map(function(d) {
return d[model.features.indexOf(feature)];
}));
var setSize = data.length;
var entropies = attrVals.map(function(n) {
var subset = data.filter(function(x) {
return x[feature] === n;
});
return (subset.length/setSize) * C45.entropy(model,
subset.map(function(d) {
return d[d.length-1];
})
);
});
var sumOfEntropies = entropies.reduce(function(a, b) {
return a + b;
}, 0);
return {
feature: feature,
gain: setEntropy - sumOfEntropies,
cut: 0
};
} else if (featureTypes[model.features.indexOf(feature)] === 'number') {
var attrVals = unique(data.map(function(d) {
return d[model.features.indexOf(feature)];
}));
var gainVals = attrVals.map(function(cut) {
var cutf = parseFloat(cut);
var gain = setEntropy - C45.conditionalEntropy(model, data, feature, cutf, target);
return {
feature: feature,
gain: gain,
cut: cutf
};
});
var maxgain = max(gainVals, function(e) {
return e.gain;
});
return maxgain;
}
},
log2: function(n) {
return Math.log(n) / Math.log(2);
},
maxGain: function(model, data, target, features, featureTypes) {
var g45 = features.map(function(feature) {
return C45.gain(model, data, target, features, feature, featureTypes);
});
return max(g45, function(e) {
return e.gain;
});
},
mostCommon: function(targets) {
return sortBy(targets, function(target) {
return C45.count(target, targets);
}).reverse()[0];
},
/** Print the tree
*
*/
print: function (model,indent) {
var NL = '\n',
line='',sep;
if (indent==undefined) indent=0;
if (!model) return '';
var sp = function () {return Comp.string.create(indent);};
switch (model.type) {
case NODE_TYPES.RESULT:
return sp()+'-> '+model.name+NL;
case NODE_TYPES.FEATURE_CATEGORY:
line=sp()+'$'+model.name+'?'+NL;
Comp.array.iter(model.values,function (v) {
line += C45.print(v,indent+2);
});
return line;
case NODE_TYPES.FEATURE_NUMBER:
line = sp()+'$'+model.name+'>'+model.cut+'?'+NL;
if (model.values[0].type==NODE_TYPES.FEATURE_VALUE)
line = line+C45.print(model.values[0].child,indent+2);
else
line = line+C45.print(model.values[0],indent+2);
line = line+sp()+'$'+model.name+'<='+model.cut+'?'+NL;
if (model.values[0].type==NODE_TYPES.FEATURE_VALUE)
line = line+C45.print(model.values[1].child,indent+2);
else
line = line+C45.print(model.values[1],indent+2);
return line;
case NODE_TYPES.FEATURE_VALUE:
line=sp()+''+model.name+NL;
line += C45.print(model.child,indent+2);
return line;
}
return 'model?';
},
prob: function(target, targets) {
return C45.count(target,targets)/targets.length;
},
};
module.exports = {
classify:C45.classify,
create:C45.create,
entropy:C45.entropy,
log2:C45.log2,
print:function (model,indent) { return C45.print(model.model,indent) },
unique:unique,
train:C45.train,
current:function (module) { current=module.current; Aios=module;}
}
};
BundleModuleCode['ml/text']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2019 BSSLAB
** $CREATED: 5-3-19 by sbosse.
** $VERSION: 1.1.1
**
** $INFO:
**
** JavaScript AIOS Machine Learning API: Text analysis
**
** Portable model
**
** $ENDOFINFO
*/
'use strict';
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios=none;
function similarity(s1, s2) {
var longer = s1;
var shorter = s2;
if (s1.length < s2.length) {
longer = s2;
shorter = s1;
}
var longerLength = longer.length;
if (longerLength == 0) {
return 1.0;
}
return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength);
}
function editDistance(s1, s2) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
var costs = new Array();
for (var i = 0; i <= s1.length; i++) {
var lastValue = i;
for (var j = 0; j <= s2.length; j++) {
if (i == 0)
costs[j] = j;
else {
if (j > 0) {
var newValue = costs[j - 1];
if (s1.charAt(i - 1) != s2.charAt(j - 1))
newValue = Math.min(Math.min(newValue, lastValue),
costs[j]) + 1;
costs[j - 1] = lastValue;
lastValue = newValue;
}
}
}
if (i > 0)
costs[s2.length] = lastValue;
}
return costs[s2.length];
}
// Create a model
function create(strings,options) {
return {
data:strings
}
}
// Classify one sample; return best matching string
function classify(model,sample) {
var matches = model.data.map(function (h) {
return {
match:similarity(h,sample),
string:h
}
}).sort(function (a,b) {
if (a.match < b.match) return 1; else return -1;
});
return matches[0];
}
module.exports = {
classify:classify,
create:create,
similarity:similarity,
current:function (module) { current=module.current; Aios=module;}
}
};
BundleModuleCode['ml/rf']=function (module,exports){
// MIT License
// Random Forest Trees (only binary classifier)
// Andrej Karpathy
// @blab+
// https://github.com/karpathy/forestjs
var RandomForest = function(options) {
var L = {};
return L
}
RandomForest.code = {
/*
data is 2D array of size N x D of examples
labels is a 1D array of labels (only -1 or 1 for now). In future will support multiclass or maybe even regression
options.numTrees can be used to customize number of trees to train (default = 100)
options.maxDepth is the maximum depth of each tree in the forest (default = 4)
options.numTries is the number of random hypotheses generated at each node during training (default = 10)
options.trainFun is a function with signature "function myWeakTrain(data, labels, ix, options)". Here, ix is a list of
indeces into data of the instances that should be payed attention to. Everything not in the list
should be ignored. This is done for efficiency. The function should return a model where you store
variables. (i.e. model = {}; model.myvar = 5;) This will be passed to testFun.
options.testFun is a function with signature "funtion myWeakTest(inst, model)" where inst is 1D array specifying an example,
and model will be the same model that you return in options.trainFun. For example, model.myvar will be 5.
see decisionStumpTrain() and decisionStumpTest() downstairs for example.
*/
train: function(L, data, labels, options) {
options = options || {};
L.options = options;
L.numTrees = options.numTrees || 100;
// initialize many trees and train them all independently
L.trees= new Array(L.numTrees);
for(var i=0;i<L.numTrees;i++) {
L.trees[i] = DecisionTree();
DecisionTree.code.train(L.trees[i],data, labels, options);
}
},
/*
inst is a 1D array of length D of an example.
returns the probability of label 1, i.e. a number in range [0, 1]
*/
predictOne: function(L, inst) {
// have each tree predict and average out all votes
var dec=0;
for(var i=0;i<L.numTrees;i++) {
dec += DecisionTree.code.predictOne(L.trees[i],inst);
}
dec /= L.numTrees;
return dec;
},
// convenience function. Here, data is NxD array.
// returns probabilities of being 1 for all data in an array.
predict: function(L, data) {
var probabilities= new Array(data.length);
for(var i=0;i<data.length;i++) {
probabilities[i]= RandomForest.code.predictOne(L,data[i]);
}
return probabilities;
}
}
// represents a single decision tree
var DecisionTree = function(options) {
var L = {};
return L
}
DecisionTree.code = {
train: function(L, data, labels, options) {
options = options || {};
var maxDepth = options.maxDepth || 4;
var weakType = options.type || 0;
var trainFun= decisionStumpTrain;
var testFun= decisionStumpTest;
if(options.trainFun) trainFun = options.trainFun;
if(options.testFun) testFun = options.testFun;
if(weakType == 0) {
// Default
trainFun = decisionStumpTrain;
testFun = decisionStumpTest;
}
if(weakType) {
trainFun = decision2DStumpTrain;
L.testFun = testFun = decision2DStumpTest;
}
// initialize various helper variables
var numInternals= Math.pow(2, maxDepth)-1;
var numNodes= Math.pow(2, maxDepth + 1)-1;
var ixs= new Array(numNodes);
for(var i=1;i<ixs.length;i++) ixs[i]=[];
ixs[0]= new Array(labels.length);
for(var i=0;i<labels.length;i++) ixs[0][i]= i; // root node starts out with all nodes as relevant
var models = new Array(numInternals);
// train
for(var n=0; n < numInternals; n++) {
// few base cases
var ixhere= ixs[n];
if(ixhere.length == 0) { continue; }
if(ixhere.length == 1) { ixs[n*2+1] = [ixhere[0]]; continue; } // arbitrary send it down left
// learn a weak model on relevant data for this node
var model= trainFun(data, labels, ixhere);
models[n]= model; // back it up model
// split the data according to the learned model
var ixleft=[];
var ixright=[];
for(var i=0; i<ixhere.length;i++) {
var label= testFun(data[ixhere[i]], model);
if(label === 1) ixleft.push(ixhere[i]);
else ixright.push(ixhere[i]);
}
ixs[n*2+1]= ixleft;
ixs[n*2+2]= ixright;
}
// compute data distributions at the leafs
var leafPositives = new Array(numNodes);
var leafNegatives = new Array(numNodes);
for(var n=numInternals; n < numNodes; n++) {
var numones= 0;
for(var i=0;i<ixs[n].length;i++) {
if(labels[ixs[n][i]] === 1) numones+=1;
}
leafPositives[n]= numones;
leafNegatives[n]= ixs[n].length-numones;
}
// back up important prediction variables for predicting later
L.models= models;
L.leafPositives = leafPositives;
L.leafNegatives = leafNegatives;
L.maxDepth= maxDepth;
// L.trainFun= trainFun;
// L.testFun= testFun;
},
// returns probability that example inst is 1.
predictOne: function(L, inst) {
var testFun = L.testFun||decisionStumpTest;
var n=0;
for(var i=0;i<L.maxDepth;i++) {
var dir= testFun(inst, L.models[n]);
if(dir === 1) n= n*2+1; // descend left
else n= n*2+2; // descend right
}
return (L.leafPositives[n] + 0.5) / (L.leafNegatives[n] + 1.0); // bayesian smoothing!
}
}
// returns model
function decisionStumpTrain(data, labels, ix, options) {
options = options || {};
var numtries = options.numTries || 10;
// choose a dimension at random and pick a best split
var ri= randi(0, data[0].length);
var N= ix.length;
// evaluate class entropy of incoming data
var H= entropy(labels, ix);
var bestGain=0;
var bestThr= 0;
for(var i=0;i<numtries;i++) {
// pick a random splitting threshold
var ix1= ix[randi(0, N)];
var ix2= ix[randi(0, N)];
while(ix2==ix1) ix2= ix[randi(0, N)]; // enforce distinctness of ix2
var a= Math.random();
var thr= data[ix1][ri]*a + data[ix2][ri]*(1-a);
// measure information gain we'd get from split with thr
var l1=1, r1=1, lm1=1, rm1=1; //counts for Left and label 1, right and label 1, left and minus 1, right and minus 1
for(var j=0;j<ix.length;j++) {
if(data[ix[j]][ri] < thr) {
if(labels[ix[j]]==1) l1++;
else lm1++;
} else {
if(labels[ix[j]]==1) r1++;
else rm1++;
}
}
var t= l1+lm1; // normalize the counts to obtain probability estimates
l1=l1/t;
lm1=lm1/t;
t= r1+rm1;
r1=r1/t;
rm1= rm1/t;
var LH= -l1*Math.log(l1) -lm1*Math.log(lm1); // left and right entropy
var RH= -r1*Math.log(r1) -rm1*Math.log(rm1);
var informationGain= H - LH - RH;
//console.log("Considering split %f, entropy %f -> %f, %f. Gain %f", thr, H, LH, RH, informationGain);
if(informationGain > bestGain || i === 0) {
bestGain= informationGain;
bestThr= thr;
}
}
model= {};
model.thr= bestThr;
model.ri= ri;
return model;
}
// returns a decision for a single data instance
function decisionStumpTest(inst, model) {
if(!model) {
// this is a leaf that never received any data...
return 1;
}
return inst[model.ri] < model.thr ? 1 : -1;
}
// returns model. Code duplication with decisionStumpTrain :(
function decision2DStumpTrain(data, labels, ix, options) {
options = options || {};
var numtries = options.numTries || 10;
// choose a dimension at random and pick a best split
var N= ix.length;
var ri1= 0;
var ri2= 1;
if(data[0].length > 2) {
// more than 2D data. Pick 2 random dimensions
ri1= randi(0, data[0].length);
ri2= randi(0, data[0].length);
while(ri2 == ri1) ri2= randi(0, data[0].length); // must be distinct!
}
// evaluate class entropy of incoming data
var H= entropy(labels, ix);
var bestGain=0;
var bestw1, bestw2, bestthr;
var dots= new Array(ix.length);
for(var i=0;i<numtries;i++) {
// pick random line parameters
var alpha= randf(0, 2*Math.PI);
var w1= Math.cos(alpha);
var w2= Math.sin(alpha);
// project data on this line and get the dot products
for(var j=0;j<ix.length;j++) {
dots[j]= w1*data[ix[j]][ri1] + w2*data[ix[j]][ri2];
}
// we are in a tricky situation because data dot product distribution
// can be skewed. So we don't want to select just randomly between
// min and max. But we also don't want to sort as that is too expensive
// let's pick two random points and make the threshold be somewhere between them.
// for skewed datasets, the selected points will with relatively high likelihood
// be in the high-desnity regions, so the thresholds will make sense
var ix1= ix[randi(0, N)];
var ix2= ix[randi(0, N)];
while(ix2==ix1) ix2= ix[randi(0, N)]; // enforce distinctness of ix2
var a= Math.random();
var dotthr= dots[ix1]*a + dots[ix2]*(1-a);
// measure information gain we'd get from split with thr
var l1=1, r1=1, lm1=1, rm1=1; //counts for Left and label 1, right and label 1, left and minus 1, right and minus 1
for(var j=0;j<ix.length;j++) {
if(dots[j] < dotthr) {
if(labels[ix[j]]==1) l1++;
else lm1++;
} else {
if(labels[ix[j]]==1) r1++;
else rm1++;
}
}
var t= l1+lm1;
l1=l1/t;
lm1=lm1/t;
t= r1+rm1;
r1=r1/t;
rm1= rm1/t;
var LH= -l1*Math.log(l1) -lm1*Math.log(lm1); // left and right entropy
var RH= -r1*Math.log(r1) -rm1*Math.log(rm1);
var informationGain= H - LH - RH;
//console.log("Considering split %f, entropy %f -> %f, %f. Gain %f", thr, H, LH, RH, informationGain);
if(informationGain > bestGain || i === 0) {
bestGain= informationGain;
bestw1= w1;
bestw2= w2;
bestthr= dotthr;
}
}
model= {};
model.w1= bestw1;
model.w2= bestw2;
model.dotthr= bestthr;
return model;
}
// returns label for a single data instance
function decision2DStumpTest(inst, model) {
if(!model) {
// this is a leaf that never received any data...
return 1;
}
return inst[0]*model.w1 + inst[1]*model.w2 < model.dotthr ? 1 : -1;
}
// Misc utility functions
function entropy(labels, ix) {
var N= ix.length;
var p=0.0;
for(var i=0;i<N;i++) {
if(labels[ix[i]]==1) p+=1;
}
p=(1+p)/(N+2); // let's be bayesian about this
q=(1+N-p)/(N+2);
return (-p*Math.log(p) -q*Math.log(q));
}
// generate random floating point number between a and b
function randf(a, b) {
return Math.random()*(b-a)+a;
}
// generate random integer between a and b (b excluded)
function randi(a, b) {
return Math.floor(Math.random()*(b-a)+a);
}
module.exports = RandomForest
};
BundleModuleCode['ml/rl']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Ankit Kuwadekar, Stefan Bosse
** $INITIAL: (C) 2015, Andrej Karpathy
** $MODIFIED: (C) 2006-2019 bLAB by sbosse
** $VERSION: 1.1.2
**
** $INFO:
**
** Reinforcement Learning module that implements several common RL algorithms.
** Portable models (TDAgent/DPAgent/DQNAgent)
**
** $ENDOFINFO
*/
"use strict";
var options = {
version:'1.1.2'
}
var Io = Require('com/io')
var R = module.exports; // the Recurrent library
// Utility fun
function assert(condition, message) {
// from http://stackoverflow.com/questions/15313418/javascript-assert
if (!condition) {
message = message || "Assertion failed";
if (typeof Error !== "undefined") {
throw new Error(message);
}
throw message; // Fallback
}
}
// Random numbers utils
var return_v = false;
var v_val = 0.0;
var gaussRandom = function() {
if(return_v) {
return_v = false;
return v_val;
}
var u = 2*Math.random()-1;
var v = 2*Math.random()-1;
var r = u*u + v*v;
if(r == 0 || r > 1) return gaussRandom();
var c = Math.sqrt(-2*Math.log(r)/r);
v_val = v*c; // cache this
return_v = true;
return u*c;
}
var randf = function(a, b) { return Math.random()*(b-a)+a; }
var randi = function(a, b) { return Math.floor(Math.random()*(b-a)+a); }
var randn = function(mu, std){ return mu+gaussRandom()*std; }
// helper function returns array of zeros of length n
// and uses typed arrays if available
var zeros = function(n) {
if(typeof(n)==='undefined' || isNaN(n)) { return []; }
if(typeof ArrayBuffer === 'undefined') {
// lacking browser support
var arr = new Array(n);
for(var i=0;i<n;i++) { arr[i] = 0; }
return arr;
} else {
return new Float64Array(n);
}
}
// Mat holds a matrix
var Mat = function(n,d) {
var M = {}
// n is number of rows d is number of columns
M.n = n;
M.d = d;
M.w = zeros(n * d);
M.dw = zeros(n * d);
return M;
}
Mat.code = {
get: function(M,row, col) {
// slow but careful accessor function
// we want row-major order
var ix = (M.d * row) + col;
assert(ix >= 0 && ix < M.w.length);
return M.w[ix];
},
set: function(M, row, col, v) {
// slow but careful accessor function
var ix = (M.d * row) + col;
assert(ix >= 0 && ix < M.w.length);
M.w[ix] = v;
},
setFrom: function(M, arr) {
for(var i=0,n=arr.length;i<n;i++) {
M.w[i] = arr[i];
}
},
setColumn: function(M, m, i) {
for(var q=0,n=m.w.length;q<n;q++) {
M.w[(M.d * q) + i] = m.w[q];
}
},
toJSON: function(M) {
var json = {};
json['n'] = M.n;
json['d'] = M.d;
json['w'] = M.w;
return json;
},
fromJSON: function(M, json) {
M.n = json.n;
M.d = json.d;
M.w = zeros(M.n * M.d);
M.dw = zeros(M.n * M.d);
for(var i=0,n=M.n * M.d;i<n;i++) {
M.w[i] = json.w[i]; // copy over weights
}
}
}
var copyMat = function(b) {
var a = Mat(b.n, b.d);
Mat.code.setFrom(a, b.w);
return a;
}
var copyNet = function(net) {
// nets are (k,v) pairs with k = string key, v = Mat()
var new_net = {};
for(var p in net) {
if(net.hasOwnProperty(p)){
new_net[p] = copyMat(net[p]);
}
}
return new_net;
}
var updateMat = function(m, alpha) {
// updates in place
for(var i=0,n=m.n*m.d;i<n;i++) {
if(m.dw[i] !== 0) {
m.w[i] += - alpha * m.dw[i];
m.dw[i] = 0;
}
}
}
var updateNet = function(net, alpha) {
for(var p in net) {
if(net.hasOwnProperty(p)){
updateMat(net[p], alpha);
}
}
}
var netToJSON = function(net) {
var j = {};
for(var p in net) {
if(net.hasOwnProperty(p)){
j[p] = Mat.code.toJSON(net[p]);
}
}
return j;
}
var netFromJSON = function(j) {
var net = {};
for(var p in j) {
if(j.hasOwnProperty(p)){
net[p] = Mat(1,1); // not proud of this
Mat.code.fromJSON(net[p],j[p]);
}
}
return net;
}
var netZeroGrads = function(net) {
for(var p in net) {
if(net.hasOwnProperty(p)){
var mat = net[p];
gradFillConst(mat, 0);
}
}
}
var netFlattenGrads = function(net) {
var n = 0;
for(var p in net) {
if(net.hasOwnProperty(p)) {
var mat = net[p]; n += mat.dw.length;
}}
var g = Mat(n, 1);
var ix = 0;
for(var p in net) {
if(net.hasOwnProperty(p)){
var mat = net[p];
for(var i=0,m=mat.dw.length;i<m;i++) {
g.w[ix] = mat.dw[i];
ix++;
}
}
}
return g;
}
// return Mat but filled with random numbers from gaussian
var RandMat = function(n,d,mu,std) {
var m = Mat(n, d);
fillRandn(m,mu,std);
//fillRand(m,-std,std); // kind of :P
return m;
}
// Mat utils
// fill matrix with random gaussian numbers
var fillRandn = function(m, mu, std) { for(var i=0,n=m.w.length;i<n;i++) { m.w[i] = randn(mu, std); } }
var fillRand = function(m, lo, hi) { for(var i=0,n=m.w.length;i<n;i++) { m.w[i] = randf(lo, hi); } }
var gradFillConst = function(m, c) { for(var i=0,n=m.dw.length;i<n;i++) { m.dw[i] = c } }
// Transformer definitions
var Graph = function(needs_backprop) {
var G = {}
if(typeof needs_backprop === 'undefined') { needs_backprop = true; }
G.needs_backprop = needs_backprop;
// this will store a list of functions that perform backprop,
// in their forward pass order. So in backprop we will go
// backwards and evoke each one
G.backprop = [];
return G
}
Graph.code = {
backward: function(G) {
for(var i=G.backprop.length-1;i>=0;i--) {
G.backprop[i](); // tick!
}
},
rowPluck: function(G, m, ix) {
// pluck a row of m with index ix and return it as col vector
assert(ix >= 0 && ix < m.n);
var d = m.d;
var out = Mat(d, 1);
for(var i=0,n=d;i<n;i++){ out.w[i] = m.w[d * ix + i]; } // copy over the data
if(G.needs_backprop) {
var backward = function() {
for(var i=0,n=d;i<n;i++){ m.dw[d * ix + i] += out.dw[i]; }
}
G.backprop.push(backward);
}
return out;
},
tanh: function(G, m) {
// tanh nonlinearity
var out = Mat(m.n, m.d);
var n = m.w.length;
for(var i=0;i<n;i++) {
out.w[i] = Math.tanh(m.w[i]);
}
if(G.needs_backprop) {
var backward = function() {
for(var i=0;i<n;i++) {
// grad for z = tanh(x) is (1 - z^2)
var mwi = out.w[i];
m.dw[i] += (1.0 - mwi * mwi) * out.dw[i];
}
}
G.backprop.push(backward);
}
return out;
},
sigmoid: function(G, m) {
// sigmoid nonlinearity
var out = Mat(m.n, m.d);
var n = m.w.length;
for(var i=0;i<n;i++) {
out.w[i] = sig(m.w[i]);
}
if(G.needs_backprop) {
var backward = function() {
for(var i=0;i<n;i++) {
// grad for z = tanh(x) is (1 - z^2)
var mwi = out.w[i];
m.dw[i] += mwi * (1.0 - mwi) * out.dw[i];
}
}
G.backprop.push(backward);
}
return out;
},
relu: function(G, m) {
var out = Mat(m.n, m.d);
var n = m.w.length;
for(var i=0;i<n;i++) {
out.w[i] = Math.max(0, m.w[i]); // relu
}
if(G.needs_backprop) {
var backward = function() {
for(var i=0;i<n;i++) {
m.dw[i] += m.w[i] > 0 ? out.dw[i] : 0.0;
}
}
G.backprop.push(backward);
}
return out;
},
mul: function(G, m1, m2) {
// multiply matrices m1 * m2
assert(m1.d === m2.n, 'matmul dimensions misaligned');
var n = m1.n;
var d = m2.d;
var out = Mat(n,d);
for(var i=0;i<m1.n;i++) { // loop over rows of m1
for(var j=0;j<m2.d;j++) { // loop over cols of m2
var dot = 0.0;
for(var k=0;k<m1.d;k++) { // dot product loop
dot += m1.w[m1.d*i+k] * m2.w[m2.d*k+j];
}
out.w[d*i+j] = dot;
}
}
if(G.needs_backprop) {
var backward = function() {
for(var i=0;i<m1.n;i++) { // loop over rows of m1
for(var j=0;j<m2.d;j++) { // loop over cols of m2
for(var k=0;k<m1.d;k++) { // dot product loop
var b = out.dw[d*i+j];
m1.dw[m1.d*i+k] += m2.w[m2.d*k+j] * b;
m2.dw[m2.d*k+j] += m1.w[m1.d*i+k] * b;
}
}
}
}
G.backprop.push(backward);
}
return out;
},
add: function(G, m1, m2) {
assert(m1.w.length === m2.w.length);
var out = Mat(m1.n, m1.d);
for(var i=0,n=m1.w.length;i<n;i++) {
out.w[i] = m1.w[i] + m2.w[i];
}
if(G.needs_backprop) {
var backward = function() {
for(var i=0,n=m1.w.length;i<n;i++) {
m1.dw[i] += out.dw[i];
m2.dw[i] += out.dw[i];
}
}
G.backprop.push(backward);
}
return out;
},
dot: function(G, m1, m2) {
// m1 m2 are both column vectors
assert(m1.w.length === m2.w.length);
var out = Mat(1,1);
var dot = 0.0;
for(var i=0,n=m1.w.length;i<n;i++) {
dot += m1.w[i] * m2.w[i];
}
out.w[0] = dot;
if(G.needs_backprop) {
var backward = function() {
for(var i=0,n=m1.w.length;i<n;i++) {
m1.dw[i] += m2.w[i] * out.dw[0];
m2.dw[i] += m1.w[i] * out.dw[0];
}
}
G.backprop.push(backward);
}
return out;
},
eltmul: function(G, m1, m2) {
assert(m1.w.length === m2.w.length);
var out = Mat(m1.n, m1.d);
for(var i=0,n=m1.w.length;i<n;i++) {
out.w[i] = m1.w[i] * m2.w[i];
}
if(G.needs_backprop) {
var backward = function() {
for(var i=0,n=m1.w.length;i<n;i++) {
m1.dw[i] += m2.w[i] * out.dw[i];
m2.dw[i] += m1.w[i] * out.dw[i];
}
}
G.backprop.push(backward);
}
return out;
},
}
var softmax = function(m) {
var out = Mat(m.n, m.d); // probability volume
var maxval = -999999;
for(var i=0,n=m.w.length;i<n;i++) { if(m.w[i] > maxval) maxval = m.w[i]; }
var s = 0.0;
for(var i=0,n=m.w.length;i<n;i++) {
out.w[i] = Math.exp(m.w[i] - maxval);
s += out.w[i];
}
for(var i=0,n=m.w.length;i<n;i++) { out.w[i] /= s; }
// no backward pass here needed
// since we will use the computed probabilities outside
// to set gradients directly on m
return out;
}
var Solver = function() {
var S = {}
S.decay_rate = 0.999;
S.smooth_eps = 1e-8;
S.step_cache = {};
return S
}
Solver.code = {
step: function(S, model, step_size, regc, clipval) {
// perform parameter update
var solver_stats = {};
var num_clipped = 0;
var num_tot = 0;
for(var k in model) {
if(model.hasOwnProperty(k)) {
var m = model[k]; // mat ref
if(!(k in S.step_cache)) { S.step_cache[k] = Mat(m.n, m.d); }
var s = S.step_cache[k];
for(var i=0,n=m.w.length;i<n;i++) {
// rmsprop adaptive learning rate
var mdwi = m.dw[i];
s.w[i] = s.w[i] * S.decay_rate + (1.0 - S.decay_rate) * mdwi * mdwi;
// gradient clip
if(mdwi > clipval) {
mdwi = clipval;
num_clipped++;
}
if(mdwi < -clipval) {
mdwi = -clipval;
num_clipped++;
}
num_tot++;
// update (and regularize)
m.w[i] += - step_size * mdwi / Math.sqrt(s.w[i] + S.smooth_eps) - regc * m.w[i];
m.dw[i] = 0; // reset gradients for next iteration
}
}
}
solver_stats['ratio_clipped'] = num_clipped*1.0/num_tot;
return solver_stats;
}
}
var initLSTM = function(input_size, hidden_sizes, output_size) {
// hidden size should be a list
var model = {};
for(var d=0;d<hidden_sizes.length;d++) { // loop over depths
var prev_size = d === 0 ? input_size : hidden_sizes[d - 1];
var hidden_size = hidden_sizes[d];
// gates parameters
model['Wix'+d] = RandMat(hidden_size, prev_size , 0, 0.08);
model['Wih'+d] = RandMat(hidden_size, hidden_size , 0, 0.08);
model['bi'+d] = Mat(hidden_size, 1);
model['Wfx'+d] = RandMat(hidden_size, prev_size , 0, 0.08);
model['Wfh'+d] = RandMat(hidden_size, hidden_size , 0, 0.08);
model['bf'+d] = Mat(hidden_size, 1);
model['Wox'+d] = RandMat(hidden_size, prev_size , 0, 0.08);
model['Woh'+d] = RandMat(hidden_size, hidden_size , 0, 0.08);
model['bo'+d] = Mat(hidden_size, 1);
// cell write params
model['Wcx'+d] = RandMat(hidden_size, prev_size , 0, 0.08);
model['Wch'+d] = RandMat(hidden_size, hidden_size , 0, 0.08);
model['bc'+d] = Mat(hidden_size, 1);
}
// decoder params
model['Whd'] = RandMat(output_size, hidden_size, 0, 0.08);
model['bd'] = Mat(output_size, 1);
return model;
}
var forwardLSTM = function(G, model, hidden_sizes, x, prev) {
// forward prop for a single tick of LSTM
// G is graph to append ops to
// model contains LSTM parameters
// x is 1D column vector with observation
// prev is a struct containing hidden and cell
// from previous iteration
if(prev == null || typeof prev.h === 'undefined') {
var hidden_prevs = [];
var cell_prevs = [];
for(var d=0;d<hidden_sizes.length;d++) {
hidden_prevs.push(R.Mat(hidden_sizes[d],1));
cell_prevs.push(R.Mat(hidden_sizes[d],1));
}
} else {
var hidden_prevs = prev.h;
var cell_prevs = prev.c;
}
var hidden = [];
var cell = [];
for(var d=0;d<hidden_sizes.length;d++) {
var input_vector = d === 0 ? x : hidden[d-1];
var hidden_prev = hidden_prevs[d];
var cell_prev = cell_prevs[d];
// input gate
var h0 = Graph.code.mul(G,model['Wix'+d], input_vector);
var h1 = Graph.code.mul(G,model['Wih'+d], hidden_prev);
var input_gate = Graph.code.sigmoid(G,Graph.code.add(G,Graph.code.add(G,h0,h1),
model['bi'+d]));
// forget gate
var h2 = Graph.code.mul(G,model['Wfx'+d], input_vector);
var h3 = Graph.code.mul(G,model['Wfh'+d], hidden_prev);
var forget_gate = Graph.code.sigmoid(
G,Graph.code.add(G,Graph.code.add(G,h2, h3),
model['bf'+d]));
// output gate
var h4 = Graph.code.mul(G,model['Wox'+d], input_vector);
var h5 = Graph.code.mul(G,model['Woh'+d], hidden_prev);
var output_gate = Graph.code.sigmoid(G,Graph.code.add(G,Graph.code.add(G,h4, h5),
model['bo'+d]));
// write operation on cells
var h6 = Graph.code.mul(G,model['Wcx'+d], input_vector);
var h7 = Graph.code.mul(G,model['Wch'+d], hidden_prev);
var cell_write = Graph.code.tanh(G,Graph.code.add(
G,Graph.code.add(G,h6, h7),
model['bc'+d]));
// compute new cell activation
var retain_cell = Graph.code.eltmul(G,forget_gate, cell_prev); // what do we keep from cell
var write_cell = Graph.code.eltmul(G,input_gate, cell_write); // what do we write to cell
var cell_d = Graph.code.add(G,retain_cell, write_cell); // new cell contents
// compute hidden state as gated, saturated cell activations
var hidden_d = Graph.code.eltmul(G, output_gate, Graph.code.tanh(G,cell_d));
hidden.push(hidden_d);
cell.push(cell_d);
}
// one decoder to outputs at end
var output = Graph.code.add(G,Graph.code.mul(G,model['Whd'], hidden[hidden.length - 1]),model['bd']);
// return cell memory, hidden representation and output
return {'h':hidden, 'c':cell, 'o' : output};
}
var sig = function(x) {
// helper function for computing sigmoid
return 1.0/(1+Math.exp(-x));
}
var maxi = function(w) {
// argmax of array w
var maxv = w[0];
var maxix = 0;
for(var i=1,n=w.length;i<n;i++) {
var v = w[i];
if(v > maxv) {
maxix = i;
maxv = v;
}
}
return maxix;
}
var samplei = function(w) {
// sample argmax from w, assuming w are
// probabilities that sum to one
var r = randf(0,1);
var x = 0.0;
var i = 0;
while(true) {
x += w[i];
if(x > r) { return i; }
i++;
}
return w.length - 1; // pretty sure we should never get here?
}
// various utils
module.exports.assert = assert;
module.exports.zeros = zeros;
module.exports.maxi = maxi;
module.exports.samplei = samplei;
module.exports.randi = randi;
module.exports.randn = randn;
module.exports.softmax = softmax;
// classes
module.exports.Mat = Mat;
module.exports.RandMat = RandMat;
module.exports.forwardLSTM = forwardLSTM;
module.exports.initLSTM = initLSTM;
// more utils
module.exports.updateMat = updateMat;
module.exports.updateNet = updateNet;
module.exports.copyMat = copyMat;
module.exports.copyNet = copyNet;
module.exports.netToJSON = netToJSON;
module.exports.netFromJSON = netFromJSON;
module.exports.netZeroGrads = netZeroGrads;
module.exports.netFlattenGrads = netFlattenGrads;
// optimization
module.exports.Solver = Solver;
module.exports.Graph = Graph;
// END OF RECURRENTJS
var RL = module.exports;
// syntactic sugar function for getting default parameter values
var getopt = function(opt, field_name, default_value) {
if(typeof opt === 'undefined') { return default_value; }
return (typeof opt[field_name] !== 'undefined') ? opt[field_name] : default_value;
}
var zeros = R.zeros; // inherit these
var assert = R.assert;
var randi = R.randi;
var randf = R.randf;
var setConst = function(arr, c) {
for(var i=0,n=arr.length;i<n;i++) {
arr[i] = c;
}
}
var sampleWeighted = function(p) {
var r = Math.random();
var c = 0.0;
for(var i=0,n=p.length;i<n;i++) {
c += p[i];
if(c >= r) { return i; }
}
// assert(false, 'sampleWeighted: Invalid samples '+Io.inspect(p));
return 0
}
// ------
// AGENTS
// ------
// DPAgent performs Value Iteration
// - can also be used for Policy Iteration if you really wanted to
// - requires model of the environment :(
// - does not learn from experience :(
// - assumes finite MDP :(
var DPAgent = function(env, opt) {
var L={};
L.V = null; // state value function
L.P = null; // policy distribution \pi(s,a)
L.env = env; // store pointer to environment
L.gamma = getopt(opt, 'gamma', 0.75); // future reward discount factor
DPAgent.code.reset(L);
return L;
}
DPAgent.code = {
reset: function(L) {
// reset the agent's policy and value function
L.ns = L.env.getNumStates();
L.na = L.env.getMaxNumActions();
L.V = zeros(L.ns);
L.P = zeros(L.ns * L.na);
// initialize uniform random policy
for(var s=0;s<L.ns;s++) {
var poss = L.env.allowedActions(s);
for(var i=0,n=poss.length;i<n;i++) {
L.P[poss[i]*L.ns+s] = 1.0 / poss.length;
}
}
},
act: function(L,s) {
// behave according to the learned policy
var poss = L.env.allowedActions(s);
var ps = [];
for(var i=0,n=poss.length;i<n;i++) {
var a = poss[i];
var prob = L.P[a*L.ns+s];
ps.push(prob);
}
var maxi = sampleWeighted(ps);
return poss[maxi];
},
learn: function(L) {
// perform a single round of value iteration
DPAgent.code.evaluatePolicy(L); // writes this.V
DPAgent.code.updatePolicy(L); // writes this.P
},
evaluatePolicy: function(L) {
// perform a synchronous update of the value function
var Vnew = zeros(L.ns);
for(var s=0;s<L.ns;s++) {
// integrate over actions in a stochastic policy
// note that we assume that policy probability mass over allowed actions sums to one
var v = 0.0;
var poss = L.env.allowedActions(s);
for(var i=0,n=poss.length;i<n;i++) {
var a = poss[i];
var prob = L.P[a*L.ns+s]; // probability of taking action under policy
if(prob === 0) { continue; } // no contribution, skip for speed
var ns = L.env.nextState(s,a);
var rs = L.env.reward(s,a,ns); // reward for s->a->ns transition
v += prob * (rs + L.gamma * L.V[ns]);
}
Vnew[s] = v;
}
L.V = Vnew; // swap
},
updatePolicy: function(L) {
// update policy to be greedy w.r.t. learned Value function
for(var s=0;s<L.ns;s++) {
var poss = L.env.allowedActions(s);
// compute value of taking each allowed action
var vmax, nmax;
var vs = [];
for(var i=0,n=poss.length;i<n;i++) {
var a = poss[i];
var ns = L.env.nextState(s,a);
var rs = L.env.reward(s,a,ns);
var v = rs + L.gamma * L.V[ns];
vs.push(v);
if(i === 0 || v > vmax) { vmax = v; nmax = 1; }
else if(v === vmax) { nmax += 1; }
}
// update policy smoothly across all argmaxy actions
for(var i=0,n=poss.length;i<n;i++) {
var a = poss[i];
L.P[a*L.ns+s] = (vs[i] === vmax) ? 1.0/nmax : 0.0;
}
}
},
}
// QAgent uses TD (Q-Learning, SARSA)
// - does not require environment model :)
// - learns from experience :)
var TDAgent = function(env, opt) {
var L={}
L.update = getopt(opt, 'update', 'qlearn'); // qlearn | sarsa
L.gamma = getopt(opt, 'gamma', 0.75); // future reward discount factor
L.epsilon = getopt(opt, 'epsilon', 0.1); // for epsilon-greedy policy
L.alpha = getopt(opt, 'alpha', 0.01); // value function learning rate
// class allows non-deterministic policy, and smoothly regressing towards the optimal policy based on Q
L.smooth_policy_update = getopt(opt, 'smooth_policy_update', false);
L.beta = getopt(opt, 'beta', 0.01); // learning rate for policy, if smooth updates are on
// eligibility traces
L.lambda = getopt(opt, 'lambda', 0); // eligibility trace decay. 0 = no eligibility traces used
L.replacing_traces = getopt(opt, 'replacing_traces', true);
// optional optimistic initial values
L.q_init_val = getopt(opt, 'q_init_val', 0);
L.planN = getopt(opt, 'planN', 0); // number of planning steps per learning iteration (0 = no planning)
L.Q = null; // state action value function
L.P = null; // policy distribution \pi(s,a)
L.e = null; // eligibility trace
L.env_model_s = null;; // environment model (s,a) -> (s',r)
L.env_model_r = null;; // environment model (s,a) -> (s',r)
L.env = env; // store pointer to environment
TDAgent.code.reset(L);
return L;
}
TDAgent.code = {
reset: function(L){
// reset the agent's policy and value function
L.ns = L.env.getNumStates();
L.na = L.env.getMaxNumActions();
L.Q = zeros(L.ns * L.na);
if(L.q_init_val !== 0) { setConst(L.Q, L.q_init_val); }
L.P = zeros(L.ns * L.na);
L.e = zeros(L.ns * L.na);
// model/planning vars
L.env_model_s = zeros(L.ns * L.na);
setConst(L.env_model_s, -1); // init to -1 so we can test if we saw the state before
L.env_model_r = zeros(L.ns * L.na);
L.sa_seen = [];
L.pq = zeros(L.ns * L.na);
// initialize uniform random policy
for(var s=0;s<L.ns;s++) {
var poss = L.env.allowedActions(s);
for(var i=0,n=poss.length;i<n;i++) {
L.P[poss[i]*L.ns+s] = 1.0 / poss.length;
}
}
// agent memory, needed for streaming updates
// (s0,a0,r0,s1,a1,r1,...)
L.r0 = null;
L.s0 = null;
L.s1 = null;
L.a0 = null;
L.a1 = null;
},
resetEpisode: function(L) {
// an episode finished
},
act: function(L,s){
// act according to epsilon greedy policy
var poss = L.env.allowedActions(s);
var probs = [];
for(var i=0,n=poss.length;i<n;i++) {
probs.push(L.P[poss[i]*L.ns+s]);
}
// epsilon greedy policy
if(Math.random() < L.epsilon) {
var a = poss[randi(0,poss.length)]; // random available action
L.explored = true;
} else {
var a = poss[sampleWeighted(probs)];
L.explored = false;
}
// shift state memory
L.s0 = L.s1;
L.a0 = L.a1;
L.s1 = s;
L.a1 = a;
return a;
},
learn: function(L,r1){
// takes reward for previous action, which came from a call to act()
if(!(L.r0 == null)) {
TDAgent.code.learnFromTuple(L, L.s0, L.a0, L.r0, L.s1, L.a1, L.lambda);
if(L.planN > 0) {
TDAgent.code.updateModel(L, L.s0, L.a0, L.r0, L.s1);
TDAgent.code.plan(L);
}
}
L.r0 = r1; // store this for next update
},
updateModel: function(L, s0, a0, r0, s1) {
// transition (s0,a0) -> (r0,s1) was observed. Update environment model
var sa = a0 * L.ns + s0;
if(L.env_model_s[sa] === -1) {
// first time we see this state action
L.sa_seen.push(a0 * L.ns + s0); // add as seen state
}
L.env_model_s[sa] = s1;
L.env_model_r[sa] = r0;
},
plan: function(L) {
// order the states based on current priority queue information
var spq = [];
for(var i=0,n=L.sa_seen.length;i<n;i++) {
var sa = L.sa_seen[i];
var sap = L.pq[sa];
if(sap > 1e-5) { // gain a bit of efficiency
spq.push({sa:sa, p:sap});
}
}
spq.sort(function(a,b){ return a.p < b.p ? 1 : -1});
// perform the updates
var nsteps = Math.min(L.planN, spq.length);
for(var k=0;k<nsteps;k++) {
// random exploration
//var i = randi(0, this.sa_seen.length); // pick random prev seen state action
//var s0a0 = this.sa_seen[i];
var s0a0 = spq[k].sa;
L.pq[s0a0] = 0; // erase priority, since we're backing up this state
var s0 = s0a0 % L.ns;
var a0 = Math.floor(s0a0 / L.ns);
var r0 = L.env_model_r[s0a0];
var s1 = L.env_model_s[s0a0];
var a1 = -1; // not used for Q learning
if(L.update === 'sarsa') {
// generate random action?...
var poss = L.env.allowedActions(s1);
var a1 = poss[randi(0,poss.length)];
}
TDAgent.code.learnFromTuple(L, s0, a0, r0, s1, a1, 0); // note lambda = 0 - shouldnt use eligibility trace here
}
},
learnFromTuple: function(L, s0, a0, r0, s1, a1, lambda) {
var sa = a0 * L.ns + s0;
// calculate the target for Q(s,a)
if(L.update === 'qlearn') {
// Q learning target is Q(s0,a0) = r0 + gamma * max_a Q[s1,a]
var poss = L.env.allowedActions(s1);
var qmax = 0;
for(var i=0,n=poss.length;i<n;i++) {
var s1a = poss[i] * L.ns + s1;
var qval = L.Q[s1a];
if(i === 0 || qval > qmax) { qmax = qval; }
}
var target = r0 + L.gamma * qmax;
} else if(L.update === 'sarsa') {
// SARSA target is Q(s0,a0) = r0 + gamma * Q[s1,a1]
var s1a1 = a1 * L.ns + s1;
var target = r0 + L.gamma * L.Q[s1a1];
}
if(lambda > 0) {
// perform an eligibility trace update
if(L.replacing_traces) {
L.e[sa] = 1;
} else {
L.e[sa] += 1;
}
var edecay = lambda * L.gamma;
var state_update = zeros(L.ns);
for(var s=0;s<L.ns;s++) {
var poss = L.env.allowedActions(s);
for(var i=0;i<poss.length;i++) {
var a = poss[i];
var saloop = a * L.ns + s;
var esa = L.e[saloop];
var update = L.alpha * esa * (target - L.Q[saloop]);
L.Q[saloop] += update;
L.updatePriority(s, a, update);
L.e[saloop] *= edecay;
var u = Math.abs(update);
if(u > state_update[s]) { state_update[s] = u; }
}
}
for(var s=0;s<L.ns;s++) {
if(state_update[s] > 1e-5) { // save efficiency here
TDAgent.code.updatePolicy(L,s);
}
}
if(L.explored && L.update === 'qlearn') {
// have to wipe the trace since q learning is off-policy :(
L.e = zeros(L.ns * L.na);
}
} else {
// simpler and faster update without eligibility trace
// update Q[sa] towards it with some step size
var update = L.alpha * (target - L.Q[sa]);
L.Q[sa] += update;
TDAgent.code.updatePriority(L,s0, a0, update);
// update the policy to reflect the change (if appropriate)
TDAgent.code.updatePolicy(L,s0);
}
},
updatePriority: function(L,s,a,u) {
// used in planning. Invoked when Q[sa] += update
// we should find all states that lead to (s,a) and upgrade their priority
// of being update in the next planning step
u = Math.abs(u);
if(u < 1e-5) { return; } // for efficiency skip small updates
if(L.planN === 0) { return; } // there is no planning to be done, skip.
for(var si=0;si<L.ns;si++) {
// note we are also iterating over impossible actions at all states,
// but this should be okay because their env_model_s should simply be -1
// as initialized, so they will never be predicted to point to any state
// because they will never be observed, and hence never be added to the model
for(var ai=0;ai<L.na;ai++) {
var siai = ai * L.ns + si;
if(L.env_model_s[siai] === s) {
// this state leads to s, add it to priority queue
L.pq[siai] += u;
}
}
}
},
updatePolicy: function(L,s) {
var poss = L.env.allowedActions(s);
// set policy at s to be the action that achieves max_a Q(s,a)
// first find the maxy Q values
var qmax, nmax;
var qs = [];
for(var i=0,n=poss.length;i<n;i++) {
var a = poss[i];
var qval = L.Q[a*L.ns+s];
qs.push(qval);
if(i === 0 || qval > qmax) { qmax = qval; nmax = 1; }
else if(qval === qmax) { nmax += 1; }
}
// now update the policy smoothly towards the argmaxy actions
var psum = 0.0;
for(var i=0,n=poss.length;i<n;i++) {
var a = poss[i];
var target = (qs[i] === qmax) ? 1.0/nmax : 0.0;
var ix = a*L.ns+s;
if(L.smooth_policy_update) {
// slightly hacky :p
L.P[ix] += L.beta * (target - L.P[ix]);
psum += L.P[ix];
} else {
// set hard target
L.P[ix] = target;
}
}
if(L.smooth_policy_update) {
// renomalize P if we're using smooth policy updates
for(var i=0,n=poss.length;i<n;i++) {
var a = poss[i];
L.P[a*L.ns+s] /= psum;
}
}
}
}
var DQNAgent = function(env, opt) {
var L = {}
L.gamma = getopt(opt, 'gamma', 0.75); // future reward discount factor
L.epsilon = getopt(opt, 'epsilon', 0.1); // for epsilon-greedy policy
L.alpha = getopt(opt, 'alpha', 0.01); // value function learning rate
L.experience_add_every = getopt(opt, 'experience_add_every', 25); // number of time steps before we add another experience to replay memory
L.experience_size = getopt(opt, 'experience_size', 5000); // size of experience replay
L.learning_steps_per_iteration = getopt(opt, 'learning_steps_per_iteration', 10);
L.tderror_clamp = getopt(opt, 'tderror_clamp', 1.0);
L.num_hidden_units = getopt(opt, 'num_hidden_units', 100);
L.env = env;
DQNAgent.code.reset(L);
return L
}
DQNAgent.code = {
reset: function(L) {
L.nh = L.num_hidden_units; // number of hidden units
L.ns = L.env.getNumStates();
L.na = L.env.getMaxNumActions();
// nets are hardcoded for now as key (str) -> Mat
// not proud of this. better solution is to have a whole Net object
// on top of Mats, but for now sticking with this
L.net = {};
L.net.W1 = R.RandMat(L.nh, L.ns, 0, 0.01);
L.net.b1 = R.Mat(L.nh, 1, 0, 0.01);
L.net.W2 = R.RandMat(L.na, L.nh, 0, 0.01);
L.net.b2 = R.Mat(L.na, 1, 0, 0.01);
L.exp = []; // experience
L.expi = 0; // where to insert
L.t = 0;
L.r0 = null;
L.s0 = null;
L.s1 = null;
L.a0 = null;
L.a1 = null;
L.tderror = 0; // for visualization only...
},
toJSON: function(L) {
// save function
var j = {};
j.nh = L.nh;
j.ns = L.ns;
j.na = L.na;
j.net = R.netToJSON(L.net);
return j;
},
fromJSON: function(L,j) {
// load function
L.nh = j.nh;
L.ns = j.ns;
L.na = j.na;
L.net = R.netFromJSON(j.net);
},
forwardQ: function(L, net, s, needs_backprop) {
var G = R.Graph(needs_backprop);
var a1mat = Graph.code.add(G,Graph.code.mul(G,net.W1, s), net.b1);
var h1mat = Graph.code.tanh(G,a1mat);
var a2mat = Graph.code.add(G,Graph.code.mul(G,net.W2, h1mat), net.b2);
L.lastG = G; // back this up. Kind of hacky isn't it
return a2mat;
},
act: function(L,slist) {
// convert to a Mat column vector
var s = R.Mat(L.ns, 1);
Mat.code.setFrom(s,slist);
// epsilon greedy policy
if(Math.random() < L.epsilon) {
var a = randi(0, L.na);
} else {
// greedy wrt Q function
var amat = DQNAgent.code.forwardQ(L,L.net, s, false);
var a = R.maxi(amat.w); // returns index of argmax action
}
// shift state memory
L.s0 = L.s1;
L.a0 = L.a1;
L.s1 = s;
L.a1 = a;
return a;
},
learn: function(L,r1) {
// perform an update on Q function
if(!(L.r0 == null) && L.alpha > 0) {
// learn from this tuple to get a sense of how "surprising" it is to the agent
var tderror = DQNAgent.code.learnFromTuple(L, L.s0, L.a0, L.r0, L.s1, L.a1);
L.tderror = tderror; // a measure of surprise
// decide if we should keep this experience in the replay
if(L.t % L.experience_add_every === 0) {
L.exp[L.expi] = [L.s0, L.a0, L.r0, L.s1, L.a1];
L.expi += 1;
if(L.expi > L.experience_size) { L.expi = 0; } // roll over when we run out
}
L.t += 1;
// sample some additional experience from replay memory and learn from it
for(var k=0;k<L.learning_steps_per_iteration;k++) {
var ri = randi(0, L.exp.length); // todo: priority sweeps?
var e = L.exp[ri];
DQNAgent.code.learnFromTuple(L, e[0], e[1], e[2], e[3], e[4])
}
}
L.r0 = r1; // store for next update
},
learnFromTuple: function(L, s0, a0, r0, s1, a1) {
// want: Q(s,a) = r + gamma * max_a' Q(s',a')
// compute the target Q value
var tmat = DQNAgent.code.forwardQ(L, L.net, s1, false);
var qmax = r0 + L.gamma * tmat.w[R.maxi(tmat.w)];
// now predict
var pred = DQNAgent.code.forwardQ(L, L.net, s0, true);
var tderror = pred.w[a0] - qmax;
var clamp = L.tderror_clamp;
if(Math.abs(tderror) > clamp) { // huber loss to robustify
if(tderror > clamp) tderror = clamp;
if(tderror < -clamp) tderror = -clamp;
}
pred.dw[a0] = tderror;
Graph.code.backward( L.lastG); // compute gradients on net params
// update net
R.updateNet(L.net, L.alpha);
return tderror;
}
}
// exports
module.exports.DPAgent = DPAgent;
module.exports.TDAgent = TDAgent;
module.exports.DQNAgent = DQNAgent;
//module.exports.SimpleReinforceAgent = SimpleReinforceAgent;
//module.exports.RecurrentReinforceAgent = RecurrentReinforceAgent;
//module.exports.DeterministPG = DeterministPG;
};
BundleModuleCode['ml/stats']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $CREATED: (C) 2006-2020 bLAB by sbosse
** $VERSION: 1.1.8
**
** $INFO:
**
** ML Data Statistics and Utils
**
** New:
** type eps = number | number []
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
///////// UTILS ////////////
var stat = {
max: function(array) {
return Math.max.apply(null, array);
},
min: function(array) {
return Math.min.apply(null, array);
},
range: function(array) {
return stat.max(array) - stat.min(array);
},
midrange: function(array) {
return stat.range(array) / 2;
},
sum: function(array) {
var num = 0;
for (var i = 0, l = array.length; i < l; i++) num += array[i];
return num;
},
mean: function(array) {
return stat.sum(array) / array.length;
},
median: function(array) {
array.sort(function(a, b) {
return a - b;
});
var mid = array.length / 2;
return mid % 1 ? array[mid - 0.5] : (array[mid - 1] + array[mid]) / 2;
},
modes: function(array) {
if (!array.length) return [];
var modeMap = {},
maxCount = 0,
modes = [];
array.forEach(function(val) {
if (!modeMap[val]) modeMap[val] = 1;
else modeMap[val]++;
if (modeMap[val] > maxCount) {
modes = [val];
maxCount = modeMap[val];
}
else if (modeMap[val] === maxCount) {
modes.push(val);
maxCount = modeMap[val];
}
});
return modes;
},
variance: function(array) {
var mean = stat.mean(array);
return stat.mean(array.map(function(num) {
return Math.pow(num - mean, 2);
}));
},
standardDeviation: function(array) {
return Math.sqrt(stat.variance(array));
},
meanAbsoluteDeviation: function(array) {
var mean = stat.mean(array);
return stat.mean(array.map(function(num) {
return Math.abs(num - mean);
}));
},
zScores: function(array) {
var mean = stat.mean(array);
var standardDeviation = stat.standardDeviation(array);
return array.map(function(num) {
return (num - mean) / standardDeviation;
});
}
};
// Function aliases:
stat.average = stat.mean;
// function ({$x:number}|{value:*,prob;number}[]|number [],boolean)
// -> {value:*,prob:number}|{index:number, prob:number}
// normalize=1: scale output max=[0,1]
// normalize=2: scale and weight output max*[0,1]
function best(o,normalize) {
var p,max,pos=0,sum=0,res;
if (Comp.obj.isArray(o) && typeof o[0]=='number') {
max=-Infinity;
for(p in o) {
sum += o[p];
if (o[p] > max) max=o[p],pos=p;
}
res = {index:pos,prob:max}
} else if (Comp.obj.isArray(o) && typeof o[0]=='object') {
for(p in o) {
sum += o[p].prob;
if (!max || o[p].prob>max.prob) max=o[p];
}
res = {value:max.value,prob:max.prob}
} else if (Comp.obj.isObj(o)) {
max=-Infinity;
for(p in o) {
sum += o[p];
if (o[p]>max) max=o[p],pos=p;
}
res = {value:pos,prob:max}
}
if (!res) return;
switch (normalize) {
case 1: res.prob=res.prob/sum; break;
case 2: res.prob=res.prob*(res.prob/sum); break;
default:
}
return res;
}
function bestNormalize(o) { return best(o,1) }
function log2(n) {
return Math.log(n) / Math.log(2);
}
// Select maximal value of an array by values
// retuned by optional function applied to array values
function max(array,fun) {
var res,max,num;
for(var i in array) {
if (fun) num=fun(array[i],i); else num=array[i];
if (max==undefined) { max=num; res=array[i] }
else if (num > max) { max=num; res=array[i] }
}
return res;
}
/**
* Finds element with highest occurrence in a list
* @private
*/
function mostCommon(list) {
var elementFrequencyMap = {};
var largestFrequency = -1;
var mostCommonElement = null;
list.forEach(function(element) {
var elementFrequency = (elementFrequencyMap[element] || 0) + 1;
elementFrequencyMap[element] = elementFrequency;
if (largestFrequency < elementFrequency) {
mostCommonElement = element;
largestFrequency = elementFrequency;
}
});
return mostCommonElement;
}
function pluck(collection, key) {
return collection.map(function(object) {
return object == null ? undefined : object[key];
});
}
function prob(value, list) {
var occurrences = list.filter(function(element) {
return element === value
});
var numOccurrences = occurrences.length;
var numElements = list.length;
return numOccurrences / numElements;
}
function sort(array) {
return array.sort(function (a,b) { return a<b?-1:1 });
}
function sum (a,b) { return a+b }
function unique(array) {
var length = array ? array.length : 0;
function baseUniq(array) {
var index = -1,
length = array.length,
seen,
result = [];
seen = result;
outer:
while (++index < length) {
var value = array[index];
var seenIndex = seen.length;
while (seenIndex--) {
if (seen[seenIndex] === value) {
continue outer;
}
}
result.push(value);
}
return result;
}
if (!length) {
return [];
}
return baseUniq(array);
}
function without () {
var array,
values=[];
for(var i in arguments) {
if (i==0) array=arguments[0];
else values.push(arguments[i]);
}
return array.filter(function (e) {
return values.indexOf(e) == -1;
});
}
////////////////////////////////////////
// Entropy of data vectors
function entropy(vals) {
var uniqueVals = unique(vals);
var probs = uniqueVals.map(function(x) {
return prob(x, vals)
});
var logVals = probs.map(function(p) {
return -p * log2(p)
});
return logVals.reduce(sum,0);
}
function entropyN(dist,N) {
var p, probs=[];
for(p in dist) probs.push(dist[p]/N);
var logVals = probs.map(function(p) {
return p==0?0:-p * log2(p)
});
return logVals.reduce(sum, 0);
}
function entropyEps(vals,eps) {
var uniqueVals = uniqueEps(vals,eps);
var probs = uniqueVals.map(function(x) {
return probEps(x, vals, eps)
});
var logVals = probs.map(function(p) {
return -p * log2(p)
});
return logVals.reduce(sum, 0);
}
// Entropy of target variable partitioned feature vector
function entropyT(data,featureIndex,targetIndex,targets) {
var en = 0;
var col = pluck(data,featureIndex);
var uniqueVals = unique(col);
uniqueVals.forEach(function (v) {
var frac = targets.map(function () { return 0 }),
cn=0;
col.forEach (function (v2,row) {
if (v2==v) cn++,frac[targets.indexOf(data[row][targetIndex])]++;
})
var p = cn/data.length;
en += (p*entropyN(frac,frac.reduce(sum)))
// print(frac,p,frac.reduce(sum))
})
return en;
}
function entropyTEps(data,feature,target,targets,eps) {
var en = 0;
var col = pluck(data,feature);
var uniqueVals = uniqueEps(col,eps);
uniqueVals.forEach(function (v) {
var frac = targets.map(function () { return 0 }),
cn=0;
col.forEach (function (v2,row) {
if (v2>=v-eps && v2<=v+eps) cn++,frac[targets.indexOf(data[row][target])]++;
})
var p = cn/data.length;
en += (p*entropyN(frac,frac.reduce(sum)))
// print(frac,p,frac.reduce(sum))
})
return en;
}
function features (data,target) {
var f;
if (Comp.obj.isObj(data[0]))
f=Object.keys(data[0]);
else if (Comp.obj.isArray(data[0]))
f=data[0].map(function (x,i) { return String(i) });
if (f && target) delete f[target];
return f;
}
function gainEps(data,feature,target,targets,eps) {
var et = entropy(pluck(data,target));
return et/entropyTEps(data,feature,target,targets,eps)
}
function maxGainEps(data,features,target,targets,eps) {
var maxgain=max(features, function(feature,index) {
var g = gainEps(data,feature,target,targets,selectEps(eps,index));
return g;
});
return maxgain;
}
function partition(data,feature,target,targets) {
var parts={};
targets.forEach(function (t) {parts[t]=[]});
data.forEach(function (row) {
parts[row[target]].push(row[feature]);
})
return parts
}
function partitionEps(data,feature,target,targets,eps) {
var p,parts={}
targets.forEach(function (t) {parts[t]={range:[Number.MAX_VALUE,-Number.MAX_VALUE],values:[]}});
data.forEach(function (row) {
parts[row[target]].values.push(row[feature]);
parts[row[target]].range[0]=Math.min(parts[row[target]].range[0],row[feature]);
parts[row[target]].range[1]=Math.max(parts[row[target]].range[1],row[feature]);
})
for(p in parts) {
parts[p].unique=uniqueEps(parts[p].values,eps)
parts[p].noise=2*stat.standardDeviation(parts[p].values);
}
return parts
}
// Return only eps-not-overlapping parititions - the most significant are selected
// (with the lowest unique column values)
function partitionUniqueEps(data,feature,target,targets,eps) {
var p, q, parts={}
// 1. Create all partitions
targets.forEach(function (t) {parts[t]={range:[Number.MAX_VALUE,-Number.MAX_VALUE],values:[]}});
data.forEach(function (row) {
parts[row[target]].values.push(row[feature]);
parts[row[target]].range[0]=Math.min(parts[row[target]].range[0],row[feature]);
parts[row[target]].range[1]=Math.max(parts[row[target]].range[1],row[feature]);
})
for(p in parts) {
parts[p].unique=uniqueEps(parts[p].values,eps)
}
// 2. Remove overlapping partitions
for(p in parts) {
if (!parts[p]) continue;
for (q in parts) {
if (!parts[p]) break;
if (p==q || !parts[q]) continue;
if ((parts[p].range[0]-eps)<parts[q].range[1] ||
(parts[p].range[1]+eps)>parts[q].range[0]) {
// overlapping, select the part with best unique column values
if ((parts[p].unique.length/parts[p].values.length)<
(parts[q].unique.length/parts[q].values.length)) {
//print('delete '+q)
delete parts[q];
} else {
//print('delete '+p)
delete parts[p];
}
}
}
}
return parts
}
function select (data,what) {
if (Comp.obj.isArray(what) && what.length==2) {
var c0=what[0],c1=what[1];
return data.map(function (row) {
return row.slice(c0,c1+1);
})
}
}
function selectEps (eps,index) {
if (typeof eps == 'number') return eps;
else return eps[index]
}
/** Split a data set by finding the best feature (column)
* based on maximal gain/entropy calculation of columns.
* type eps = number | number []
*/
function splitEps (data,features,target,targets,eps) {
var bestFeature = maxGainEps(data,features,target,targets,eps);
var index = features.indexOf(bestFeature);
eps = selectEps(eps,index);
var remainingFeatures = without(features, bestFeature);
var possibleValues = sort(uniqueEps(pluck(data, bestFeature),eps));
var choices = possibleValues.map( function(v) {
var dataS = data.filter(function(x) {
return Math.abs(x[bestFeature] - v) <= eps
});
return {
val:v,
data:dataS,
}
});
return {
feature:bestFeature,
choices:choices,
possibleValues:possibleValues,
remainingFeatures:remainingFeatures
};
}
function uniqueEps(array,eps) {
var result=[];
array.forEach(function (x) {
var found;
if (!result.length) result.push(x);
else {
result.forEach(function (y) {
if (found) return;
found = Math.abs(x-y)<=eps;
});
if (!found) result.push(x);
}
});
return result;
}
module.exports = {
analyze : function (data,features,target,eps) {
var noise=[];
if (!eps) eps=0;
var targets = unique(pluck(data,target));
var parts = {}, partsUnique = {},diversity={}
features.forEach(function (feature) {
partsUnique[feature]=partitionUniqueEps(data,feature,target,targets,eps);
parts[feature]=partitionEps(data,feature,target,targets,eps);
for(var p in parts[feature]) noise.push(parts[feature][p].noise);
})
features.forEach(function (feature) {
diversity[feature]=Object.keys(partsUnique[feature]).length;
})
return {
features:features,
partitions:parts, // for each data column
diversity:diversity,
noise:stat.mean(noise)
}
},
entropy:entropy,
entropyN:entropyN,
entropyEps:entropyEps,
entropyTEps:entropyTEps,
entropyT:entropyT,
features:features,
gainEps:gainEps,
maxGainEps:maxGainEps,
mostCommon:mostCommon,
partition:partition,
partitionEps:partitionEps,
partitionUniqueEps:partitionUniqueEps,
splitEps:splitEps,
unique:unique,
uniqueEps:uniqueEps,
utils : {
// return column by key of a matrix (array array|record array)
best:best,
bestNormalize:bestNormalize,
column:pluck,
log2:log2,
prob:prob,
// transform [v][] -> v[]
relax: function (mat) {
if (Comp.obj.isMatrix(mat) && mat[0].length==1) return mat.map(function (row) { return row[0]})
else return mat;
},
select:select,
selectEps:selectEps,
sort:sort,
stat:stat,
without:without,
// transform v[] -> [v][]
wrap: function (mat) {
if (!Comp.obj.isMatrix(mat)) return mat.map(function (v) { return [v]})
else return mat
},
},
};
};
BundleModuleCode['ml/cnn']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $CREATED: (C) 2006-2019 bLAB by sbosse
** $VERSION: 1.1.1
**
** $INFO:
**
** Convolutional neural network ML Algorithm
**
** Incremental learner using ml.update! Initial training data via ml.learn (or empty data set)
**
** $ENDOFINFO
*/
'use strict';
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios=none;
var convnetjs = Require('ml/convnet')
var that;
that = module.exports = {
// typeof options = {x:[][],y:[],width,height,depth,normalize?:[a,b],layers:{}[]..}
// format x = [ [row1=[col1=[z1,z2,..],col2,..],row2,..] ]
create : function (options) {
var net = new convnetjs.Net();
if (options.layers)
net.makeLayers(options.layers);
if (!options.iterations) options.iterations=10;
if (!options.depth) options.depth=1;
if (!options.width) options.width=options.x[0].length,options.height=1;
var trainer = new convnetjs.SGDTrainer(net, options.trainer||
{method: 'adadelta',
l2_decay: 0.001,
batch_size: 10});
// convert matrix (2dim/3dim) to volume elements
var x = options.x;
if (options.normalize) {
var a,b,
c=options.normalize[0],
d=options.normalize[1];
x.forEach(function (row) {
var min=Math.min.apply(null,row),
max=Math.max.apply(null,row);
if (a==undefined) a=min; else a=Math.min(a,min);
if (b==undefined) b=max; else b=Math.max(b,max);
})
x=x.map(function (row) {
return row.map(function (col) { return (((col-a)/(b-a))*(d-c))+c }) // scale [0,1] -> [c,d]
})
}
x=x.map(function (row) {
var vol = new convnetjs.Vol(options.width, options.height, options.depth, 0.0); //input volume (image)
vol.w = row;
return vol;
});
x.forEach (function (row) {
//net.forward(row);
})
var y = options.y;
if (!options.targets) {
options.targets=that.ml.stats.unique(y);
}
for(var iters=0;iters<options.iterations;iters++) {
y.forEach(function (v,i) {
trainer.train(x[i],options.targets.indexOf(v));
})
}
trainer.options= {width:options.width,height:options.height,depth:options.depth,targets:options.targets};
return trainer;
},
ml:{},
predict: function (model,sample) {
var options = model.options;
var vol = new convnetjs.Vol(options.width, options.height, options.depth, 0.0); //input volume (image)
vol.w = sample;
return model.net.forward(vol);
},
print: function () {
},
update: function (data) {
},
current:function (module) { current=module.current; Aios=module;}
};
};
BundleModuleCode['ml/convnet']=function (module,exports){
/*** https://github.com/karpathy/convnetjs ***/
var convnet={REVISION: 'ALPHA'}
module.exports=convnet;
"use strict";
/*** convnet_util ***/
// Random number utilities
var return_v = false;
var v_val = 0.0;
var gaussRandom = function() {
if(return_v) {
return_v = false;
return v_val;
}
var u = 2*Math.random()-1;
var v = 2*Math.random()-1;
var r = u*u + v*v;
if(r == 0 || r > 1) return gaussRandom();
var c = Math.sqrt(-2*Math.log(r)/r);
v_val = v*c; // cache this
return_v = true;
return u*c;
}
var randf = function(a, b) { return Math.random()*(b-a)+a; }
var randi = function(a, b) { return Math.floor(Math.random()*(b-a)+a); }
var randn = function(mu, std){ return mu+gaussRandom()*std; }
// Array utilities
var zeros = function(n) {
if(typeof(n)==='undefined' || isNaN(n)) { return []; }
if(typeof ArrayBuffer === 'undefined') {
// lacking browser support
var arr = new Array(n);
for(var i=0;i<n;i++) { arr[i]= 0; }
return arr;
} else {
return new Float64Array(n);
}
}
var arrContains = function(arr, elt) {
for(var i=0,n=arr.length;i<n;i++) {
if(arr[i]===elt) return true;
}
return false;
}
var arrUnique = function(arr) {
var b = [];
for(var i=0,n=arr.length;i<n;i++) {
if(!arrContains(b, arr[i])) {
b.push(arr[i]);
}
}
return b;
}
// return max and min of a given non-empty array.
var maxmin = function(w) {
if(w.length === 0) { return {}; } // ... ;s
var maxv = w[0];
var minv = w[0];
var maxi = 0;
var mini = 0;
var n = w.length;
for(var i=1;i<n;i++) {
if(w[i] > maxv) { maxv = w[i]; maxi = i; }
if(w[i] < minv) { minv = w[i]; mini = i; }
}
return {maxi: maxi, maxv: maxv, mini: mini, minv: minv, dv:maxv-minv};
}
// create random permutation of numbers, in range [0...n-1]
var randperm = function(n) {
var i = n,
j = 0,
temp;
var array = [];
for(var q=0;q<n;q++)array[q]=q;
while (i--) {
j = Math.floor(Math.random() * (i+1));
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
// sample from list lst according to probabilities in list probs
// the two lists are of same size, and probs adds up to 1
var weightedSample = function(lst, probs) {
var p = randf(0, 1.0);
var cumprob = 0.0;
for(var k=0,n=lst.length;k<n;k++) {
cumprob += probs[k];
if(p < cumprob) { return lst[k]; }
}
}
// syntactic sugar function for getting default parameter values
var getopt = function(opt, field_name, default_value) {
if(typeof field_name === 'string') {
// case of single string
return (typeof opt[field_name] !== 'undefined') ? opt[field_name] : default_value;
} else {
// assume we are given a list of string instead
var ret = default_value;
for(var i=0;i<field_name.length;i++) {
var f = field_name[i];
if (typeof opt[f] !== 'undefined') {
ret = opt[f]; // overwrite return value
}
}
return ret;
}
}
function assert(condition, message) {
if (!condition) {
message = message || "Assertion failed";
if (typeof Error !== "undefined") {
throw new Error(message);
}
throw message; // Fallback
}
}
convnet.randf = randf;
convnet.randi = randi;
convnet.randn = randn;
convnet.zeros = zeros;
convnet.maxmin = maxmin;
convnet.randperm = randperm;
convnet.weightedSample = weightedSample;
convnet.arrUnique = arrUnique;
convnet.arrContains = arrContains;
convnet.getopt = getopt;
convnet.assert = assert;
/*** convnet_vol ***/
// Vol is the basic building block of all data in a net.
// it is essentially just a 3D volume of numbers, with a
// width (sx), height (sy), and depth (depth).
// it is used to hold data for all filters, all volumes,
// all weights, and also stores all gradients w.r.t.
// the data. c is optionally a value to initialize the volume
// with. If c is missing, fills the Vol with random numbers.
var Vol = function(sx, sy, depth, c) {
// this is how you check if a variable is an array. Oh, Javascript :)
if(Object.prototype.toString.call(sx) === '[object Array]') {
// we were given a list in sx, assume 1D volume and fill it up
this.sx = 1;
this.sy = 1;
this.depth = sx.length;
// we have to do the following copy because we want to use
// fast typed arrays, not an ordinary javascript array
this.w = convnet.zeros(this.depth);
this.dw = convnet.zeros(this.depth);
for(var i=0;i<this.depth;i++) {
this.w[i] = sx[i];
}
} else {
// we were given dimensions of the vol
this.sx = sx;
this.sy = sy;
this.depth = depth;
var n = sx*sy*depth;
this.w = convnet.zeros(n);
this.dw = convnet.zeros(n);
if(typeof c === 'undefined') {
// weight normalization is done to equalize the output
// variance of every neuron, otherwise neurons with a lot
// of incoming connections have outputs of larger variance
var scale = Math.sqrt(1.0/(sx*sy*depth));
for(var i=0;i<n;i++) {
this.w[i] = convnet.randn(0.0, scale);
}
} else {
for(var i=0;i<n;i++) {
this.w[i] = c;
}
}
}
}
Vol.prototype = {
get: function(x, y, d) {
var ix=((this.sx * y)+x)*this.depth+d;
return this.w[ix];
},
set: function(x, y, d, v) {
var ix=((this.sx * y)+x)*this.depth+d;
this.w[ix] = v;
},
add: function(x, y, d, v) {
var ix=((this.sx * y)+x)*this.depth+d;
this.w[ix] += v;
},
get_grad: function(x, y, d) {
var ix = ((this.sx * y)+x)*this.depth+d;
return this.dw[ix];
},
set_grad: function(x, y, d, v) {
var ix = ((this.sx * y)+x)*this.depth+d;
this.dw[ix] = v;
},
add_grad: function(x, y, d, v) {
var ix = ((this.sx * y)+x)*this.depth+d;
this.dw[ix] += v;
},
cloneAndZero: function() { return new Vol(this.sx, this.sy, this.depth, 0.0)},
clone: function() {
var V = new Vol(this.sx, this.sy, this.depth, 0.0);
var n = this.w.length;
for(var i=0;i<n;i++) { V.w[i] = this.w[i]; }
return V;
},
addFrom: function(V) { for(var k=0;k<this.w.length;k++) { this.w[k] += V.w[k]; }},
addFromScaled: function(V, a) { for(var k=0;k<this.w.length;k++) { this.w[k] += a*V.w[k]; }},
setConst: function(a) { for(var k=0;k<this.w.length;k++) { this.w[k] = a; }},
toJSON: function() {
// todo: we may want to only save d most significant digits to save space
var json = {}
json.sx = this.sx;
json.sy = this.sy;
json.depth = this.depth;
json.w = this.w;
return json;
// we wont back up gradients to save space
},
fromJSON: function(json) {
this.sx = json.sx;
this.sy = json.sy;
this.depth = json.depth;
var n = this.sx*this.sy*this.depth;
this.w = convnet.zeros(n);
this.dw = convnet.zeros(n);
// copy over the elements.
for(var i=0;i<n;i++) {
this.w[i] = json.w[i];
}
}
}
convnet.Vol = Vol;
/*** convnet_vol_util ***/
var Vol = convnet.Vol; // convenience
// Volume utilities
// intended for use with data augmentation
// crop is the size of output
// dx,dy are offset wrt incoming volume, of the shift
// fliplr is boolean on whether we also want to flip left<->right
var augment = function(V, crop, dx, dy, fliplr) {
// note assumes square outputs of size crop x crop
if(typeof(fliplr)==='undefined') var fliplr = false;
if(typeof(dx)==='undefined') var dx = convnet.randi(0, V.sx - crop);
if(typeof(dy)==='undefined') var dy = convnet.randi(0, V.sy - crop);
// randomly sample a crop in the input volume
var W;
if(crop !== V.sx || dx!==0 || dy!==0) {
W = new Vol(crop, crop, V.depth, 0.0);
for(var x=0;x<crop;x++) {
for(var y=0;y<crop;y++) {
if(x+dx<0 || x+dx>=V.sx || y+dy<0 || y+dy>=V.sy) continue; // oob
for(var d=0;d<V.depth;d++) {
W.set(x,y,d,V.get(x+dx,y+dy,d)); // copy data over
}
}
}
} else {
W = V;
}
if(fliplr) {
// flip volume horziontally
var W2 = W.cloneAndZero();
for(var x=0;x<W.sx;x++) {
for(var y=0;y<W.sy;y++) {
for(var d=0;d<W.depth;d++) {
W2.set(x,y,d,W.get(W.sx - x - 1,y,d)); // copy data over
}
}
}
W = W2; //swap
}
return W;
}
// img is a DOM element that contains a loaded image
// returns a Vol of size (W, H, 4). 4 is for RGBA
var img_to_vol = function(img, convert_grayscale) {
if(typeof(convert_grayscale)==='undefined') var convert_grayscale = false;
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
// due to a Firefox bug
try {
ctx.drawImage(img, 0, 0);
} catch (e) {
if (e.name === "NS_ERROR_NOT_AVAILABLE") {
// sometimes happens, lets just abort
return false;
} else {
throw e;
}
}
try {
var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
if(e.name === 'IndexSizeError') {
return false; // not sure what causes this sometimes but okay abort
} else {
throw e;
}
}
// prepare the input: get pixels and normalize them
var p = img_data.data;
var W = img.width;
var H = img.height;
var pv = []
for(var i=0;i<p.length;i++) {
pv.push(p[i]/255.0-0.5); // normalize image pixels to [-0.5, 0.5]
}
var x = new Vol(W, H, 4, 0.0); //input volume (image)
x.w = pv;
if(convert_grayscale) {
// flatten into depth=1 array
var x1 = new Vol(W, H, 1, 0.0);
for(var i=0;i<W;i++) {
for(var j=0;j<H;j++) {
x1.set(i,j,0,x.get(i,j,0));
}
}
x = x1;
}
return x;
}
convnet.augment = augment;
convnet.img_to_vol = img_to_vol;
/*** convnet_layers_dotproducts ***/
// This file contains all layers that do dot products with input,
// but usually in a different connectivity pattern and weight sharing
// schemes:
// - FullyConn is fully connected dot products
// - ConvLayer does convolutions (so weight sharing spatially)
// putting them together in one file because they are very similar
var ConvLayer = function(opt) {
var opt = opt || {};
// required
this.out_depth = opt.filters;
this.sx = opt.sx; // filter size. Should be odd if possible, it's cleaner.
this.in_depth = opt.in_depth;
this.in_sx = opt.in_sx;
this.in_sy = opt.in_sy;
// optional
this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;
this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 1; // stride at which we apply filters to input volume
this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume
this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;
this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;
// computed
// note we are doing floor, so if the strided convolution of the filter doesnt fit into the input
// volume exactly, the output volume will be trimmed and not contain the (incomplete) computed
// final application.
this.out_sx = Math.floor((this.in_sx + this.pad * 2 - this.sx) / this.stride + 1);
this.out_sy = Math.floor((this.in_sy + this.pad * 2 - this.sy) / this.stride + 1);
this.layer_type = 'conv';
// initializations
var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;
this.filters = [];
for(var i=0;i<this.out_depth;i++) { this.filters.push(new Vol(this.sx, this.sy, this.in_depth)); }
this.biases = new Vol(1, 1, this.out_depth, bias);
}
ConvLayer.prototype = {
forward: function(V, is_training) {
// optimized code by @mdda that achieves 2x speedup over previous version
this.in_act = V;
var A = new Vol(this.out_sx |0, this.out_sy |0, this.out_depth |0, 0.0);
var V_sx = V.sx |0;
var V_sy = V.sy |0;
var xy_stride = this.stride |0;
for(var d=0;d<this.out_depth;d++) {
var f = this.filters[d];
var x = -this.pad |0;
var y = -this.pad |0;
for(var ay=0; ay<this.out_sy; y+=xy_stride,ay++) { // xy_stride
x = -this.pad |0;
for(var ax=0; ax<this.out_sx; x+=xy_stride,ax++) { // xy_stride
// convolve centered at this particular location
var a = 0.0;
for(var fy=0;fy<f.sy;fy++) {
var oy = y+fy; // coordinates in the original input array coordinates
for(var fx=0;fx<f.sx;fx++) {
var ox = x+fx;
if(oy>=0 && oy<V_sy && ox>=0 && ox<V_sx) {
for(var fd=0;fd<f.depth;fd++) {
// avoid function call overhead (x2) for efficiency, compromise modularity :(
a += f.w[((f.sx * fy)+fx)*f.depth+fd] * V.w[((V_sx * oy)+ox)*V.depth+fd];
}
}
}
}
a += this.biases.w[d];
A.set(ax, ay, d, a);
}
}
}
this.out_act = A;
return this.out_act;
},
backward: function() {
var V = this.in_act;
V.dw = convnet.zeros(V.w.length); // zero out gradient wrt bottom data, we're about to fill it
var V_sx = V.sx |0;
var V_sy = V.sy |0;
var xy_stride = this.stride |0;
for(var d=0;d<this.out_depth;d++) {
var f = this.filters[d];
var x = -this.pad |0;
var y = -this.pad |0;
for(var ay=0; ay<this.out_sy; y+=xy_stride,ay++) { // xy_stride
x = -this.pad |0;
for(var ax=0; ax<this.out_sx; x+=xy_stride,ax++) { // xy_stride
// convolve centered at this particular location
var chain_grad = this.out_act.get_grad(ax,ay,d); // gradient from above, from chain rule
for(var fy=0;fy<f.sy;fy++) {
var oy = y+fy; // coordinates in the original input array coordinates
for(var fx=0;fx<f.sx;fx++) {
var ox = x+fx;
if(oy>=0 && oy<V_sy && ox>=0 && ox<V_sx) {
for(var fd=0;fd<f.depth;fd++) {
// avoid function call overhead (x2) for efficiency, compromise modularity :(
var ix1 = ((V_sx * oy)+ox)*V.depth+fd;
var ix2 = ((f.sx * fy)+fx)*f.depth+fd;
f.dw[ix2] += V.w[ix1]*chain_grad;
V.dw[ix1] += f.w[ix2]*chain_grad;
}
}
}
}
this.biases.dw[d] += chain_grad;
}
}
}
},
getParamsAndGrads: function() {
var response = [];
for(var i=0;i<this.out_depth;i++) {
response.push({params: this.filters[i].w, grads: this.filters[i].dw, l2_decay_mul: this.l2_decay_mul, l1_decay_mul: this.l1_decay_mul});
}
response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});
return response;
},
toJSON: function() {
var json = {};
json.sx = this.sx; // filter size in x, y dims
json.sy = this.sy;
json.stride = this.stride;
json.in_depth = this.in_depth;
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.l1_decay_mul = this.l1_decay_mul;
json.l2_decay_mul = this.l2_decay_mul;
json.pad = this.pad;
json.filters = [];
for(var i=0;i<this.filters.length;i++) {
json.filters.push(this.filters[i].toJSON());
}
json.biases = this.biases.toJSON();
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.sx = json.sx; // filter size in x, y dims
this.sy = json.sy;
this.stride = json.stride;
this.in_depth = json.in_depth; // depth of input volume
this.filters = [];
this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;
this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;
this.pad = typeof json.pad !== 'undefined' ? json.pad : 0;
for(var i=0;i<json.filters.length;i++) {
var v = new Vol(0,0,0,0);
v.fromJSON(json.filters[i]);
this.filters.push(v);
}
this.biases = new Vol(0,0,0,0);
this.biases.fromJSON(json.biases);
}
}
var FullyConnLayer = function(opt) {
var opt = opt || {};
// required
// ok fine we will allow 'filters' as the word as well
this.out_depth = typeof opt.num_neurons !== 'undefined' ? opt.num_neurons : opt.filters;
// optional
this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;
this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;
// computed
this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
this.out_sx = 1;
this.out_sy = 1;
this.layer_type = 'fc';
// initializations
var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;
this.filters = [];
for(var i=0;i<this.out_depth ;i++) { this.filters.push(new Vol(1, 1, this.num_inputs)); }
this.biases = new Vol(1, 1, this.out_depth, bias);
}
FullyConnLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var A = new Vol(1, 1, this.out_depth, 0.0);
var Vw = V.w;
for(var i=0;i<this.out_depth;i++) {
var a = 0.0;
var wi = this.filters[i].w;
for(var d=0;d<this.num_inputs;d++) {
a += Vw[d] * wi[d]; // for efficiency use Vols directly for now
}
a += this.biases.w[i];
A.w[i] = a;
}
this.out_act = A;
return this.out_act;
},
backward: function() {
var V = this.in_act;
V.dw = convnet.zeros(V.w.length); // zero out the gradient in input Vol
// compute gradient wrt weights and data
for(var i=0;i<this.out_depth;i++) {
var tfi = this.filters[i];
var chain_grad = this.out_act.dw[i];
for(var d=0;d<this.num_inputs;d++) {
V.dw[d] += tfi.w[d]*chain_grad; // grad wrt input data
tfi.dw[d] += V.w[d]*chain_grad; // grad wrt params
}
this.biases.dw[i] += chain_grad;
}
},
getParamsAndGrads: function() {
var response = [];
for(var i=0;i<this.out_depth;i++) {
response.push({params: this.filters[i].w, grads: this.filters[i].dw, l1_decay_mul: this.l1_decay_mul, l2_decay_mul: this.l2_decay_mul});
}
response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});
return response;
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.num_inputs = this.num_inputs;
json.l1_decay_mul = this.l1_decay_mul;
json.l2_decay_mul = this.l2_decay_mul;
json.filters = [];
for(var i=0;i<this.filters.length;i++) {
json.filters.push(this.filters[i].toJSON());
}
json.biases = this.biases.toJSON();
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.num_inputs = json.num_inputs;
this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;
this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;
this.filters = [];
for(var i=0;i<json.filters.length;i++) {
var v = new Vol(0,0,0,0);
v.fromJSON(json.filters[i]);
this.filters.push(v);
}
this.biases = new Vol(0,0,0,0);
this.biases.fromJSON(json.biases);
}
}
convnet.ConvLayer = ConvLayer;
convnet.FullyConnLayer = FullyConnLayer;
/*** convnet_layers_pool ***/
var PoolLayer = function(opt) {
var opt = opt || {};
// required
this.sx = opt.sx; // filter size
this.in_depth = opt.in_depth;
this.in_sx = opt.in_sx;
this.in_sy = opt.in_sy;
// optional
this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;
this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 2;
this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume
// computed
this.out_depth = this.in_depth;
this.out_sx = Math.floor((this.in_sx + this.pad * 2 - this.sx) / this.stride + 1);
this.out_sy = Math.floor((this.in_sy + this.pad * 2 - this.sy) / this.stride + 1);
this.layer_type = 'pool';
// store switches for x,y coordinates for where the max comes from, for each output neuron
this.switchx = convnet.zeros(this.out_sx*this.out_sy*this.out_depth);
this.switchy = convnet.zeros(this.out_sx*this.out_sy*this.out_depth);
}
PoolLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var A = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);
var n=0; // a counter for switches
for(var d=0;d<this.out_depth;d++) {
var x = -this.pad;
var y = -this.pad;
for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {
y = -this.pad;
for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {
// convolve centered at this particular location
var a = -99999; // hopefully small enough ;\
var winx=-1,winy=-1;
for(var fx=0;fx<this.sx;fx++) {
for(var fy=0;fy<this.sy;fy++) {
var oy = y+fy;
var ox = x+fx;
if(oy>=0 && oy<V.sy && ox>=0 && ox<V.sx) {
var v = V.get(ox, oy, d);
// perform max pooling and store pointers to where
// the max came from. This will speed up backprop
// and can help make nice visualizations in future
if(v > a) { a = v; winx=ox; winy=oy;}
}
}
}
this.switchx[n] = winx;
this.switchy[n] = winy;
n++;
A.set(ax, ay, d, a);
}
}
}
this.out_act = A;
return this.out_act;
},
backward: function() {
// pooling layers have no parameters, so simply compute
// gradient wrt data here
var V = this.in_act;
V.dw = convnet.zeros(V.w.length); // zero out gradient wrt data
var A = this.out_act; // computed in forward pass
var n = 0;
for(var d=0;d<this.out_depth;d++) {
var x = -this.pad;
var y = -this.pad;
for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {
y = -this.pad;
for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {
var chain_grad = this.out_act.get_grad(ax,ay,d);
V.add_grad(this.switchx[n], this.switchy[n], d, chain_grad);
n++;
}
}
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.sx = this.sx;
json.sy = this.sy;
json.stride = this.stride;
json.in_depth = this.in_depth;
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.pad = this.pad;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.sx = json.sx;
this.sy = json.sy;
this.stride = json.stride;
this.in_depth = json.in_depth;
this.pad = typeof json.pad !== 'undefined' ? json.pad : 0; // backwards compatibility
this.switchx = convnet.zeros(this.out_sx*this.out_sy*this.out_depth); // need to re-init these appropriately
this.switchy = convnet.zeros(this.out_sx*this.out_sy*this.out_depth);
}
}
convnet.PoolLayer = PoolLayer;
/*** convnet_layers_input ***/
var getopt = convnet.getopt;
var InputLayer = function(opt) {
var opt = opt || {};
// required: depth
this.out_depth = getopt(opt, ['out_depth', 'depth'], 0);
// optional: default these dimensions to 1
this.out_sx = getopt(opt, ['out_sx', 'sx', 'width'], 1);
this.out_sy = getopt(opt, ['out_sy', 'sy', 'height'], 1);
// computed
this.layer_type = 'input';
}
InputLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
this.out_act = V;
return this.out_act; // simply identity function for now
},
backward: function() { },
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
}
}
convnet.InputLayer = InputLayer;
/*** convnet_layers_loss ***/
// Layers that implement a loss. Currently these are the layers that
// can initiate a backward() pass. In future we probably want a more
// flexible system that can accomodate multiple losses to do multi-task
// learning, and stuff like that. But for now, one of the layers in this
// file must be the final layer in a Net.
// This is a classifier, with N discrete classes from 0 to N-1
// it gets a stream of N incoming numbers and computes the softmax
// function (exponentiate and normalize to sum to 1 as probabilities should)
var SoftmaxLayer = function(opt) {
var opt = opt || {};
// computed
this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
this.out_depth = this.num_inputs;
this.out_sx = 1;
this.out_sy = 1;
this.layer_type = 'softmax';
}
SoftmaxLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var A = new Vol(1, 1, this.out_depth, 0.0);
// compute max activation
var as = V.w;
var amax = V.w[0];
for(var i=1;i<this.out_depth;i++) {
if(as[i] > amax) amax = as[i];
}
// compute exponentials (carefully to not blow up)
var es = convnet.zeros(this.out_depth);
var esum = 0.0;
for(var i=0;i<this.out_depth;i++) {
var e = Math.exp(as[i] - amax);
esum += e;
es[i] = e;
}
// normalize and output to sum to one
for(var i=0;i<this.out_depth;i++) {
es[i] /= esum;
A.w[i] = es[i];
}
this.es = es; // save these for backprop
this.out_act = A;
return this.out_act;
},
backward: function(y) {
// compute and accumulate gradient wrt weights and bias of this layer
var x = this.in_act;
x.dw = convnet.zeros(x.w.length); // zero out the gradient of input Vol
for(var i=0;i<this.out_depth;i++) {
var indicator = i === y ? 1.0 : 0.0;
var mul = -(indicator - this.es[i]);
x.dw[i] = mul;
}
// loss is the class negative log likelihood
return -Math.log(this.es[y]);
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.num_inputs = this.num_inputs;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.num_inputs = json.num_inputs;
}
}
// implements an L2 regression cost layer,
// so penalizes \sum_i(||x_i - y_i||^2), where x is its input
// and y is the user-provided array of "correct" values.
var RegressionLayer = function(opt) {
var opt = opt || {};
// computed
this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
this.out_depth = this.num_inputs;
this.out_sx = 1;
this.out_sy = 1;
this.layer_type = 'regression';
}
RegressionLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
this.out_act = V;
return V; // identity function
},
// y is a list here of size num_inputs
// or it can be a number if only one value is regressed
// or it can be a struct {dim: i, val: x} where we only want to
// regress on dimension i and asking it to have value x
backward: function(y) {
// compute and accumulate gradient wrt weights and bias of this layer
var x = this.in_act;
x.dw = convnet.zeros(x.w.length); // zero out the gradient of input Vol
var loss = 0.0;
if(y instanceof Array || y instanceof Float64Array) {
for(var i=0;i<this.out_depth;i++) {
var dy = x.w[i] - y[i];
x.dw[i] = dy;
loss += 0.5*dy*dy;
}
} else if(typeof y === 'number') {
// lets hope that only one number is being regressed
var dy = x.w[0] - y;
x.dw[0] = dy;
loss += 0.5*dy*dy;
} else {
// assume it is a struct with entries .dim and .val
// and we pass gradient only along dimension dim to be equal to val
var i = y.dim;
var yi = y.val;
var dy = x.w[i] - yi;
x.dw[i] = dy;
loss += 0.5*dy*dy;
}
return loss;
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.num_inputs = this.num_inputs;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.num_inputs = json.num_inputs;
}
}
var SVMLayer = function(opt) {
var opt = opt || {};
// computed
this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
this.out_depth = this.num_inputs;
this.out_sx = 1;
this.out_sy = 1;
this.layer_type = 'svm';
}
SVMLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
this.out_act = V; // nothing to do, output raw scores
return V;
},
backward: function(y) {
// compute and accumulate gradient wrt weights and bias of this layer
var x = this.in_act;
x.dw = convnet.zeros(x.w.length); // zero out the gradient of input Vol
// we're using structured loss here, which means that the score
// of the ground truth should be higher than the score of any other
// class, by a margin
var yscore = x.w[y]; // score of ground truth
var margin = 1.0;
var loss = 0.0;
for(var i=0;i<this.out_depth;i++) {
if(y === i) { continue; }
var ydiff = -yscore + x.w[i] + margin;
if(ydiff > 0) {
// violating dimension, apply loss
x.dw[i] += 1;
x.dw[y] -= 1;
loss += ydiff;
}
}
return loss;
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.num_inputs = this.num_inputs;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.num_inputs = json.num_inputs;
}
}
convnet.RegressionLayer = RegressionLayer;
convnet.SoftmaxLayer = SoftmaxLayer;
convnet.SVMLayer = SVMLayer;
/*** convnet_layers_nonlinearities ***/
// Implements ReLU nonlinearity elementwise
// x -> max(0, x)
// the output is in [0, inf)
var ReluLayer = function(opt) {
var opt = opt || {};
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'relu';
}
ReluLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var V2 = V.clone();
var N = V.w.length;
var V2w = V2.w;
for(var i=0;i<N;i++) {
if(V2w[i] < 0) V2w[i] = 0; // threshold at 0
}
this.out_act = V2;
return this.out_act;
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var V2 = this.out_act;
var N = V.w.length;
V.dw = convnet.zeros(N); // zero out gradient wrt data
for(var i=0;i<N;i++) {
if(V2.w[i] <= 0) V.dw[i] = 0; // threshold
else V.dw[i] = V2.dw[i];
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
}
}
// Implements Sigmoid nnonlinearity elementwise
// x -> 1/(1+e^(-x))
// so the output is between 0 and 1.
var SigmoidLayer = function(opt) {
var opt = opt || {};
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'sigmoid';
}
SigmoidLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var V2 = V.cloneAndZero();
var N = V.w.length;
var V2w = V2.w;
var Vw = V.w;
for(var i=0;i<N;i++) {
V2w[i] = 1.0/(1.0+Math.exp(-Vw[i]));
}
this.out_act = V2;
return this.out_act;
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var V2 = this.out_act;
var N = V.w.length;
V.dw = convnet.zeros(N); // zero out gradient wrt data
for(var i=0;i<N;i++) {
var v2wi = V2.w[i];
V.dw[i] = v2wi * (1.0 - v2wi) * V2.dw[i];
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
}
}
// Implements Maxout nnonlinearity that computes
// x -> max(x)
// where x is a vector of size group_size. Ideally of course,
// the input size should be exactly divisible by group_size
var MaxoutLayer = function(opt) {
var opt = opt || {};
// required
this.group_size = typeof opt.group_size !== 'undefined' ? opt.group_size : 2;
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = Math.floor(opt.in_depth / this.group_size);
this.layer_type = 'maxout';
this.switches = convnet.zeros(this.out_sx*this.out_sy*this.out_depth); // useful for backprop
}
MaxoutLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var N = this.out_depth;
var V2 = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);
// optimization branch. If we're operating on 1D arrays we dont have
// to worry about keeping track of x,y,d coordinates inside
// input volumes. In convnets we do :(
if(this.out_sx === 1 && this.out_sy === 1) {
for(var i=0;i<N;i++) {
var ix = i * this.group_size; // base index offset
var a = V.w[ix];
var ai = 0;
for(var j=1;j<this.group_size;j++) {
var a2 = V.w[ix+j];
if(a2 > a) {
a = a2;
ai = j;
}
}
V2.w[i] = a;
this.switches[i] = ix + ai;
}
} else {
var n=0; // counter for switches
for(var x=0;x<V.sx;x++) {
for(var y=0;y<V.sy;y++) {
for(var i=0;i<N;i++) {
var ix = i * this.group_size;
var a = V.get(x, y, ix);
var ai = 0;
for(var j=1;j<this.group_size;j++) {
var a2 = V.get(x, y, ix+j);
if(a2 > a) {
a = a2;
ai = j;
}
}
V2.set(x,y,i,a);
this.switches[n] = ix + ai;
n++;
}
}
}
}
this.out_act = V2;
return this.out_act;
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var V2 = this.out_act;
var N = this.out_depth;
V.dw = convnet.zeros(V.w.length); // zero out gradient wrt data
// pass the gradient through the appropriate switch
if(this.out_sx === 1 && this.out_sy === 1) {
for(var i=0;i<N;i++) {
var chain_grad = V2.dw[i];
V.dw[this.switches[i]] = chain_grad;
}
} else {
// bleh okay, lets do this the hard way
var n=0; // counter for switches
for(var x=0;x<V2.sx;x++) {
for(var y=0;y<V2.sy;y++) {
for(var i=0;i<N;i++) {
var chain_grad = V2.get_grad(x,y,i);
V.set_grad(x,y,this.switches[n],chain_grad);
n++;
}
}
}
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.group_size = this.group_size;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.group_size = json.group_size;
this.switches = convnet.zeros(this.group_size);
}
}
// a helper function, since tanh is not yet part of ECMAScript. Will be in v6.
function tanh(x) {
var y = Math.exp(2 * x);
return (y - 1) / (y + 1);
}
// Implements Tanh nnonlinearity elementwise
// x -> tanh(x)
// so the output is between -1 and 1.
var TanhLayer = function(opt) {
var opt = opt || {};
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'tanh';
}
TanhLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var V2 = V.cloneAndZero();
var N = V.w.length;
for(var i=0;i<N;i++) {
V2.w[i] = tanh(V.w[i]);
}
this.out_act = V2;
return this.out_act;
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var V2 = this.out_act;
var N = V.w.length;
V.dw = convnet.zeros(N); // zero out gradient wrt data
for(var i=0;i<N;i++) {
var v2wi = V2.w[i];
V.dw[i] = (1.0 - v2wi * v2wi) * V2.dw[i];
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
}
}
convnet.TanhLayer = TanhLayer;
convnet.MaxoutLayer = MaxoutLayer;
convnet.ReluLayer = ReluLayer;
convnet.SigmoidLayer = SigmoidLayer;
/*** convnet_layers_dropout ***/
// An inefficient dropout layer
// Note this is not most efficient implementation since the layer before
// computed all these activations and now we're just going to drop them :(
// same goes for backward pass. Also, if we wanted to be efficient at test time
// we could equivalently be clever and upscale during train and copy pointers during test
// todo: make more efficient.
var DropoutLayer = function(opt) {
var opt = opt || {};
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'dropout';
this.drop_prob = typeof opt.drop_prob !== 'undefined' ? opt.drop_prob : 0.5;
this.dropped = convnet.zeros(this.out_sx*this.out_sy*this.out_depth);
}
DropoutLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
if(typeof(is_training)==='undefined') { is_training = false; } // default is prediction mode
var V2 = V.clone();
var N = V.w.length;
if(is_training) {
// do dropout
for(var i=0;i<N;i++) {
if(Math.random()<this.drop_prob) { V2.w[i]=0; this.dropped[i] = true; } // drop!
else {this.dropped[i] = false;}
}
} else {
// scale the activations during prediction
for(var i=0;i<N;i++) { V2.w[i]*=this.drop_prob; }
}
this.out_act = V2;
return this.out_act; // dummy identity function for now
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var chain_grad = this.out_act;
var N = V.w.length;
V.dw = convnet.zeros(N); // zero out gradient wrt data
for(var i=0;i<N;i++) {
if(!(this.dropped[i])) {
V.dw[i] = chain_grad.dw[i]; // copy over the gradient
}
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.drop_prob = this.drop_prob;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.drop_prob = json.drop_prob;
}
}
convnet.DropoutLayer = DropoutLayer;
/*** convnet_layers_normailzation ***/
// a bit experimental layer for now. I think it works but I'm not 100%
// the gradient check is a bit funky. I'll look into this a bit later.
// Local Response Normalization in window, along depths of volumes
var LocalResponseNormalizationLayer = function(opt) {
var opt = opt || {};
// required
this.k = opt.k;
this.n = opt.n;
this.alpha = opt.alpha;
this.beta = opt.beta;
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'lrn';
// checks
if(this.n%2 === 0) { console.log('WARNING n should be odd for LRN layer'); }
}
LocalResponseNormalizationLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var A = V.cloneAndZero();
this.S_cache_ = V.cloneAndZero();
var n2 = Math.floor(this.n/2);
for(var x=0;x<V.sx;x++) {
for(var y=0;y<V.sy;y++) {
for(var i=0;i<V.depth;i++) {
var ai = V.get(x,y,i);
// normalize in a window of size n
var den = 0.0;
for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {
var aa = V.get(x,y,j);
den += aa*aa;
}
den *= this.alpha / this.n;
den += this.k;
this.S_cache_.set(x,y,i,den); // will be useful for backprop
den = Math.pow(den, this.beta);
A.set(x,y,i,ai/den);
}
}
}
this.out_act = A;
return this.out_act; // dummy identity function for now
},
backward: function() {
// evaluate gradient wrt data
var V = this.in_act; // we need to set dw of this
V.dw = convnet.zeros(V.w.length); // zero out gradient wrt data
var A = this.out_act; // computed in forward pass
var n2 = Math.floor(this.n/2);
for(var x=0;x<V.sx;x++) {
for(var y=0;y<V.sy;y++) {
for(var i=0;i<V.depth;i++) {
var chain_grad = this.out_act.get_grad(x,y,i);
var S = this.S_cache_.get(x,y,i);
var SB = Math.pow(S, this.beta);
var SB2 = SB*SB;
// normalize in a window of size n
for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {
var aj = V.get(x,y,j);
var g = -aj*this.beta*Math.pow(S,this.beta-1)*this.alpha/this.n*2*aj;
if(j===i) g+= SB;
g /= SB2;
g *= chain_grad;
V.add_grad(x,y,j,g);
}
}
}
}
},
getParamsAndGrads: function() { return []; },
toJSON: function() {
var json = {};
json.k = this.k;
json.n = this.n;
json.alpha = this.alpha; // normalize by size
json.beta = this.beta;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.out_depth = this.out_depth;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.k = json.k;
this.n = json.n;
this.alpha = json.alpha; // normalize by size
this.beta = json.beta;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.out_depth = json.out_depth;
this.layer_type = json.layer_type;
}
}
convnet.LocalResponseNormalizationLayer = LocalResponseNormalizationLayer;
/*** convnet_net ***/
var assert = convnet.assert;
// Net manages a set of layers
// For now constraints: Simple linear order of layers, first layer input last layer a cost layer
var Net = function(options) {
this.layers = [];
}
Net.prototype = {
// takes a list of layer definitions and creates the network layer objects
makeLayers: function(defs) {
// few checks
assert(defs.length >= 2, 'Error! At least one input layer and one loss layer are required.');
assert(defs[0].type === 'input', 'Error! First layer must be the input layer, to declare size of inputs');
// desugar layer_defs for adding activation, dropout layers etc
var desugar = function() {
var new_defs = [];
for(var i=0;i<defs.length;i++) {
var def = defs[i];
if(def.type==='softmax' || def.type==='svm') {
// add an fc layer here, there is no reason the user should
// have to worry about this and we almost always want to
new_defs.push({type:'fc', num_neurons: def.num_classes});
}
if(def.type==='regression') {
// add an fc layer here, there is no reason the user should
// have to worry about this and we almost always want to
new_defs.push({type:'fc', num_neurons: def.num_neurons});
}
if((def.type==='fc' || def.type==='conv')
&& typeof(def.bias_pref) === 'undefined'){
def.bias_pref = 0.0;
if(typeof def.activation !== 'undefined' && def.activation === 'relu') {
def.bias_pref = 0.1; // relus like a bit of positive bias to get gradients early
// otherwise it's technically possible that a relu unit will never turn on (by chance)
// and will never get any gradient and never contribute any computation. Dead relu.
}
}
new_defs.push(def);
if(typeof def.activation !== 'undefined') {
if(def.activation==='relu') { new_defs.push({type:'relu'}); }
else if (def.activation==='sigmoid') { new_defs.push({type:'sigmoid'}); }
else if (def.activation==='tanh') { new_defs.push({type:'tanh'}); }
else if (def.activation==='maxout') {
// create maxout activation, and pass along group size, if provided
var gs = def.group_size !== 'undefined' ? def.group_size : 2;
new_defs.push({type:'maxout', group_size:gs});
}
else { console.log('ERROR unsupported activation ' + def.activation); }
}
if(typeof def.drop_prob !== 'undefined' && def.type !== 'dropout') {
new_defs.push({type:'dropout', drop_prob: def.drop_prob});
}
}
return new_defs;
}
defs = desugar(defs);
// create the layers
this.layers = [];
for(var i=0;i<defs.length;i++) {
var def = defs[i];
if(i>0) {
var prev = this.layers[i-1];
def.in_sx = prev.out_sx;
def.in_sy = prev.out_sy;
def.in_depth = prev.out_depth;
}
switch(def.type) {
case 'fc': this.layers.push(new convnet.FullyConnLayer(def)); break;
case 'lrn': this.layers.push(new convnet.LocalResponseNormalizationLayer(def)); break;
case 'dropout': this.layers.push(new convnet.DropoutLayer(def)); break;
case 'input': this.layers.push(new convnet.InputLayer(def)); break;
case 'softmax': this.layers.push(new convnet.SoftmaxLayer(def)); break;
case 'regression': this.layers.push(new convnet.RegressionLayer(def)); break;
case 'conv': this.layers.push(new convnet.ConvLayer(def)); break;
case 'pool': this.layers.push(new convnet.PoolLayer(def)); break;
case 'relu': this.layers.push(new convnet.ReluLayer(def)); break;
case 'sigmoid': this.layers.push(new convnet.SigmoidLayer(def)); break;
case 'tanh': this.layers.push(new convnet.TanhLayer(def)); break;
case 'maxout': this.layers.push(new convnet.MaxoutLayer(def)); break;
case 'svm': this.layers.push(new convnet.SVMLayer(def)); break;
default: console.log('ERROR: UNRECOGNIZED LAYER TYPE: ' + def.type);
}
}
},
// forward prop the network.
// The trainer class passes is_training = true, but when this function is
// called from outside (not from the trainer), it defaults to prediction mode
forward: function(V, is_training) {
if(typeof(is_training) === 'undefined') is_training = false;
var act = this.layers[0].forward(V, is_training);
for(var i=1;i<this.layers.length;i++) {
act = this.layers[i].forward(act, is_training);
}
return act;
},
getCostLoss: function(V, y) {
this.forward(V, false);
var N = this.layers.length;
var loss = this.layers[N-1].backward(y);
return loss;
},
// backprop: compute gradients wrt all parameters
backward: function(y) {
var N = this.layers.length;
var loss = this.layers[N-1].backward(y); // last layer assumed to be loss layer
for(var i=N-2;i>=0;i--) { // first layer assumed input
this.layers[i].backward();
}
return loss;
},
getParamsAndGrads: function() {
// accumulate parameters and gradients for the entire network
var response = [];
for(var i=0;i<this.layers.length;i++) {
var layer_reponse = this.layers[i].getParamsAndGrads();
for(var j=0;j<layer_reponse.length;j++) {
response.push(layer_reponse[j]);
}
}
return response;
},
getPrediction: function() {
// this is a convenience function for returning the argmax
// prediction, assuming the last layer of the net is a softmax
var S = this.layers[this.layers.length-1];
assert(S.layer_type === 'softmax', 'getPrediction function assumes softmax as last layer of the net!');
var p = S.out_act.w;
var maxv = p[0];
var maxi = 0;
for(var i=1;i<p.length;i++) {
if(p[i] > maxv) { maxv = p[i]; maxi = i;}
}
return maxi; // return index of the class with highest class probability
},
toJSON: function() {
var json = {};
json.layers = [];
for(var i=0;i<this.layers.length;i++) {
json.layers.push(this.layers[i].toJSON());
}
return json;
},
fromJSON: function(json) {
this.layers = [];
for(var i=0;i<json.layers.length;i++) {
var Lj = json.layers[i]
var t = Lj.layer_type;
var L;
if(t==='input') { L = new convnet.InputLayer(); }
if(t==='relu') { L = new convnet.ReluLayer(); }
if(t==='sigmoid') { L = new convnet.SigmoidLayer(); }
if(t==='tanh') { L = new convnet.TanhLayer(); }
if(t==='dropout') { L = new convnet.DropoutLayer(); }
if(t==='conv') { L = new convnet.ConvLayer(); }
if(t==='pool') { L = new convnet.PoolLayer(); }
if(t==='lrn') { L = new convnet.LocalResponseNormalizationLayer(); }
if(t==='softmax') { L = new convnet.SoftmaxLayer(); }
if(t==='regression') { L = new convnet.RegressionLayer(); }
if(t==='fc') { L = new convnet.FullyConnLayer(); }
if(t==='maxout') { L = new convnet.MaxoutLayer(); }
if(t==='svm') { L = new convnet.SVMLayer(); }
L.fromJSON(Lj);
this.layers.push(L);
}
}
}
convnet.Net = Net;
/*** convnet_trainers ***/
var Trainer = function(net, options) {
this.net = net;
var options = options || {};
this.learning_rate = typeof options.learning_rate !== 'undefined' ? options.learning_rate : 0.01;
this.l1_decay = typeof options.l1_decay !== 'undefined' ? options.l1_decay : 0.0;
this.l2_decay = typeof options.l2_decay !== 'undefined' ? options.l2_decay : 0.0;
this.batch_size = typeof options.batch_size !== 'undefined' ? options.batch_size : 1;
this.method = typeof options.method !== 'undefined' ? options.method : 'sgd'; // sgd/adam/adagrad/adadelta/windowgrad/netsterov
this.momentum = typeof options.momentum !== 'undefined' ? options.momentum : 0.9;
this.ro = typeof options.ro !== 'undefined' ? options.ro : 0.95; // used in adadelta
this.eps = typeof options.eps !== 'undefined' ? options.eps : 1e-8; // used in adam or adadelta
this.beta1 = typeof options.beta1 !== 'undefined' ? options.beta1 : 0.9; // used in adam
this.beta2 = typeof options.beta2 !== 'undefined' ? options.beta2 : 0.999; // used in adam
this.k = 0; // iteration counter
this.gsum = []; // last iteration gradients (used for momentum calculations)
this.xsum = []; // used in adam or adadelta
// check if regression is expected
if(this.net.layers[this.net.layers.length - 1].layer_type === "regression")
this.regression = true;
else
this.regression = false;
}
Trainer.prototype = {
train: function(x, y) {
var start = new Date().getTime();
this.net.forward(x, true); // also set the flag that lets the net know we're just training
var end = new Date().getTime();
var fwd_time = end - start;
var start = new Date().getTime();
var cost_loss = this.net.backward(y);
var l2_decay_loss = 0.0;
var l1_decay_loss = 0.0;
var end = new Date().getTime();
var bwd_time = end - start;
if(this.regression && y.constructor !== Array)
console.log("Warning: a regression net requires an array as training output vector.");
this.k++;
if(this.k % this.batch_size === 0) {
var pglist = this.net.getParamsAndGrads();
// initialize lists for accumulators. Will only be done once on first iteration
if(this.gsum.length === 0 && (this.method !== 'sgd' || this.momentum > 0.0)) {
// only vanilla sgd doesnt need either lists
// momentum needs gsum
// adagrad needs gsum
// adam and adadelta needs gsum and xsum
for(var i=0;i<pglist.length;i++) {
this.gsum.push(convnet.zeros(pglist[i].params.length));
if(this.method === 'adam' || this.method === 'adadelta') {
this.xsum.push(convnet.zeros(pglist[i].params.length));
} else {
this.xsum.push([]); // conserve memory
}
}
}
// perform an update for all sets of weights
for(var i=0;i<pglist.length;i++) {
var pg = pglist[i]; // param, gradient, other options in future (custom learning rate etc)
var p = pg.params;
var g = pg.grads;
// learning rate for some parameters.
var l2_decay_mul = typeof pg.l2_decay_mul !== 'undefined' ? pg.l2_decay_mul : 1.0;
var l1_decay_mul = typeof pg.l1_decay_mul !== 'undefined' ? pg.l1_decay_mul : 1.0;
var l2_decay = this.l2_decay * l2_decay_mul;
var l1_decay = this.l1_decay * l1_decay_mul;
var plen = p.length;
for(var j=0;j<plen;j++) {
l2_decay_loss += l2_decay*p[j]*p[j]/2; // accumulate weight decay loss
l1_decay_loss += l1_decay*Math.abs(p[j]);
var l1grad = l1_decay * (p[j] > 0 ? 1 : -1);
var l2grad = l2_decay * (p[j]);
var gij = (l2grad + l1grad + g[j]) / this.batch_size; // raw batch gradient
var gsumi = this.gsum[i];
var xsumi = this.xsum[i];
if(this.method === 'adam') {
// adam update
gsumi[j] = gsumi[j] * this.beta1 + (1- this.beta1) * gij; // update biased first moment estimate
xsumi[j] = xsumi[j] * this.beta2 + (1-this.beta2) * gij * gij; // update biased second moment estimate
var biasCorr1 = gsumi[j] * (1 - Math.pow(this.beta1, this.k)); // correct bias first moment estimate
var biasCorr2 = xsumi[j] * (1 - Math.pow(this.beta2, this.k)); // correct bias second moment estimate
var dx = - this.learning_rate * biasCorr1 / (Math.sqrt(biasCorr2) + this.eps);
p[j] += dx;
} else if(this.method === 'adagrad') {
// adagrad update
gsumi[j] = gsumi[j] + gij * gij;
var dx = - this.learning_rate / Math.sqrt(gsumi[j] + this.eps) * gij;
p[j] += dx;
} else if(this.method === 'windowgrad') {
// this is adagrad but with a moving window weighted average
// so the gradient is not accumulated over the entire history of the run.
// it's also referred to as Idea #1 in Zeiler paper on Adadelta. Seems reasonable to me!
gsumi[j] = this.ro * gsumi[j] + (1-this.ro) * gij * gij;
var dx = - this.learning_rate / Math.sqrt(gsumi[j] + this.eps) * gij; // eps added for better conditioning
p[j] += dx;
} else if(this.method === 'adadelta') {
gsumi[j] = this.ro * gsumi[j] + (1-this.ro) * gij * gij;
var dx = - Math.sqrt((xsumi[j] + this.eps)/(gsumi[j] + this.eps)) * gij;
xsumi[j] = this.ro * xsumi[j] + (1-this.ro) * dx * dx; // yes, xsum lags behind gsum by 1.
p[j] += dx;
} else if(this.method === 'nesterov') {
var dx = gsumi[j];
gsumi[j] = gsumi[j] * this.momentum + this.learning_rate * gij;
dx = this.momentum * dx - (1.0 + this.momentum) * gsumi[j];
p[j] += dx;
} else {
// assume SGD
if(this.momentum > 0.0) {
// momentum update
var dx = this.momentum * gsumi[j] - this.learning_rate * gij; // step
gsumi[j] = dx; // back this up for next iteration of momentum
p[j] += dx; // apply corrected gradient
} else {
// vanilla sgd
p[j] += - this.learning_rate * gij;
}
}
g[j] = 0.0; // zero out gradient so that we can begin accumulating anew
}
}
}
// appending softmax_loss for backwards compatibility, but from now on we will always use cost_loss
// in future, TODO: have to completely redo the way loss is done around the network as currently
// loss is a bit of a hack. Ideally, user should specify arbitrary number of loss functions on any layer
// and it should all be computed correctly and automatically.
return {fwd_time: fwd_time, bwd_time: bwd_time,
l2_decay_loss: l2_decay_loss, l1_decay_loss: l1_decay_loss,
cost_loss: cost_loss, softmax_loss: cost_loss,
loss: cost_loss + l1_decay_loss + l2_decay_loss}
}
}
convnet.Trainer = Trainer;
convnet.SGDTrainer = Trainer; // backwards compatibility
/*** convnet_magicnets ***/
// used utilities, make explicit local references
var randf = convnet.randf;
var randi = convnet.randi;
var Net = convnet.Net;
var Trainer = convnet.Trainer;
var maxmin = convnet.maxmin;
var randperm = convnet.randperm;
var weightedSample = convnet.weightedSample;
var getopt = convnet.getopt;
var arrUnique = convnet.arrUnique;
/*
A MagicNet takes data: a list of convnetjs.Vol(), and labels
which for now are assumed to be class indeces 0..K. MagicNet then:
- creates data folds for cross-validation
- samples candidate networks
- evaluates candidate networks on all data folds
- produces predictions by model-averaging the best networks
*/
var MagicNet = function(data, labels, opt) {
var opt = opt || {};
if(typeof data === 'undefined') { data = []; }
if(typeof labels === 'undefined') { labels = []; }
// required inputs
this.data = data; // store these pointers to data
this.labels = labels;
// optional inputs
this.train_ratio = getopt(opt, 'train_ratio', 0.7);
this.num_folds = getopt(opt, 'num_folds', 10);
this.num_candidates = getopt(opt, 'num_candidates', 50); // we evaluate several in parallel
// how many epochs of data to train every network? for every fold?
// higher values mean higher accuracy in final results, but more expensive
this.num_epochs = getopt(opt, 'num_epochs', 50);
// number of best models to average during prediction. Usually higher = better
this.ensemble_size = getopt(opt, 'ensemble_size', 10);
// candidate parameters
this.batch_size_min = getopt(opt, 'batch_size_min', 10);
this.batch_size_max = getopt(opt, 'batch_size_max', 300);
this.l2_decay_min = getopt(opt, 'l2_decay_min', -4);
this.l2_decay_max = getopt(opt, 'l2_decay_max', 2);
this.learning_rate_min = getopt(opt, 'learning_rate_min', -4);
this.learning_rate_max = getopt(opt, 'learning_rate_max', 0);
this.momentum_min = getopt(opt, 'momentum_min', 0.9);
this.momentum_max = getopt(opt, 'momentum_max', 0.9);
this.neurons_min = getopt(opt, 'neurons_min', 5);
this.neurons_max = getopt(opt, 'neurons_max', 30);
// computed
this.folds = []; // data fold indices, gets filled by sampleFolds()
this.candidates = []; // candidate networks that are being currently evaluated
this.evaluated_candidates = []; // history of all candidates that were fully evaluated on all folds
this.unique_labels = arrUnique(labels);
this.iter = 0; // iteration counter, goes from 0 -> num_epochs * num_training_data
this.foldix = 0; // index of active fold
// callbacks
this.finish_fold_callback = null;
this.finish_batch_callback = null;
// initializations
if(this.data.length > 0) {
this.sampleFolds();
this.sampleCandidates();
}
};
MagicNet.prototype = {
// sets this.folds to a sampling of this.num_folds folds
sampleFolds: function() {
var N = this.data.length;
var num_train = Math.floor(this.train_ratio * N);
this.folds = []; // flush folds, if any
for(var i=0;i<this.num_folds;i++) {
var p = randperm(N);
this.folds.push({train_ix: p.slice(0, num_train), test_ix: p.slice(num_train, N)});
}
},
// returns a random candidate network
sampleCandidate: function() {
var input_depth = this.data[0].w.length;
var num_classes = this.unique_labels.length;
// sample network topology and hyperparameters
var layer_defs = [];
layer_defs.push({type:'input', out_sx:1, out_sy:1, out_depth: input_depth});
var nwl = weightedSample([0,1,2,3], [0.2, 0.3, 0.3, 0.2]); // prefer nets with 1,2 hidden layers
for(var q=0;q<nwl;q++) {
var ni = randi(this.neurons_min, this.neurons_max);
var act = ['tanh','maxout','relu'][randi(0,3)];
if(randf(0,1)<0.5) {
var dp = Math.random();
layer_defs.push({type:'fc', num_neurons: ni, activation: act, drop_prob: dp});
} else {
layer_defs.push({type:'fc', num_neurons: ni, activation: act});
}
}
layer_defs.push({type:'softmax', num_classes: num_classes});
var net = new Net();
net.makeLayers(layer_defs);
// sample training hyperparameters
var bs = randi(this.batch_size_min, this.batch_size_max); // batch size
var l2 = Math.pow(10, randf(this.l2_decay_min, this.l2_decay_max)); // l2 weight decay
var lr = Math.pow(10, randf(this.learning_rate_min, this.learning_rate_max)); // learning rate
var mom = randf(this.momentum_min, this.momentum_max); // momentum. Lets just use 0.9, works okay usually ;p
var tp = randf(0,1); // trainer type
var trainer_def;
if(tp<0.33) {
trainer_def = {method:'adadelta', batch_size:bs, l2_decay:l2};
} else if(tp<0.66) {
trainer_def = {method:'adagrad', learning_rate: lr, batch_size:bs, l2_decay:l2};
} else {
trainer_def = {method:'sgd', learning_rate: lr, momentum: mom, batch_size:bs, l2_decay:l2};
}
var trainer = new Trainer(net, trainer_def);
var cand = {};
cand.acc = [];
cand.accv = 0; // this will maintained as sum(acc) for convenience
cand.layer_defs = layer_defs;
cand.trainer_def = trainer_def;
cand.net = net;
cand.trainer = trainer;
return cand;
},
// sets this.candidates with this.num_candidates candidate nets
sampleCandidates: function() {
this.candidates = []; // flush, if any
for(var i=0;i<this.num_candidates;i++) {
var cand = this.sampleCandidate();
this.candidates.push(cand);
}
},
step: function() {
// run an example through current candidate
this.iter++;
// step all candidates on a random data point
var fold = this.folds[this.foldix]; // active fold
var dataix = fold.train_ix[randi(0, fold.train_ix.length)];
for(var k=0;k<this.candidates.length;k++) {
var x = this.data[dataix];
var l = this.labels[dataix];
this.candidates[k].trainer.train(x, l);
}
// process consequences: sample new folds, or candidates
var lastiter = this.num_epochs * fold.train_ix.length;
if(this.iter >= lastiter) {
// finished evaluation of this fold. Get final validation
// accuracies, record them, and go on to next fold.
var val_acc = this.evalValErrors();
for(var k=0;k<this.candidates.length;k++) {
var c = this.candidates[k];
c.acc.push(val_acc[k]);
c.accv += val_acc[k];
}
this.iter = 0; // reset step number
this.foldix++; // increment fold
if(this.finish_fold_callback !== null) {
this.finish_fold_callback();
}
if(this.foldix >= this.folds.length) {
// we finished all folds as well! Record these candidates
// and sample new ones to evaluate.
for(var k=0;k<this.candidates.length;k++) {
this.evaluated_candidates.push(this.candidates[k]);
}
// sort evaluated candidates according to accuracy achieved
this.evaluated_candidates.sort(function(a, b) {
return (a.accv / a.acc.length)
> (b.accv / b.acc.length)
? -1 : 1;
});
// and clip only to the top few ones (lets place limit at 3*ensemble_size)
// otherwise there are concerns with keeping these all in memory
// if MagicNet is being evaluated for a very long time
if(this.evaluated_candidates.length > 3 * this.ensemble_size) {
this.evaluated_candidates = this.evaluated_candidates.slice(0, 3 * this.ensemble_size);
}
if(this.finish_batch_callback !== null) {
this.finish_batch_callback();
}
this.sampleCandidates(); // begin with new candidates
this.foldix = 0; // reset this
} else {
// we will go on to another fold. reset all candidates nets
for(var k=0;k<this.candidates.length;k++) {
var c = this.candidates[k];
var net = new Net();
net.makeLayers(c.layer_defs);
var trainer = new Trainer(net, c.trainer_def);
c.net = net;
c.trainer = trainer;
}
}
}
},
evalValErrors: function() {
// evaluate candidates on validation data and return performance of current networks
// as simple list
var vals = [];
var fold = this.folds[this.foldix]; // active fold
for(var k=0;k<this.candidates.length;k++) {
var net = this.candidates[k].net;
var v = 0.0;
for(var q=0;q<fold.test_ix.length;q++) {
var x = this.data[fold.test_ix[q]];
var l = this.labels[fold.test_ix[q]];
net.forward(x);
var yhat = net.getPrediction();
v += (yhat === l ? 1.0 : 0.0); // 0 1 loss
}
v /= fold.test_ix.length; // normalize
vals.push(v);
}
return vals;
},
// returns prediction scores for given test data point, as Vol
// uses an averaged prediction from the best ensemble_size models
// x is a Vol.
predict_soft: function(data) {
// forward prop the best networks
// and accumulate probabilities at last layer into a an output Vol
var eval_candidates = [];
var nv = 0;
if(this.evaluated_candidates.length === 0) {
// not sure what to do here, first batch of nets hasnt evaluated yet
// lets just predict with current candidates.
nv = this.candidates.length;
eval_candidates = this.candidates;
} else {
// forward prop the best networks from evaluated_candidates
nv = Math.min(this.ensemble_size, this.evaluated_candidates.length);
eval_candidates = this.evaluated_candidates
}
// forward nets of all candidates and average the predictions
var xout, n;
for(var j=0;j<nv;j++) {
var net = eval_candidates[j].net;
var x = net.forward(data);
if(j===0) {
xout = x;
n = x.w.length;
} else {
// add it on
for(var d=0;d<n;d++) {
xout.w[d] += x.w[d];
}
}
}
// produce average
for(var d=0;d<n;d++) {
xout.w[d] /= nv;
}
return xout;
},
predict: function(data) {
var xout = this.predict_soft(data);
if(xout.w.length !== 0) {
var stats = maxmin(xout.w);
var predicted_label = stats.maxi;
} else {
var predicted_label = -1; // error out
}
return predicted_label;
},
toJSON: function() {
// dump the top ensemble_size networks as a list
var nv = Math.min(this.ensemble_size, this.evaluated_candidates.length);
var json = {};
json.nets = [];
for(var i=0;i<nv;i++) {
json.nets.push(this.evaluated_candidates[i].net.toJSON());
}
return json;
},
fromJSON: function(json) {
this.ensemble_size = json.nets.length;
this.evaluated_candidates = [];
for(var i=0;i<this.ensemble_size;i++) {
var net = new Net();
net.fromJSON(json.nets[i]);
var dummy_candidate = {};
dummy_candidate.net = net;
this.evaluated_candidates.push(dummy_candidate);
}
},
// callback functions
// called when a fold is finished, while evaluating a batch
onFinishFold: function(f) { this.finish_fold_callback = f; },
// called when a batch of candidates has finished evaluating
onFinishBatch: function(f) { this.finish_batch_callback = f; }
};
convnet.MagicNet = MagicNet;
};
BundleModuleCode['ml/ann']=function (module,exports){
/*******************************************************************************
CONFIG
*******************************************************************************/
// Config
var config = {
warnings: false
};
/*******************************************************************************
ACTIVATION FUNCTIONS
*******************************************************************************/
// https://en.wikipedia.org/wiki/Activation_function
// https://stats.stackexchange.com/questions/115258/comprehensive-list-of-activation-functions-in-neural-networks-with-pros-cons
var activation = {
LOGISTIC: function LOGISTIC (x, derivate) {
var fx = 1 / (1 + Math.exp(-x));
if (!derivate) return fx;
return fx * (1 - fx);
},
TANH: function TANH (x, derivate) {
if (derivate) return 1 - Math.pow(Math.tanh(x), 2);
return Math.tanh(x);
},
IDENTITY: function IDENTITY (x, derivate) {
return derivate ? 1 : x;
},
STEP: function STEP (x, derivate) {
return derivate ? 0 : x > 0 ? 1 : 0;
},
RELU: function RELU (x, derivate) {
if (derivate) return x > 0 ? 1 : 0;
return x > 0 ? x : 0;
},
SOFTSIGN: function SOFTSIGN (x, derivate) {
var d = 1 + Math.abs(x);
if (derivate) return x / Math.pow(d, 2);
return x / d;
},
SINUSOID: function SINUSOID (x, derivate) {
if (derivate) return Math.cos(x);
return Math.sin(x);
},
GAUSSIAN: function GAUSSIAN (x, derivate) {
var d = Math.exp(-Math.pow(x, 2));
if (derivate) return -2 * x * d;
return d;
},
BENT_IDENTITY: function BENT_IDENTITY (x, derivate) {
var d = Math.sqrt(Math.pow(x, 2) + 1);
if (derivate) return x / (2 * d) + 1;
return (d - 1) / 2 + x;
},
BIPOLAR: function BIPOLAR (x, derivate) {
return derivate ? 0 : x > 0 ? 1 : -1;
},
BIPOLAR_SIGMOID: function BIPOLAR_SIGMOID (x, derivate) {
var d = 2 / (1 + Math.exp(-x)) - 1;
if (derivate) return 1 / 2 * (1 + d) * (1 - d);
return d;
},
HARD_TANH: function HARD_TANH (x, derivate) {
if (derivate) return x > -1 && x < 1 ? 1 : 0;
return Math.max(-1, Math.min(1, x));
},
ABSOLUTE: function ABSOLUTE (x, derivate) {
if (derivate) return x < 0 ? -1 : 1;
return Math.abs(x);
},
INVERSE: function INVERSE (x, derivate) {
if (derivate) return -1;
return 1 - x;
},
// https://arxiv.org/pdf/1706.02515.pdf
SELU: function SELU (x, derivate) {
var alpha = 1.6732632423543772848170429916717;
var scale = 1.0507009873554804934193349852946;
var fx = x > 0 ? x : alpha * Math.exp(x) - alpha;
if (derivate) { return x > 0 ? scale : (fx + alpha) * scale; }
return fx * scale;
}
};
/*******************************************************************************
MUTATION
*******************************************************************************/
// https://en.wikipedia.org/wiki/mutation_(genetic_algorithm)
var mutation = {
ADD_NODE: {
name: 'ADD_NODE'
},
SUB_NODE: {
name: 'SUB_NODE',
keep_gates: true
},
ADD_CONN: {
name: 'ADD_CONN'
},
SUB_CONN: {
name: 'REMOVE_CONN'
},
MOD_WEIGHT: {
name: 'MOD_WEIGHT',
min: -1,
max: 1
},
MOD_BIAS: {
name: 'MOD_BIAS',
min: -1,
max: 1
},
MOD_ACTIVATION: {
name: 'MOD_ACTIVATION',
mutateOutput: true,
allowed: [
activation.LOGISTIC,
activation.TANH,
activation.RELU,
activation.IDENTITY,
activation.STEP,
activation.SOFTSIGN,
activation.SINUSOID,
activation.GAUSSIAN,
activation.BENT_IDENTITY,
activation.BIPOLAR,
activation.BIPOLAR_SIGMOID,
activation.HARD_TANH,
activation.ABSOLUTE,
activation.INVERSE,
activation.SELU
]
},
ADD_SELF_CONN: {
name: 'ADD_SELF_CONN'
},
SUB_SELF_CONN: {
name: 'SUB_SELF_CONN'
},
ADD_GATE: {
name: 'ADD_GATE'
},
SUB_GATE: {
name: 'SUB_GATE'
},
ADD_BACK_CONN: {
name: 'ADD_BACK_CONN'
},
SUB_BACK_CONN: {
name: 'SUB_BACK_CONN'
},
SWAP_NODES: {
name: 'SWAP_NODES',
mutateOutput: true
}
};
mutation.ALL = [
mutation.ADD_NODE,
mutation.SUB_NODE,
mutation.ADD_CONN,
mutation.SUB_CONN,
mutation.MOD_WEIGHT,
mutation.MOD_BIAS,
mutation.MOD_ACTIVATION,
mutation.ADD_GATE,
mutation.SUB_GATE,
mutation.ADD_SELF_CONN,
mutation.SUB_SELF_CONN,
mutation.ADD_BACK_CONN,
mutation.SUB_BACK_CONN,
mutation.SWAP_NODES
];
mutation.FFW = [
mutation.ADD_NODE,
mutation.SUB_NODE,
mutation.ADD_CONN,
mutation.SUB_CONN,
mutation.MOD_WEIGHT,
mutation.MOD_BIAS,
mutation.MOD_ACTIVATION,
mutation.SWAP_NODES
];
/*******************************************************************************
SELECTION
*******************************************************************************/
// https://en.wikipedia.org/wiki/Selection_(genetic_algorithm)
var selection = {
FITNESS_PROPORTIONATE: {
name: 'FITNESS_PROPORTIONATE'
},
POWER: {
name: 'POWER',
power: 4
},
TOURNAMENT: {
name: 'TOURNAMENT',
size: 5,
probability: 0.5
}
};
/*******************************************************************************
CROSSOVER
*******************************************************************************/
// https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)
var crossover = {
SINGLE_POINT: {
name: 'SINGLE_POINT',
config: [0.4]
},
TWO_POINT: {
name: 'TWO_POINT',
config: [0.4, 0.9]
},
UNIFORM: {
name: 'UNIFORM'
},
AVERAGE: {
name: 'AVERAGE'
}
};
/*******************************************************************************
COST FUNCTIONS
*******************************************************************************/
// https://en.wikipedia.org/wiki/Loss_function
var cost = {
// Cross entropy error
CROSS_ENTROPY: function (target, output) {
var error = 0;
for (var i = 0; i < output.length; i++) {
// Avoid negative and zero numbers, use 1e-15 http://bit.ly/2p5W29A
error -= target[i] * Math.log(Math.max(output[i], 1e-15)) + (1 - target[i]) * Math.log(1 - Math.max(output[i], 1e-15));
}
return error / output.length;
},
// Mean Squared Error
MSE: function (target, output) {
var error = 0;
for (var i = 0; i < output.length; i++) {
error += Math.pow(target[i] - output[i], 2);
}
return error / output.length;
},
// Binary error
BINARY: function (target, output) {
var misses = 0;
for (var i = 0; i < output.length; i++) {
misses += Math.round(target[i] * 2) !== Math.round(output[i] * 2);
}
return misses;
},
// Mean Absolute Error
MAE: function (target, output) {
var error = 0;
for (var i = 0; i < output.length; i++) {
error += Math.abs(target[i] - output[i]);
}
return error / output.length;
},
// Mean Absolute Percentage Error
MAPE: function (target, output) {
var error = 0;
for (var i = 0; i < output.length; i++) {
error += Math.abs((output[i] - target[i]) / Math.max(target[i], 1e-15));
}
return error / output.length;
},
// Mean Squared Logarithmic Error
MSLE: function (target, output) {
var error = 0;
for (var i = 0; i < output.length; i++) {
error += Math.log(Math.max(target[i], 1e-15)) - Math.log(Math.max(output[i], 1e-15));
}
return error;
},
// Hinge loss, for classifiers
HINGE: function (target, output) {
var error = 0;
for (var i = 0; i < output.length; i++) {
error += Math.max(0, 1 - target[i] * output[i]);
}
return error;
}
};
/*******************************************************************************
GATING
*******************************************************************************/
// Specifies how to gate a connection between two groups of multiple neurons
var gating = {
OUTPUT: {
name: 'OUTPUT'
},
INPUT: {
name: 'INPUT'
},
SELF: {
name: 'SELF'
}
};
/*******************************************************************************
CONNECTION
*******************************************************************************/
// Specifies in what manner two groups are connected
var connection = {
ALL_TO_ALL: {
name: 'OUTPUT'
},
ALL_TO_ELSE: {
name: 'INPUT'
},
ONE_TO_ONE: {
name: 'SELF'
}
};
/*******************************************************************************
RATE
*******************************************************************************/
// https://stackoverflow.com/questions/30033096/what-is-lr-policy-in-caffe/30045244
var rate = {
FIXED: function () {
var func = function (baseRate, iteration) { return baseRate; };
return func;
},
STEP: function (gamma, stepSize) {
gamma = gamma || 0.9;
stepSize = stepSize || 100;
var func = function (baseRate, iteration) {
return baseRate * Math.pow(gamma, Math.floor(iteration / stepSize));
};
return func;
},
EXP: function (gamma) {
gamma = gamma || 0.999;
var func = function (baseRate, iteration) {
return baseRate * Math.pow(gamma, iteration);
};
return func;
},
INV: function (gamma, power) {
gamma = gamma || 0.001;
power = power || 2;
var func = function (baseRate, iteration) {
return baseRate * Math.pow(1 + gamma * iteration, -power);
};
return func;
}
};
/*******************************************************************************
METHODS
*******************************************************************************/
var methods = {
activation: activation,
mutation: mutation,
selection: selection,
crossover: crossover,
cost: cost,
gating: gating,
connection: connection,
rate: rate
};
/*******************************************************************************
CONNECTION
*******************************************************************************/
function Connection (from, to, weight) {
this.from = from;
this.to = to;
this.gain = 1;
this.weight = (typeof weight === 'undefined') ? Math.random() * 0.2 - 0.1 : weight;
this.gater = null;
this.elegibility = 0;
// For tracking momentum
this.previousDeltaWeight = 0;
// Batch training
this.totalDeltaWeight = 0;
this.xtrace = {
nodes: [],
values: []
};
}
Connection.prototype = {
/**
* Converts the connection to a json object
*/
toJSON: function () {
var json = {
weight: this.weight
};
return json;
}
};
/**
* Returns an innovation ID
* https://en.wikipedia.org/wiki/Pairing_function (Cantor pairing function)
*/
Connection.innovationID = function (a, b) {
return 1 / 2 * (a + b) * (a + b + 1) + b;
};
/*******************************************************************************
NETWORK
*******************************************************************************/
/* Easier variable naming */
var mutation = methods.mutation;
function Network (input, output) {
if (typeof input === 'undefined' || typeof output === 'undefined') {
throw new Error('No input or output size given');
}
this.input = input;
this.output = output;
// Store all the node and connection genes
this.nodes = []; // Stored in activation order
this.connections = [];
this.gates = [];
this.selfconns = [];
// Regularization
this.dropout = 0;
// Create input and output nodes
var i;
for (i = 0; i < this.input + this.output; i++) {
var type = i < this.input ? 'input' : 'output';
this.nodes.push(new Node(type));
}
// Connect input nodes with output nodes directly
for (i = 0; i < this.input; i++) {
for (var j = this.input; j < this.output + this.input; j++) {
// https://stats.stackexchange.com/a/248040/147931
var weight = Math.random() * this.input * Math.sqrt(2 / this.input);
this.connect(this.nodes[i], this.nodes[j], weight);
}
}
}
Network.prototype = {
/**
* Activates the network
*/
activate: function (input, training) {
var output = [];
// Activate nodes chronologically
for (var i = 0; i < this.nodes.length; i++) {
if (this.nodes[i].type === 'input') {
this.nodes[i].activate(input[i]);
} else if (this.nodes[i].type === 'output') {
var activation = this.nodes[i].activate();
output.push(activation);
} else {
if (training) this.nodes[i].mask = Math.random() < this.dropout ? 0 : 1;
this.nodes[i].activate();
}
}
return output;
},
/**
* Activates the network without calculating elegibility traces and such
*/
noTraceActivate: function (input) {
var output = [];
// Activate nodes chronologically
for (var i = 0; i < this.nodes.length; i++) {
if (this.nodes[i].type === 'input') {
this.nodes[i].noTraceActivate(input[i]);
} else if (this.nodes[i].type === 'output') {
var activation = this.nodes[i].noTraceActivate();
output.push(activation);
} else {
this.nodes[i].noTraceActivate();
}
}
return output;
},
/**
* Backpropagate the network
*/
propagate: function (rate, momentum, update, target) {
if (typeof target === 'undefined' || target.length !== this.output) {
throw new Error('Output target length should match network output length');
}
var targetIndex = target.length;
// Propagate output nodes
var i;
for (i = this.nodes.length - 1; i >= this.nodes.length - this.output; i--) {
this.nodes[i].propagate(rate, momentum, update, target[--targetIndex]);
}
// Propagate hidden and input nodes
for (i = this.nodes.length - this.output - 1; i >= this.input; i--) {
this.nodes[i].propagate(rate, momentum, update);
}
},
/**
* Clear the context of the network
*/
clear: function () {
for (var i = 0; i < this.nodes.length; i++) {
this.nodes[i].clear();
}
},
/**
* Connects the from node to the to node
*/
connect: function (from, to, weight) {
var connections = from.connect(to, weight);
for (var i = 0; i < connections.length; i++) {
var connection = connections[i];
if (from !== to) {
this.connections.push(connection);
} else {
this.selfconns.push(connection);
}
}
return connections;
},
/**
* Disconnects the from node from the to node
*/
disconnect: function (from, to) {
// Delete the connection in the network's connection array
var connections = from === to ? this.selfconns : this.connections;
for (var i = 0; i < connections.length; i++) {
var connection = connections[i];
if (connection.from === from && connection.to === to) {
if (connection.gater !== null) this.ungate(connection);
connections.splice(i, 1);
break;
}
}
// Delete the connection at the sending and receiving neuron
from.disconnect(to);
},
/**
* Gate a connection with a node
*/
gate: function (node, connection) {
if (this.nodes.indexOf(node) === -1) {
throw new Error('This node is not part of the network!');
} else if (connection.gater != null) {
if (config.warnings) console.warn('This connection is already gated!');
return;
}
node.gate(connection);
this.gates.push(connection);
},
/**
* Remove the gate of a connection
*/
ungate: function (connection) {
var index = this.gates.indexOf(connection);
if (index === -1) {
throw new Error('This connection is not gated!');
}
this.gates.splice(index, 1);
connection.gater.ungate(connection);
},
/**
* Removes a node from the network
*/
remove: function (node) {
var index = this.nodes.indexOf(node);
if (index === -1) {
throw new Error('This node does not exist in the network!');
}
// Keep track of gaters
var gaters = [];
// Remove selfconnections from this.selfconns
this.disconnect(node, node);
// Get all its inputting nodes
var inputs = [];
for (var i = node.connections.in.length - 1; i >= 0; i--) {
var connection = node.connections.in[i];
if (mutation.SUB_NODE.keep_gates && connection.gater !== null && connection.gater !== node) {
gaters.push(connection.gater);
}
inputs.push(connection.from);
this.disconnect(connection.from, node);
}
// Get all its outputing nodes
var outputs = [];
for (i = node.connections.out.length - 1; i >= 0; i--) {
var connection = node.connections.out[i];
if (mutation.SUB_NODE.keep_gates && connection.gater !== null && connection.gater !== node) {
gaters.push(connection.gater);
}
outputs.push(connection.to);
this.disconnect(node, connection.to);
}
// Connect the input nodes to the output nodes (if not already connected)
var connections = [];
for (i = 0; i < inputs.length; i++) {
var input = inputs[i];
for (var j = 0; j < outputs.length; j++) {
var output = outputs[j];
if (!input.isProjectingTo(output)) {
var conn = this.connect(input, output);
connections.push(conn[0]);
}
}
}
// Gate random connections with gaters
for (i = 0; i < gaters.length; i++) {
if (connections.length === 0) break;
var gater = gaters[i];
var connIndex = Math.floor(Math.random() * connections.length);
this.gate(gater, connections[connIndex]);
connections.splice(connIndex, 1);
}
// Remove gated connections gated by this node
for (i = node.connections.gated.length - 1; i >= 0; i--) {
var conn = node.connections.gated[i];
this.ungate(conn);
}
// Remove selfconnection
this.disconnect(node, node);
// Remove the node from this.nodes
this.nodes.splice(index, 1);
},
/**
* Mutates the network with the given method
*/
mutate: function (method) {
if (typeof method === 'undefined') {
throw new Error('No (correct) mutate method given!');
}
var i, j;
switch (method) {
case mutation.ADD_NODE:
// Look for an existing connection and place a node in between
var connection = this.connections[Math.floor(Math.random() * this.connections.length)];
var gater = connection.gater;
this.disconnect(connection.from, connection.to);
// Insert the new node right before the old connection.to
var toIndex = this.nodes.indexOf(connection.to);
var node = new Node('hidden');
// Random squash function
node.mutate(mutation.MOD_ACTIVATION);
// Place it in this.nodes
var minBound = Math.min(toIndex, this.nodes.length - this.output);
this.nodes.splice(minBound, 0, node);
// Now create two new connections
var newConn1 = this.connect(connection.from, node)[0];
var newConn2 = this.connect(node, connection.to)[0];
// Check if the original connection was gated
if (gater != null) {
this.gate(gater, Math.random() >= 0.5 ? newConn1 : newConn2);
}
break;
case mutation.SUB_NODE:
// Check if there are nodes left to remove
if (this.nodes.length === this.input + this.output) {
if (config.warnings) console.warn('No more nodes left to remove!');
break;
}
// Select a node which isn't an input or output node
var index = Math.floor(Math.random() * (this.nodes.length - this.output - this.input) + this.input);
this.remove(this.nodes[index]);
break;
case mutation.ADD_CONN:
// Create an array of all uncreated (feedforward) connections
var available = [];
for (i = 0; i < this.nodes.length - this.output; i++) {
var node1 = this.nodes[i];
for (j = Math.max(i + 1, this.input); j < this.nodes.length; j++) {
var node2 = this.nodes[j];
if (!node1.isProjectingTo(node2)) available.push([node1, node2]);
}
}
if (available.length === 0) {
if (config.warnings) console.warn('No more connections to be made!');
break;
}
var pair = available[Math.floor(Math.random() * available.length)];
this.connect(pair[0], pair[1]);
break;
case mutation.SUB_CONN:
// List of possible connections that can be removed
var possible = [];
for (i = 0; i < this.connections.length; i++) {
var conn = this.connections[i];
// Check if it is not disabling a node
if (conn.from.connections.out.length > 1 && conn.to.connections.in.length > 1 && this.nodes.indexOf(conn.to) > this.nodes.indexOf(conn.from)) {
possible.push(conn);
}
}
if (possible.length === 0) {
if (config.warnings) console.warn('No connections to remove!');
break;
}
var randomConn = possible[Math.floor(Math.random() * possible.length)];
this.disconnect(randomConn.from, randomConn.to);
break;
case mutation.MOD_WEIGHT:
var allconnections = this.connections.concat(this.selfconns);
var connection = allconnections[Math.floor(Math.random() * allconnections.length)];
var modification = Math.random() * (method.max - method.min) + method.min;
connection.weight += modification;
break;
case mutation.MOD_BIAS:
// Has no effect on input node, so they are excluded
var index = Math.floor(Math.random() * (this.nodes.length - this.input) + this.input);
var node = this.nodes[index];
node.mutate(method);
break;
case mutation.MOD_ACTIVATION:
// Has no effect on input node, so they are excluded
if (!method.mutateOutput && this.input + this.output === this.nodes.length) {
if (config.warnings) console.warn('No nodes that allow mutation of activation function');
break;
}
var index = Math.floor(Math.random() * (this.nodes.length - (method.mutateOutput ? 0 : this.output) - this.input) + this.input);
var node = this.nodes[index];
node.mutate(method);
break;
case mutation.ADD_SELF_CONN:
// Check which nodes aren't selfconnected yet
var possible = [];
for (i = this.input; i < this.nodes.length; i++) {
var node = this.nodes[i];
if (node.connections.self.weight === 0) {
possible.push(node);
}
}
if (possible.length === 0) {
if (config.warnings) console.warn('No more self-connections to add!');
break;
}
// Select a random node
var node = possible[Math.floor(Math.random() * possible.length)];
// Connect it to himself
this.connect(node, node);
break;
case mutation.SUB_SELF_CONN:
if (this.selfconns.length === 0) {
if (config.warnings) console.warn('No more self-connections to remove!');
break;
}
var conn = this.selfconns[Math.floor(Math.random() * this.selfconns.length)];
this.disconnect(conn.from, conn.to);
break;
case mutation.ADD_GATE:
var allconnections = this.connections.concat(this.selfconns);
// Create a list of all non-gated connections
var possible = [];
for (i = 0; i < allconnections.length; i++) {
var conn = allconnections[i];
if (conn.gater === null) {
possible.push(conn);
}
}
if (possible.length === 0) {
if (config.warnings) console.warn('No more connections to gate!');
break;
}
// Select a random gater node and connection, can't be gated by input
var index = Math.floor(Math.random() * (this.nodes.length - this.input) + this.input);
var node = this.nodes[index];
var conn = possible[Math.floor(Math.random() * possible.length)];
// Gate the connection with the node
this.gate(node, conn);
break;
case mutation.SUB_GATE:
// Select a random gated connection
if (this.gates.length === 0) {
if (config.warnings) console.warn('No more connections to ungate!');
break;
}
var index = Math.floor(Math.random() * this.gates.length);
var gatedconn = this.gates[index];
this.ungate(gatedconn);
break;
case mutation.ADD_BACK_CONN:
// Create an array of all uncreated (backfed) connections
var available = [];
for (i = this.input; i < this.nodes.length; i++) {
var node1 = this.nodes[i];
for (j = this.input; j < i; j++) {
var node2 = this.nodes[j];
if (!node1.isProjectingTo(node2)) available.push([node1, node2]);
}
}
if (available.length === 0) {
if (config.warnings) console.warn('No more connections to be made!');
break;
}
var pair = available[Math.floor(Math.random() * available.length)];
this.connect(pair[0], pair[1]);
break;
case mutation.SUB_BACK_CONN:
// List of possible connections that can be removed
var possible = [];
for (i = 0; i < this.connections.length; i++) {
var conn = this.connections[i];
// Check if it is not disabling a node
if (conn.from.connections.out.length > 1 && conn.to.connections.in.length > 1 && this.nodes.indexOf(conn.from) > this.nodes.indexOf(conn.to)) {
possible.push(conn);
}
}
if (possible.length === 0) {
if (config.warnings) console.warn('No connections to remove!');
break;
}
var randomConn = possible[Math.floor(Math.random() * possible.length)];
this.disconnect(randomConn.from, randomConn.to);
break;
case mutation.SWAP_NODES:
// Has no effect on input node, so they are excluded
if ((method.mutateOutput && this.nodes.length - this.input < 2) ||
(!method.mutateOutput && this.nodes.length - this.input - this.output < 2)) {
if (config.warnings) console.warn('No nodes that allow swapping of bias and activation function');
break;
}
var index = Math.floor(Math.random() * (this.nodes.length - (method.mutateOutput ? 0 : this.output) - this.input) + this.input);
var node1 = this.nodes[index];
index = Math.floor(Math.random() * (this.nodes.length - (method.mutateOutput ? 0 : this.output) - this.input) + this.input);
var node2 = this.nodes[index];
var biasTemp = node1.bias;
var squashTemp = node1.squash;
node1.bias = node2.bias;
node1.squash = node2.squash;
node2.bias = biasTemp;
node2.squash = squashTemp;
break;
}
},
/**
* Train the given set to this network
*/
train: function (set, options) {
if (set[0].input.length !== this.input || set[0].output.length !== this.output) {
throw new Error('Dataset input/output size should be same as network input/output size!');
}
options = options || {};
// Warning messages
if (typeof options.rate === 'undefined') {
if (config.warnings) console.warn('Using default learning rate, please define a rate!');
}
if (typeof options.iterations === 'undefined') {
if (config.warnings) console.warn('No target iterations given, running until error is reached!');
}
// Read the options
var targetError = options.error || 0.05;
var cost = options.cost || methods.cost.MSE;
var baseRate = options.rate || 0.3;
var dropout = options.dropout || 0;
var momentum = options.momentum || 0;
var batchSize = options.batchSize || 1; // online learning
var ratePolicy = options.ratePolicy || methods.rate.FIXED();
var start = Date.now();
if (batchSize > set.length) {
throw new Error('Batch size must be smaller or equal to dataset length!');
} else if (typeof options.iterations === 'undefined' && typeof options.error === 'undefined') {
throw new Error('At least one of the following options must be specified: error, iterations');
} else if (typeof options.error === 'undefined') {
targetError = -1; // run until iterations
} else if (typeof options.iterations === 'undefined') {
options.iterations = 0; // run until target error
}
// Save to network
this.dropout = dropout;
if (options.crossValidate) {
var numTrain = Math.ceil((1 - options.crossValidate.testSize) * set.length);
var trainSet = set.slice(0, numTrain);
var testSet = set.slice(numTrain);
}
// Loops the training process
var currentRate = baseRate;
var iteration = 0;
var error = 1;
var i, j, x;
while (error > targetError && (options.iterations === 0 || iteration < options.iterations)) {
if (options.crossValidate && error <= options.crossValidate.testError) break;
iteration++;
// Update the rate
currentRate = ratePolicy(baseRate, iteration);
// Checks if cross validation is enabled
if (options.crossValidate) {
this._trainSet(trainSet, batchSize, currentRate, momentum, cost);
if (options.clear) this.clear();
error = this.test(testSet, cost).error;
if (options.clear) this.clear();
} else {
error = this._trainSet(set, batchSize, currentRate, momentum, cost);
if (options.clear) this.clear();
}
// Checks for options such as scheduled logs and shuffling
if (options.shuffle) {
for (j, x, i = set.length; i; j = Math.floor(Math.random() * i), x = set[--i], set[i] = set[j], set[j] = x);
}
if (options.log && iteration % options.log === 0) {
console.log('iteration', iteration, 'error', error, 'rate', currentRate);
}
if (options.schedule && iteration % options.schedule.iterations === 0) {
options.schedule.function({ error: error, iteration: iteration });
}
}
if (options.clear) this.clear();
if (dropout) {
for (i = 0; i < this.nodes.length; i++) {
if (this.nodes[i].type === 'hidden' || this.nodes[i].type === 'constant') {
this.nodes[i].mask = 1 - this.dropout;
}
}
}
return {
error: error,
iterations: iteration,
time: Date.now() - start
};
},
/**
* Performs one training epoch and returns the error
* private function used in this.train
*/
_trainSet: function (set, batchSize, currentRate, momentum, costFunction) {
var errorSum = 0;
for (var i = 0; i < set.length; i++) {
var input = set[i].input;
var target = set[i].output;
var update = !!((i + 1) % batchSize === 0 || (i + 1) === set.length);
var output = this.activate(input, true);
this.propagate(currentRate, momentum, update, target);
errorSum += costFunction(target, output);
}
return errorSum / set.length;
},
/**
* Tests a set and returns the error and elapsed time
*/
test: function (set, cost) {
if (cost == undefined) cost = methods.cost.MSE;
// Check if dropout is enabled, set correct mask
var i;
if (this.dropout) {
for (i = 0; i < this.nodes.length; i++) {
if (this.nodes[i].type === 'hidden' || this.nodes[i].type === 'constant') {
this.nodes[i].mask = 1 - this.dropout;
}
}
}
var error = 0;
var start = Date.now();
for (i = 0; i < set.length; i++) {
var input = set[i].input;
var target = set[i].output;
var output = this.noTraceActivate(input);
error += cost(target, output);
}
error /= set.length;
var results = {
error: error,
time: Date.now() - start
};
return results;
},
/**
* Creates a json that can be used to create a graph with d3 and webcola
*/
graph: function (width, height) {
var input = 0;
var output = 0;
var json = {
nodes: [],
links: [],
constraints: [{
type: 'alignment',
axis: 'x',
offsets: []
}, {
type: 'alignment',
axis: 'y',
offsets: []
}]
};
var i;
for (i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i];
if (node.type === 'input') {
if (this.input === 1) {
json.constraints[0].offsets.push({
node: i,
offset: 0
});
} else {
json.constraints[0].offsets.push({
node: i,
offset: 0.8 * width / (this.input - 1) * input++
});
}
json.constraints[1].offsets.push({
node: i,
offset: 0
});
} else if (node.type === 'output') {
if (this.output === 1) {
json.constraints[0].offsets.push({
node: i,
offset: 0
});
} else {
json.constraints[0].offsets.push({
node: i,
offset: 0.8 * width / (this.output - 1) * output++
});
}
json.constraints[1].offsets.push({
node: i,
offset: -0.8 * height
});
}
json.nodes.push({
id: i,
name: node.type === 'hidden' ? node.squash.name : node.type.toUpperCase(),
activation: node.activation,
bias: node.bias
});
}
var connections = this.connections.concat(this.selfconns);
for (i = 0; i < connections.length; i++) {
var connection = connections[i];
if (connection.gater == null) {
json.links.push({
source: this.nodes.indexOf(connection.from),
target: this.nodes.indexOf(connection.to),
weight: connection.weight
});
} else {
// Add a gater 'node'
var index = json.nodes.length;
json.nodes.push({
id: index,
activation: connection.gater.activation,
name: 'GATE'
});
json.links.push({
source: this.nodes.indexOf(connection.from),
target: index,
weight: 1 / 2 * connection.weight
});
json.links.push({
source: index,
target: this.nodes.indexOf(connection.to),
weight: 1 / 2 * connection.weight
});
json.links.push({
source: this.nodes.indexOf(connection.gater),
target: index,
weight: connection.gater.activation,
gate: true
});
}
}
return json;
},
/**
* Convert the network to a json object
*/
toJSON: function () {
var json = {
nodes: [],
connections: [],
input: this.input,
output: this.output,
dropout: this.dropout
};
// So we don't have to use expensive .indexOf()
var i;
for (i = 0; i < this.nodes.length; i++) {
this.nodes[i].index = i;
}
for (i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i];
var tojson = node.toJSON();
tojson.index = i;
json.nodes.push(tojson);
if (node.connections.self.weight !== 0) {
var tojson = node.connections.self.toJSON();
tojson.from = i;
tojson.to = i;
tojson.gater = node.connections.self.gater != null ? node.connections.self.gater.index : null;
json.connections.push(tojson);
}
}
for (i = 0; i < this.connections.length; i++) {
var conn = this.connections[i];
var tojson = conn.toJSON();
tojson.from = conn.from.index;
tojson.to = conn.to.index;
tojson.gater = conn.gater != null ? conn.gater.index : null;
json.connections.push(tojson);
}
return json;
},
/**
* Sets the value of a property for every node in this network
*/
set: function (values) {
for (var i = 0; i < this.nodes.length; i++) {
this.nodes[i].bias = values.bias || this.nodes[i].bias;
this.nodes[i].squash = values.squash || this.nodes[i].squash;
}
},
/**
* Evolves the network to reach a lower error on a dataset
*/
evolve: function (set, options) {
if (set[0].input.length !== this.input || set[0].output.length !== this.output) {
throw new Error('Dataset input/output size should be same as network input/output size!');
}
// Read the options
options = options || {};
var targetError = typeof options.error !== 'undefined' ? options.error : 0.05;
var growth = typeof options.growth !== 'undefined' ? options.growth : 0.0001;
var cost = options.cost || methods.cost.MSE;
var amount = options.amount || 1;
var start = Date.now();
if (typeof options.iterations === 'undefined' && typeof options.error === 'undefined') {
throw new Error('At least one of the following options must be specified: error, iterations');
} else if (typeof options.error === 'undefined') {
targetError = -1; // run until iterations
} else if (typeof options.iterations === 'undefined') {
options.iterations = 0; // run until target error
}
var fitnessFunction;
{
// Create the fitness function
fitnessFunction = function (genome) {
var score = 0;
for (var i = 0; i < amount; i++) {
score -= genome.test(set, cost).error;
}
score -= (genome.nodes.length - genome.input - genome.output + genome.connections.length + genome.gates.length) * growth;
score = isNaN(score) ? -Infinity : score; // this can cause problems with fitness proportionate selection
return score / amount;
};
}
// Intialise the NEAT instance
options.network = this;
var neat = new Neat(this.input, this.output, fitnessFunction, options);
var error = -Infinity;
var bestFitness = -Infinity;
var bestGenome;
while (error < -targetError && (options.iterations === 0 || neat.generation < options.iterations)) {
var fittest = neat.evolve();
var fitness = fittest.score;
error = fitness + (fittest.nodes.length - fittest.input - fittest.output + fittest.connections.length + fittest.gates.length) * growth;
if (fitness > bestFitness) {
bestFitness = fitness;
bestGenome = fittest;
}
if (options.log && neat.generation % options.log === 0) {
console.log('iteration', neat.generation, 'fitness', fitness, 'error', -error);
}
if (options.schedule && neat.generation % options.schedule.iterations === 0) {
options.schedule.function({ fitness: fitness, error: -error, iteration: neat.generation });
}
}
if (typeof bestGenome !== 'undefined') {
this.nodes = bestGenome.nodes;
this.connections = bestGenome.connections;
this.selfconns = bestGenome.selfconns;
this.gates = bestGenome.gates;
if (options.clear) this.clear();
}
return {
error: -error,
iterations: neat.generation,
time: Date.now() - start
};
},
/**
* Creates a standalone function of the network which can be run without the
* need of a library
*/
standalone: function () {
var present = [];
var activations = [];
var states = [];
var lines = [];
var functions = [];
var i;
for (i = 0; i < this.input; i++) {
var node = this.nodes[i];
activations.push(node.activation);
states.push(node.state);
}
lines.push('for(var i = 0; i < input.length; i++) A[i] = input[i];');
// So we don't have to use expensive .indexOf()
for (i = 0; i < this.nodes.length; i++) {
this.nodes[i].index = i;
}
for (i = this.input; i < this.nodes.length; i++) {
var node = this.nodes[i];
activations.push(node.activation);
states.push(node.state);
var functionIndex = present.indexOf(node.squash.name);
if (functionIndex === -1) {
functionIndex = present.length;
present.push(node.squash.name);
functions.push(node.squash.toString());
}
var incoming = [];
for (var j = 0; j < node.connections.in.length; j++) {
var conn = node.connections.in[j];
var computation = "A[" + conn.from.index + "] * " + conn.weight;
if (conn.gater != null) {
computation += " * A[" + conn.gater.index + "]";
}
incoming.push(computation);
}
if (node.connections.self.weight) {
var conn = node.connections.self;
var computation = "S[" + i + "] * " + conn.weight;
if (conn.gater != null) {
computation += " * A[" + conn.gater.index + "]";
}
incoming.push(computation);
}
var line1 = "S[" + i + "] = " + incoming.join(' + ') + " + " + node.bias + ";";
var line2 = "A[" + i + "] = F[" + functionIndex + "](S[" + i + "])" + (!node.mask ? ' * ' + node.mask : '') + ";";
lines.push(line1);
lines.push(line2);
}
var output = [];
for (i = this.nodes.length - this.output; i < this.nodes.length; i++) {
output.push("A[" + i + "]");
}
output = "return [" + output.join(',') + "];";
lines.push(output);
var total = '';
total += "var F = [" + functions.toString() + "];\r\n";
total += "var A = [" + activations.toString() + "];\r\n";
total += "var S = [" + states.toString() + "];\r\n";
total += "function activate(input){\r\n" + lines.join('\r\n') + "\r\n}";
return total;
},
/**
* Serialize to send to workers efficiently
*/
serialize: function () {
var activations = [];
var states = [];
var conns = [];
var squashes = [
'LOGISTIC', 'TANH', 'IDENTITY', 'STEP', 'RELU', 'SOFTSIGN', 'SINUSOID',
'GAUSSIAN', 'BENT_IDENTITY', 'BIPOLAR', 'BIPOLAR_SIGMOID', 'HARD_TANH',
'ABSOLUTE', 'INVERSE', 'SELU'
];
conns.push(this.input);
conns.push(this.output);
var i;
for (i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i];
node.index = i;
activations.push(node.activation);
states.push(node.state);
}
for (i = this.input; i < this.nodes.length; i++) {
var node = this.nodes[i];
conns.push(node.index);
conns.push(node.bias);
conns.push(squashes.indexOf(node.squash.name));
conns.push(node.connections.self.weight);
conns.push(node.connections.self.gater == null ? -1 : node.connections.self.gater.index);
for (var j = 0; j < node.connections.in.length; j++) {
var conn = node.connections.in[j];
conns.push(conn.from.index);
conns.push(conn.weight);
conns.push(conn.gater == null ? -1 : conn.gater.index);
}
conns.push(-2); // stop token -> next node
}
return [activations, states, conns];
}
};
/**
* Convert a json object to a network
*/
Network.fromJSON = function (json) {
var network = new Network(json.input, json.output);
network.dropout = json.dropout;
network.nodes = [];
network.connections = [];
var i;
for (i = 0; i < json.nodes.length; i++) {
network.nodes.push(Node.fromJSON(json.nodes[i]));
}
for (i = 0; i < json.connections.length; i++) {
var conn = json.connections[i];
var connection = network.connect(network.nodes[conn.from], network.nodes[conn.to])[0];
connection.weight = conn.weight;
if (conn.gater != null) {
network.gate(network.nodes[conn.gater], connection);
}
}
return network;
};
/**
* Merge two networks into one
*/
Network.merge = function (network1, network2) {
// Create a copy of the networks
network1 = Network.fromJSON(network1.toJSON());
network2 = Network.fromJSON(network2.toJSON());
// Check if output and input size are the same
if (network1.output !== network2.input) {
throw new Error('Output size of network1 should be the same as the input size of network2!');
}
// Redirect all connections from network2 input from network1 output
var i;
for (i = 0; i < network2.connections.length; i++) {
var conn = network2.connections[i];
if (conn.from.type === 'input') {
var index = network2.nodes.indexOf(conn.from);
// redirect
conn.from = network1.nodes[network1.nodes.length - 1 - index];
}
}
// Delete input nodes of network2
for (i = network2.input - 1; i >= 0; i--) {
network2.nodes.splice(i, 1);
}
// Change the node type of network1's output nodes (now hidden)
for (i = network1.nodes.length - network1.output; i < network1.nodes.length; i++) {
network1.nodes[i].type = 'hidden';
}
// Create one network from both networks
network1.connections = network1.connections.concat(network2.connections);
network1.nodes = network1.nodes.concat(network2.nodes);
return network1;
};
/**
* Create an offspring from two parent networks
*/
Network.crossOver = function (network1, network2, equal) {
if (network1.input !== network2.input || network1.output !== network2.output) {
throw new Error("Networks don't have the same input/output size!");
}
// Initialise offspring
var offspring = new Network(network1.input, network1.output);
offspring.connections = [];
offspring.nodes = [];
// Save scores and create a copy
var score1 = network1.score || 0;
var score2 = network2.score || 0;
// Determine offspring node size
var size;
if (equal || score1 === score2) {
var max = Math.max(network1.nodes.length, network2.nodes.length);
var min = Math.min(network1.nodes.length, network2.nodes.length);
size = Math.floor(Math.random() * (max - min + 1) + min);
} else if (score1 > score2) {
size = network1.nodes.length;
} else {
size = network2.nodes.length;
}
// Rename some variables for easier reading
var outputSize = network1.output;
// Set indexes so we don't need indexOf
var i;
for (i = 0; i < network1.nodes.length; i++) {
network1.nodes[i].index = i;
}
for (i = 0; i < network2.nodes.length; i++) {
network2.nodes[i].index = i;
}
// Assign nodes from parents to offspring
for (i = 0; i < size; i++) {
// Determine if an output node is needed
var node;
if (i < size - outputSize) {
var random = Math.random();
node = random >= 0.5 ? network1.nodes[i] : network2.nodes[i];
var other = random < 0.5 ? network1.nodes[i] : network2.nodes[i];
if (typeof node === 'undefined' || node.type === 'output') {
node = other;
}
} else {
if (Math.random() >= 0.5) {
node = network1.nodes[network1.nodes.length + i - size];
} else {
node = network2.nodes[network2.nodes.length + i - size];
}
}
var newNode = new Node();
newNode.bias = node.bias;
newNode.squash = node.squash;
newNode.type = node.type;
offspring.nodes.push(newNode);
}
// Create arrays of connection genes
var n1conns = {};
var n2conns = {};
// Normal connections
for (i = 0; i < network1.connections.length; i++) {
var conn = network1.connections[i];
var data = {
weight: conn.weight,
from: conn.from.index,
to: conn.to.index,
gater: conn.gater != null ? conn.gater.index : -1
};
n1conns[Connection.innovationID(data.from, data.to)] = data;
}
// Selfconnections
for (i = 0; i < network1.selfconns.length; i++) {
var conn = network1.selfconns[i];
var data = {
weight: conn.weight,
from: conn.from.index,
to: conn.to.index,
gater: conn.gater != null ? conn.gater.index : -1
};
n1conns[Connection.innovationID(data.from, data.to)] = data;
}
// Normal connections
for (i = 0; i < network2.connections.length; i++) {
var conn = network2.connections[i];
var data = {
weight: conn.weight,
from: conn.from.index,
to: conn.to.index,
gater: conn.gater != null ? conn.gater.index : -1
};
n2conns[Connection.innovationID(data.from, data.to)] = data;
}
// Selfconnections
for (i = 0; i < network2.selfconns.length; i++) {
var conn = network2.selfconns[i];
var data = {
weight: conn.weight,
from: conn.from.index,
to: conn.to.index,
gater: conn.gater != null ? conn.gater.index : -1
};
n2conns[Connection.innovationID(data.from, data.to)] = data;
}
// Split common conn genes from disjoint or excess conn genes
var connections = [];
var keys1 = Object.keys(n1conns);
var keys2 = Object.keys(n2conns);
for (i = keys1.length - 1; i >= 0; i--) {
// Common gene
if (typeof n2conns[keys1[i]] !== 'undefined') {
var conn = Math.random() >= 0.5 ? n1conns[keys1[i]] : n2conns[keys1[i]];
connections.push(conn);
// Because deleting is expensive, just set it to some value
n2conns[keys1[i]] = undefined;
} else if (score1 >= score2 || equal) {
connections.push(n1conns[keys1[i]]);
}
}
// Excess/disjoint gene
if (score2 >= score1 || equal) {
for (i = 0; i < keys2.length; i++) {
if (typeof n2conns[keys2[i]] !== 'undefined') {
connections.push(n2conns[keys2[i]]);
}
}
}
// Add common conn genes uniformly
for (i = 0; i < connections.length; i++) {
var connData = connections[i];
if (connData.to < size && connData.from < size) {
var from = offspring.nodes[connData.from];
var to = offspring.nodes[connData.to];
var conn = offspring.connect(from, to)[0];
conn.weight = connData.weight;
if (connData.gater !== -1 && connData.gater < size) {
offspring.gate(offspring.nodes[connData.gater], conn);
}
}
}
return offspring;
};
/*******************************************************************************
architect
*******************************************************************************/
var architect = {
/**
* Constructs a network from a given array of connected nodes
*/
Construct: function (list) {
// Create a network
var network = new Network(0, 0);
// Transform all groups into nodes
var nodes = [];
var i;
for (i = 0; i < list.length; i++) {
var j;
if (list[i] instanceof Group) {
for (j = 0; j < list[i].nodes.length; j++) {
nodes.push(list[i].nodes[j]);
}
} else if (list[i] instanceof Layer) {
for (j = 0; j < list[i].nodes.length; j++) {
for (var k = 0; k < list[i].nodes[j].nodes.length; k++) {
nodes.push(list[i].nodes[j].nodes[k]);
}
}
} else if (list[i] instanceof Node) {
nodes.push(list[i]);
}
}
// Determine input and output nodes
var inputs = [];
var outputs = [];
for (i = nodes.length - 1; i >= 0; i--) {
if (nodes[i].type === 'output' || nodes[i].connections.out.length + nodes[i].connections.gated.length === 0) {
nodes[i].type = 'output';
network.output++;
outputs.push(nodes[i]);
nodes.splice(i, 1);
} else if (nodes[i].type === 'input' || !nodes[i].connections.in.length) {
nodes[i].type = 'input';
network.input++;
inputs.push(nodes[i]);
nodes.splice(i, 1);
}
}
// Input nodes are always first, output nodes are always last
nodes = inputs.concat(nodes).concat(outputs);
if (network.input === 0 || network.output === 0) {
throw new Error('Given nodes have no clear input/output node!');
}
for (i = 0; i < nodes.length; i++) {
var j;
for (j = 0; j < nodes[i].connections.out.length; j++) {
network.connections.push(nodes[i].connections.out[j]);
}
for (j = 0; j < nodes[i].connections.gated.length; j++) {
network.gates.push(nodes[i].connections.gated[j]);
}
if (nodes[i].connections.self.weight !== 0) {
network.selfconns.push(nodes[i].connections.self);
}
}
network.nodes = nodes;
return network;
},
/**
* Creates a multilayer perceptron (MLP)
*/
Perceptron: function () {
// Convert arguments to Array
var layers = Array.prototype.slice.call(arguments);
if (layers.length < 3) {
throw new Error('You have to specify at least 3 layers');
}
// Create a list of nodes/groups
var nodes = [];
nodes.push(new Group(layers[0]));
for (var i = 1; i < layers.length; i++) {
var layer = layers[i];
layer = new Group(layer);
nodes.push(layer);
nodes[i - 1].connect(nodes[i], methods.connection.ALL_TO_ALL);
}
// Construct the network
return architect.Construct(nodes);
},
/**
* Creates a randomly connected network
*/
Random: function (input, hidden, output, options) {
options = options || {};
var connections = options.connections || hidden * 2;
var backconnections = options.backconnections || 0;
var selfconnections = options.selfconnections || 0;
var gates = options.gates || 0;
var network = new Network(input, output);
var i;
for (i = 0; i < hidden; i++) {
network.mutate(methods.mutation.ADD_NODE);
}
for (i = 0; i < connections - hidden; i++) {
network.mutate(methods.mutation.ADD_CONN);
}
for (i = 0; i < backconnections; i++) {
network.mutate(methods.mutation.ADD_BACK_CONN);
}
for (i = 0; i < selfconnections; i++) {
network.mutate(methods.mutation.ADD_SELF_CONN);
}
for (i = 0; i < gates; i++) {
network.mutate(methods.mutation.ADD_GATE);
}
return network;
},
/**
* Creates a long short-term memory network
*/
LSTM: function () {
var args = Array.prototype.slice.call(arguments);
if (args.length < 3) {
throw new Error('You have to specify at least 3 layers');
}
var last = args.pop();
var outputLayer;
if (typeof last === 'number') {
outputLayer = new Group(last);
last = {};
} else {
outputLayer = new Group(args.pop()); // last argument
}
outputLayer.set({
type: 'output'
});
var options = {};
options.memoryToMemory = last.memoryToMemory || false;
options.outputToMemory = last.outputToMemory || false;
options.outputToGates = last.outputToGates || false;
options.inputToOutput = last.inputToOutput === undefined ? true : last.inputToOutput;
options.inputToDeep = last.inputToDeep === undefined ? true : last.inputToDeep;
var inputLayer = new Group(args.shift()); // first argument
inputLayer.set({
type: 'input'
});
var blocks = args; // all the arguments in the middle
var nodes = [];
nodes.push(inputLayer);
var previous = inputLayer;
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
// Init required nodes (in activation order)
var inputGate = new Group(block);
var forgetGate = new Group(block);
var memoryCell = new Group(block);
var outputGate = new Group(block);
var outputBlock = i === blocks.length - 1 ? outputLayer : new Group(block);
inputGate.set({
bias: 1
});
forgetGate.set({
bias: 1
});
outputGate.set({
bias: 1
});
// Connect the input with all the nodes
var input = previous.connect(memoryCell, methods.connection.ALL_TO_ALL);
previous.connect(inputGate, methods.connection.ALL_TO_ALL);
previous.connect(outputGate, methods.connection.ALL_TO_ALL);
previous.connect(forgetGate, methods.connection.ALL_TO_ALL);
// Set up internal connections
memoryCell.connect(inputGate, methods.connection.ALL_TO_ALL);
memoryCell.connect(forgetGate, methods.connection.ALL_TO_ALL);
memoryCell.connect(outputGate, methods.connection.ALL_TO_ALL);
var forget = memoryCell.connect(memoryCell, methods.connection.ONE_TO_ONE);
var output = memoryCell.connect(outputBlock, methods.connection.ALL_TO_ALL);
// Set up gates
inputGate.gate(input, methods.gating.INPUT);
forgetGate.gate(forget, methods.gating.SELF);
outputGate.gate(output, methods.gating.OUTPUT);
// Input to all memory cells
if (options.inputToDeep && i > 0) {
var input = inputLayer.connect(memoryCell, methods.connection.ALL_TO_ALL);
inputGate.gate(input, methods.gating.INPUT);
}
// Optional connections
if (options.memoryToMemory) {
var input = memoryCell.connect(memoryCell, methods.connection.ALL_TO_ELSE);
inputGate.gate(input, methods.gating.INPUT);
}
if (options.outputToMemory) {
var input = outputLayer.connect(memoryCell, methods.connection.ALL_TO_ALL);
inputGate.gate(input, methods.gating.INPUT);
}
if (options.outputToGates) {
outputLayer.connect(inputGate, methods.connection.ALL_TO_ALL);
outputLayer.connect(forgetGate, methods.connection.ALL_TO_ALL);
outputLayer.connect(outputGate, methods.connection.ALL_TO_ALL);
}
// Add to array
nodes.push(inputGate);
nodes.push(forgetGate);
nodes.push(memoryCell);
nodes.push(outputGate);
if (i !== blocks.length - 1) nodes.push(outputBlock);
previous = outputBlock;
}
// input to output direct connection
if (options.inputToOutput) {
inputLayer.connect(outputLayer, methods.connection.ALL_TO_ALL);
}
nodes.push(outputLayer);
return architect.Construct(nodes);
},
/**
* Creates a gated recurrent unit network
*/
GRU: function () {
var args = Array.prototype.slice.call(arguments);
if (args.length < 3) {
throw new Error('not enough layers (minimum 3) !!');
}
var inputLayer = new Group(args.shift()); // first argument
var outputLayer = new Group(args.pop()); // last argument
var blocks = args; // all the arguments in the middle
var nodes = [];
nodes.push(inputLayer);
var previous = inputLayer;
for (var i = 0; i < blocks.length; i++) {
var layer = new Layer.GRU(blocks[i]);
previous.connect(layer);
previous = layer;
nodes.push(layer);
}
previous.connect(outputLayer);
nodes.push(outputLayer);
return architect.Construct(nodes);
},
/**
* Creates a hopfield network of the given size
*/
Hopfield: function (size) {
var input = new Group(size);
var output = new Group(size);
input.connect(output, methods.connection.ALL_TO_ALL);
input.set({
type: 'input'
});
output.set({
squash: methods.activation.STEP,
type: 'output'
});
var network = new architect.Construct([input, output]);
return network;
},
/**
* Creates a NARX network (remember previous inputs/outputs)
*/
NARX: function (inputSize, hiddenLayers, outputSize, previousInput, previousOutput) {
if (!Array.isArray(hiddenLayers)) {
hiddenLayers = [hiddenLayers];
}
var nodes = [];
var input = new Layer.Dense(inputSize);
var inputMemory = new Layer.Memory(inputSize, previousInput);
var hidden = [];
var output = new Layer.Dense(outputSize);
var outputMemory = new Layer.Memory(outputSize, previousOutput);
nodes.push(input);
nodes.push(outputMemory);
for (var i = 0; i < hiddenLayers.length; i++) {
var hiddenLayer = new Layer.Dense(hiddenLayers[i]);
hidden.push(hiddenLayer);
nodes.push(hiddenLayer);
if (typeof hidden[i - 1] !== 'undefined') {
hidden[i - 1].connect(hiddenLayer, methods.connection.ALL_TO_ALL);
}
}
nodes.push(inputMemory);
nodes.push(output);
input.connect(hidden[0], methods.connection.ALL_TO_ALL);
input.connect(inputMemory, methods.connection.ONE_TO_ONE, 1);
inputMemory.connect(hidden[0], methods.connection.ALL_TO_ALL);
hidden[hidden.length - 1].connect(output, methods.connection.ALL_TO_ALL);
output.connect(outputMemory, methods.connection.ONE_TO_ONE, 1);
outputMemory.connect(hidden[0], methods.connection.ALL_TO_ALL);
input.set({
type: 'input'
});
output.set({
type: 'output'
});
return architect.Construct(nodes);
}
};
/*******************************************************************************
NODE
*******************************************************************************/
function Node (type) {
this.bias = (type === 'input') ? 0 : Math.random() * 0.2 - 0.1;
this.squash = methods.activation.LOGISTIC;
this.type = type || 'hidden';
this.activation = 0;
this.state = 0;
this.old = 0;
// For dropout
this.mask = 1;
// For tracking momentum
this.previousDeltaBias = 0;
// Batch training
this.totalDeltaBias = 0;
this.connections = {
in: [],
out: [],
gated: [],
self: new Connection(this, this, 0)
};
// Data for backpropagation
this.error = {
responsibility: 0,
projected: 0,
gated: 0
};
}
Node.prototype = {
/**
* Activates the node
*/
activate: function (input) {
// Check if an input is given
if (typeof input !== 'undefined') {
this.activation = input;
return this.activation;
}
this.old = this.state;
// All activation sources coming from the node itself
this.state = this.connections.self.gain * this.connections.self.weight * this.state + this.bias;
// Activation sources coming from connections
var i;
for (i = 0; i < this.connections.in.length; i++) {
var connection = this.connections.in[i];
this.state += connection.from.activation * connection.weight * connection.gain;
}
// Squash the values received
this.activation = this.squash(this.state) * this.mask;
this.derivative = this.squash(this.state, true);
// Update traces
var nodes = [];
var influences = [];
for (i = 0; i < this.connections.gated.length; i++) {
var conn = this.connections.gated[i];
var node = conn.to;
var index = nodes.indexOf(node);
if (index > -1) {
influences[index] += conn.weight * conn.from.activation;
} else {
nodes.push(node);
influences.push(conn.weight * conn.from.activation +
(node.connections.self.gater === this ? node.old : 0));
}
// Adjust the gain to this nodes' activation
conn.gain = this.activation;
}
for (i = 0; i < this.connections.in.length; i++) {
var connection = this.connections.in[i];
// Elegibility trace
connection.elegibility = this.connections.self.gain * this.connections.self.weight *
connection.elegibility + connection.from.activation * connection.gain;
// Extended trace
for (var j = 0; j < nodes.length; j++) {
var node = nodes[j];
var influence = influences[j];
var index = connection.xtrace.nodes.indexOf(node);
if (index > -1) {
connection.xtrace.values[index] = node.connections.self.gain * node.connections.self.weight *
connection.xtrace.values[index] + this.derivative * connection.elegibility * influence;
} else {
// Does not exist there yet, might be through mutation
connection.xtrace.nodes.push(node);
connection.xtrace.values.push(this.derivative * connection.elegibility * influence);
}
}
}
return this.activation;
},
/**
* Activates the node without calculating elegibility traces and such
*/
noTraceActivate: function (input) {
// Check if an input is given
if (typeof input !== 'undefined') {
this.activation = input;
return this.activation;
}
// All activation sources coming from the node itself
this.state = this.connections.self.gain * this.connections.self.weight * this.state + this.bias;
// Activation sources coming from connections
var i;
for (i = 0; i < this.connections.in.length; i++) {
var connection = this.connections.in[i];
this.state += connection.from.activation * connection.weight * connection.gain;
}
// Squash the values received
this.activation = this.squash(this.state);
for (i = 0; i < this.connections.gated.length; i++) {
this.connections.gated[i].gain = this.activation;
}
return this.activation;
},
/**
* Back-propagate the error, aka learn
*/
propagate: function (rate, momentum, update, target) {
momentum = momentum || 0;
rate = rate || 0.3;
// Error accumulator
var error = 0;
// Output nodes get their error from the enviroment
if (this.type === 'output') {
this.error.responsibility = this.error.projected = target - this.activation;
} else { // the rest of the nodes compute their error responsibilities by backpropagation
// error responsibilities from all the connections projected from this node
var i;
for (i = 0; i < this.connections.out.length; i++) {
var connection = this.connections.out[i];
var node = connection.to;
// Eq. 21
error += node.error.responsibility * connection.weight * connection.gain;
}
// Projected error responsibility
this.error.projected = this.derivative * error;
// Error responsibilities from all connections gated by this neuron
error = 0;
for (i = 0; i < this.connections.gated.length; i++) {
var conn = this.connections.gated[i];
var node = conn.to;
var influence = node.connections.self.gater === this ? node.old : 0;
influence += conn.weight * conn.from.activation;
error += node.error.responsibility * influence;
}
// Gated error responsibility
this.error.gated = this.derivative * error;
// Error responsibility
this.error.responsibility = this.error.projected + this.error.gated;
}
if (this.type === 'constant') return;
// Adjust all the node's incoming connections
for (i = 0; i < this.connections.in.length; i++) {
var connection = this.connections.in[i];
var gradient = this.error.projected * connection.elegibility;
for (var j = 0; j < connection.xtrace.nodes.length; j++) {
var node = connection.xtrace.nodes[j];
var value = connection.xtrace.values[j];
gradient += node.error.responsibility * value;
}
// Adjust weight
var deltaWeight = rate * gradient * this.mask;
connection.totalDeltaWeight += deltaWeight;
if (update) {
connection.totalDeltaWeight += momentum * connection.previousDeltaWeight;
connection.weight += connection.totalDeltaWeight;
connection.previousDeltaWeight = connection.totalDeltaWeight;
connection.totalDeltaWeight = 0;
}
}
// Adjust bias
var deltaBias = rate * this.error.responsibility;
this.totalDeltaBias += deltaBias;
if (update) {
this.totalDeltaBias += momentum * this.previousDeltaBias;
this.bias += this.totalDeltaBias;
this.previousDeltaBias = this.totalDeltaBias;
this.totalDeltaBias = 0;
}
},
/**
* Creates a connection from this node to the given node
*/
connect: function (target, weight) {
var connections = [];
if (typeof target.bias !== 'undefined') { // must be a node!
if (target === this) {
// Turn on the self connection by setting the weight
if (this.connections.self.weight !== 0) {
if (config.warnings) console.warn('This connection already exists!');
} else {
this.connections.self.weight = weight || 1;
}
connections.push(this.connections.self);
} else if (this.isProjectingTo(target)) {
throw new Error('Already projecting a connection to this node!');
} else {
var connection = new Connection(this, target, weight);
target.connections.in.push(connection);
this.connections.out.push(connection);
connections.push(connection);
}
} else { // should be a group
for (var i = 0; i < target.nodes.length; i++) {
var connection = new Connection(this, target.nodes[i], weight);
target.nodes[i].connections.in.push(connection);
this.connections.out.push(connection);
target.connections.in.push(connection);
connections.push(connection);
}
}
return connections;
},
/**
* Disconnects this node from the other node
*/
disconnect: function (node, twosided) {
if (this === node) {
this.connections.self.weight = 0;
return;
}
for (var i = 0; i < this.connections.out.length; i++) {
var conn = this.connections.out[i];
if (conn.to === node) {
this.connections.out.splice(i, 1);
var j = conn.to.connections.in.indexOf(conn);
conn.to.connections.in.splice(j, 1);
if (conn.gater !== null) conn.gater.ungate(conn);
break;
}
}
if (twosided) {
node.disconnect(this);
}
},
/**
* Make this node gate a connection
*/
gate: function (connections) {
if (!Array.isArray(connections)) {
connections = [connections];
}
for (var i = 0; i < connections.length; i++) {
var connection = connections[i];
this.connections.gated.push(connection);
connection.gater = this;
}
},
/**
* Removes the gates from this node from the given connection(s)
*/
ungate: function (connections) {
if (!Array.isArray(connections)) {
connections = [connections];
}
for (var i = connections.length - 1; i >= 0; i--) {
var connection = connections[i];
var index = this.connections.gated.indexOf(connection);
this.connections.gated.splice(index, 1);
connection.gater = null;
connection.gain = 1;
}
},
/**
* Clear the context of the node
*/
clear: function () {
for (var i = 0; i < this.connections.in.length; i++) {
var connection = this.connections.in[i];
connection.elegibility = 0;
connection.xtrace = {
nodes: [],
values: []
};
}
for (i = 0; i < this.connections.gated.length; i++) {
var conn = this.connections.gated[i];
conn.gain = 0;
}
this.error.responsibility = this.error.projected = this.error.gated = 0;
this.old = this.state = this.activation = 0;
},
/**
* Mutates the node with the given method
*/
mutate: function (method) {
if (typeof method === 'undefined') {
throw new Error('No mutate method given!');
} else if (!(method.name in methods.mutation)) {
throw new Error('This method does not exist!');
}
switch (method) {
case methods.mutation.MOD_ACTIVATION:
// Can't be the same squash
var squash = method.allowed[(method.allowed.indexOf(this.squash) + Math.floor(Math.random() * (method.allowed.length - 1)) + 1) % method.allowed.length];
this.squash = squash;
break;
case methods.mutation.MOD_BIAS:
var modification = Math.random() * (method.max - method.min) + method.min;
this.bias += modification;
break;
}
},
/**
* Checks if this node is projecting to the given node
*/
isProjectingTo: function (node) {
if (node === this && this.connections.self.weight !== 0) return true;
for (var i = 0; i < this.connections.out.length; i++) {
var conn = this.connections.out[i];
if (conn.to === node) {
return true;
}
}
return false;
},
/**
* Checks if the given node is projecting to this node
*/
isProjectedBy: function (node) {
if (node === this && this.connections.self.weight !== 0) return true;
for (var i = 0; i < this.connections.in.length; i++) {
var conn = this.connections.in[i];
if (conn.from === node) {
return true;
}
}
return false;
},
/**
* Converts the node to a json object
*/
toJSON: function () {
var json = {
bias: this.bias,
type: this.type,
squash: this.squash.name,
mask: this.mask
};
return json;
}
};
/**
* Convert a json object to a node
*/
Node.fromJSON = function (json) {
var node = new Node();
node.bias = json.bias;
node.type = json.type;
node.mask = json.mask;
node.squash = methods.activation[json.squash];
return node;
};
/*******************************************************************************
Group
*******************************************************************************/
function Layer () {
this.output = null;
this.nodes = [];
this.connections = { in: [],
out: [],
self: []
};
}
Layer.prototype = {
/**
* Activates all the nodes in the group
*/
activate: function (value) {
var values = [];
if (typeof value !== 'undefined' && value.length !== this.nodes.length) {
throw new Error('Array with values should be same as the amount of nodes!');
}
for (var i = 0; i < this.nodes.length; i++) {
var activation;
if (typeof value === 'undefined') {
activation = this.nodes[i].activate();
} else {
activation = this.nodes[i].activate(value[i]);
}
values.push(activation);
}
return values;
},
/**
* Propagates all the node in the group
*/
propagate: function (rate, momentum, target) {
if (typeof target !== 'undefined' && target.length !== this.nodes.length) {
throw new Error('Array with values should be same as the amount of nodes!');
}
for (var i = this.nodes.length - 1; i >= 0; i--) {
if (typeof target === 'undefined') {
this.nodes[i].propagate(rate, momentum, true);
} else {
this.nodes[i].propagate(rate, momentum, true, target[i]);
}
}
},
/**
* Connects the nodes in this group to nodes in another group or just a node
*/
connect: function (target, method, weight) {
var connections;
if (target instanceof Group || target instanceof Node) {
connections = this.output.connect(target, method, weight);
} else if (target instanceof Layer) {
connections = target.input(this, method, weight);
}
return connections;
},
/**
* Make nodes from this group gate the given connection(s)
*/
gate: function (connections, method) {
this.output.gate(connections, method);
},
/**
* Sets the value of a property for every node
*/
set: function (values) {
for (var i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i];
if (node instanceof Node) {
if (typeof values.bias !== 'undefined') {
node.bias = values.bias;
}
node.squash = values.squash || node.squash;
node.type = values.type || node.type;
} else if (node instanceof Group) {
node.set(values);
}
}
},
/**
* Disconnects all nodes from this group from another given group/node
*/
disconnect: function (target, twosided) {
twosided = twosided || false;
// In the future, disconnect will return a connection so indexOf can be used
var i, j, k;
if (target instanceof Group) {
for (i = 0; i < this.nodes.length; i++) {
for (j = 0; j < target.nodes.length; j++) {
this.nodes[i].disconnect(target.nodes[j], twosided);
for (k = this.connections.out.length - 1; k >= 0; k--) {
var conn = this.connections.out[k];
if (conn.from === this.nodes[i] && conn.to === target.nodes[j]) {
this.connections.out.splice(k, 1);
break;
}
}
if (twosided) {
for (k = this.connections.in.length - 1; k >= 0; k--) {
var conn = this.connections.in[k];
if (conn.from === target.nodes[j] && conn.to === this.nodes[i]) {
this.connections.in.splice(k, 1);
break;
}
}
}
}
}
} else if (target instanceof Node) {
for (i = 0; i < this.nodes.length; i++) {
this.nodes[i].disconnect(target, twosided);
for (j = this.connections.out.length - 1; j >= 0; j--) {
var conn = this.connections.out[j];
if (conn.from === this.nodes[i] && conn.to === target) {
this.connections.out.splice(j, 1);
break;
}
}
if (twosided) {
for (k = this.connections.in.length - 1; k >= 0; k--) {
var conn = this.connections.in[k];
if (conn.from === target && conn.to === this.nodes[i]) {
this.connections.in.splice(k, 1);
break;
}
}
}
}
}
},
/**
* Clear the context of this group
*/
clear: function () {
for (var i = 0; i < this.nodes.length; i++) {
this.nodes[i].clear();
}
}
};
Layer.Dense = function (size) {
// Create the layer
var layer = new Layer();
// Init required nodes (in activation order)
var block = new Group(size);
layer.nodes.push(block);
layer.output = block;
layer.input = function (from, method, weight) {
if (from instanceof Layer) from = from.output;
method = method || methods.connection.ALL_TO_ALL;
return from.connect(block, method, weight);
};
return layer;
};
Layer.LSTM = function (size) {
// Create the layer
var layer = new Layer();
// Init required nodes (in activation order)
var inputGate = new Group(size);
var forgetGate = new Group(size);
var memoryCell = new Group(size);
var outputGate = new Group(size);
var outputBlock = new Group(size);
inputGate.set({
bias: 1
});
forgetGate.set({
bias: 1
});
outputGate.set({
bias: 1
});
// Set up internal connections
memoryCell.connect(inputGate, methods.connection.ALL_TO_ALL);
memoryCell.connect(forgetGate, methods.connection.ALL_TO_ALL);
memoryCell.connect(outputGate, methods.connection.ALL_TO_ALL);
var forget = memoryCell.connect(memoryCell, methods.connection.ONE_TO_ONE);
var output = memoryCell.connect(outputBlock, methods.connection.ALL_TO_ALL);
// Set up gates
forgetGate.gate(forget, methods.gating.SELF);
outputGate.gate(output, methods.gating.OUTPUT);
// Add to nodes array
layer.nodes = [inputGate, forgetGate, memoryCell, outputGate, outputBlock];
// Define output
layer.output = outputBlock;
layer.input = function (from, method, weight) {
if (from instanceof Layer) from = from.output;
method = method || methods.connection.ALL_TO_ALL;
var connections = [];
var input = from.connect(memoryCell, method, weight);
connections = connections.concat(input);
connections = connections.concat(from.connect(inputGate, method, weight));
connections = connections.concat(from.connect(outputGate, method, weight));
connections = connections.concat(from.connect(forgetGate, method, weight));
inputGate.gate(input, methods.gating.INPUT);
return connections;
};
return layer;
};
Layer.GRU = function (size) {
// Create the layer
var layer = new Layer();
var updateGate = new Group(size);
var inverseUpdateGate = new Group(size);
var resetGate = new Group(size);
var memoryCell = new Group(size);
var output = new Group(size);
var previousOutput = new Group(size);
previousOutput.set({
bias: 0,
squash: methods.activation.IDENTITY,
type: 'constant'
});
memoryCell.set({
squash: methods.activation.TANH
});
inverseUpdateGate.set({
bias: 0,
squash: methods.activation.INVERSE,
type: 'constant'
});
updateGate.set({
bias: 1
});
resetGate.set({
bias: 0
});
// Update gate calculation
previousOutput.connect(updateGate, methods.connection.ALL_TO_ALL);
// Inverse update gate calculation
updateGate.connect(inverseUpdateGate, methods.connection.ONE_TO_ONE, 1);
// Reset gate calculation
previousOutput.connect(resetGate, methods.connection.ALL_TO_ALL);
// Memory calculation
var reset = previousOutput.connect(memoryCell, methods.connection.ALL_TO_ALL);
resetGate.gate(reset, methods.gating.OUTPUT); // gate
// Output calculation
var update1 = previousOutput.connect(output, methods.connection.ALL_TO_ALL);
var update2 = memoryCell.connect(output, methods.connection.ALL_TO_ALL);
updateGate.gate(update1, methods.gating.OUTPUT);
inverseUpdateGate.gate(update2, methods.gating.OUTPUT);
// Previous output calculation
output.connect(previousOutput, methods.connection.ONE_TO_ONE, 1);
// Add to nodes array
layer.nodes = [updateGate, inverseUpdateGate, resetGate, memoryCell, output, previousOutput];
layer.output = output;
layer.input = function (from, method, weight) {
if (from instanceof Layer) from = from.output;
method = method || methods.connection.ALL_TO_ALL;
var connections = [];
connections = connections.concat(from.connect(updateGate, method, weight));
connections = connections.concat(from.connect(resetGate, method, weight));
connections = connections.concat(from.connect(memoryCell, method, weight));
return connections;
};
return layer;
};
Layer.Memory = function (size, memory) {
// Create the layer
var layer = new Layer();
// Because the output can only be one group, we have to put the nodes all in óne group
var previous = null;
var i;
for (i = 0; i < memory; i++) {
var block = new Group(size);
block.set({
squash: methods.activation.IDENTITY,
bias: 0,
type: 'constant'
});
if (previous != null) {
previous.connect(block, methods.connection.ONE_TO_ONE, 1);
}
layer.nodes.push(block);
previous = block;
}
layer.nodes.reverse();
for (i = 0; i < layer.nodes.length; i++) {
layer.nodes[i].nodes.reverse();
}
// Because output can only be óne group, fit all memory nodes in óne group
var outputGroup = new Group(0);
for (var group in layer.nodes) {
outputGroup.nodes = outputGroup.nodes.concat(layer.nodes[group].nodes);
}
layer.output = outputGroup;
layer.input = function (from, method, weight) {
if (from instanceof Layer) from = from.output;
method = method || methods.connection.ALL_TO_ALL;
if (from.nodes.length !== layer.nodes[layer.nodes.length - 1].nodes.length) {
throw new Error('Previous layer size must be same as memory size');
}
return from.connect(layer.nodes[layer.nodes.length - 1], methods.connection.ONE_TO_ONE, 1);
};
return layer;
};
/*******************************************************************************
Group
*******************************************************************************/
function Group (size) {
this.nodes = [];
this.connections = {
in: [],
out: [],
self: []
};
for (var i = 0; i < size; i++) {
this.nodes.push(new Node());
}
}
Group.prototype = {
/**
* Activates all the nodes in the group
*/
activate: function (value) {
var values = [];
if (typeof value !== 'undefined' && value.length !== this.nodes.length) {
throw new Error('Array with values should be same as the amount of nodes!');
}
for (var i = 0; i < this.nodes.length; i++) {
var activation;
if (typeof value === 'undefined') {
activation = this.nodes[i].activate();
} else {
activation = this.nodes[i].activate(value[i]);
}
values.push(activation);
}
return values;
},
/**
* Propagates all the node in the group
*/
propagate: function (rate, momentum, target) {
if (typeof target !== 'undefined' && target.length !== this.nodes.length) {
throw new Error('Array with values should be same as the amount of nodes!');
}
for (var i = this.nodes.length - 1; i >= 0; i--) {
if (typeof target === 'undefined') {
this.nodes[i].propagate(rate, momentum, true);
} else {
this.nodes[i].propagate(rate, momentum, true, target[i]);
}
}
},
/**
* Connects the nodes in this group to nodes in another group or just a node
*/
connect: function (target, method, weight) {
var connections = [];
var i, j;
if (target instanceof Group) {
if (typeof method === 'undefined') {
if (this !== target) {
if (config.warnings) console.warn('No group connection specified, using ALL_TO_ALL');
method = methods.connection.ALL_TO_ALL;
} else {
if (config.warnings) console.warn('No group connection specified, using ONE_TO_ONE');
method = methods.connection.ONE_TO_ONE;
}
}
if (method === methods.connection.ALL_TO_ALL || method === methods.connection.ALL_TO_ELSE) {
for (i = 0; i < this.nodes.length; i++) {
for (j = 0; j < target.nodes.length; j++) {
if (method === methods.connection.ALL_TO_ELSE && this.nodes[i] === target.nodes[j]) continue;
var connection = this.nodes[i].connect(target.nodes[j], weight);
this.connections.out.push(connection[0]);
target.connections.in.push(connection[0]);
connections.push(connection[0]);
}
}
} else if (method === methods.connection.ONE_TO_ONE) {
if (this.nodes.length !== target.nodes.length) {
throw new Error('From and To group must be the same size!');
}
for (i = 0; i < this.nodes.length; i++) {
var connection = this.nodes[i].connect(target.nodes[i], weight);
this.connections.self.push(connection[0]);
connections.push(connection[0]);
}
}
} else if (target instanceof Layer) {
connections = target.input(this, method, weight);
} else if (target instanceof Node) {
for (i = 0; i < this.nodes.length; i++) {
var connection = this.nodes[i].connect(target, weight);
this.connections.out.push(connection[0]);
connections.push(connection[0]);
}
}
return connections;
},
/**
* Make nodes from this group gate the given connection(s)
*/
gate: function (connections, method) {
if (typeof method === 'undefined') {
throw new Error('Please specify Gating.INPUT, Gating.OUTPUT');
}
if (!Array.isArray(connections)) {
connections = [connections];
}
var nodes1 = [];
var nodes2 = [];
var i, j;
for (i = 0; i < connections.length; i++) {
var connection = connections[i];
if (!nodes1.includes(connection.from)) nodes1.push(connection.from);
if (!nodes2.includes(connection.to)) nodes2.push(connection.to);
}
switch (method) {
case methods.gating.INPUT:
for (i = 0; i < nodes2.length; i++) {
var node = nodes2[i];
var gater = this.nodes[i % this.nodes.length];
for (j = 0; j < node.connections.in.length; j++) {
var conn = node.connections.in[j];
if (connections.includes(conn)) {
gater.gate(conn);
}
}
}
break;
case methods.gating.OUTPUT:
for (i = 0; i < nodes1.length; i++) {
var node = nodes1[i];
var gater = this.nodes[i % this.nodes.length];
for (j = 0; j < node.connections.out.length; j++) {
var conn = node.connections.out[j];
if (connections.includes(conn)) {
gater.gate(conn);
}
}
}
break;
case methods.gating.SELF:
for (i = 0; i < nodes1.length; i++) {
var node = nodes1[i];
var gater = this.nodes[i % this.nodes.length];
if (connections.includes(node.connections.self)) {
gater.gate(node.connections.self);
}
}
}
},
/**
* Sets the value of a property for every node
*/
set: function (values) {
for (var i = 0; i < this.nodes.length; i++) {
if (typeof values.bias !== 'undefined') {
this.nodes[i].bias = values.bias;
}
this.nodes[i].squash = values.squash || this.nodes[i].squash;
this.nodes[i].type = values.type || this.nodes[i].type;
}
},
/**
* Disconnects all nodes from this group from another given group/node
*/
disconnect: function (target, twosided) {
twosided = twosided || false;
// In the future, disconnect will return a connection so indexOf can be used
var i, j, k;
if (target instanceof Group) {
for (i = 0; i < this.nodes.length; i++) {
for (j = 0; j < target.nodes.length; j++) {
this.nodes[i].disconnect(target.nodes[j], twosided);
for (k = this.connections.out.length - 1; k >= 0; k--) {
var conn = this.connections.out[k];
if (conn.from === this.nodes[i] && conn.to === target.nodes[j]) {
this.connections.out.splice(k, 1);
break;
}
}
if (twosided) {
for (k = this.connections.in.length - 1; k >= 0; k--) {
var conn = this.connections.in[k];
if (conn.from === target.nodes[j] && conn.to === this.nodes[i]) {
this.connections.in.splice(k, 1);
break;
}
}
}
}
}
} else if (target instanceof Node) {
for (i = 0; i < this.nodes.length; i++) {
this.nodes[i].disconnect(target, twosided);
for (j = this.connections.out.length - 1; j >= 0; j--) {
var conn = this.connections.out[j];
if (conn.from === this.nodes[i] && conn.to === target) {
this.connections.out.splice(j, 1);
break;
}
}
if (twosided) {
for (j = this.connections.in.length - 1; j >= 0; j--) {
var conn = this.connections.in[j];
if (conn.from === target && conn.to === this.nodes[i]) {
this.connections.in.splice(j, 1);
break;
}
}
}
}
}
},
/**
* Clear the context of this group
*/
clear: function () {
for (var i = 0; i < this.nodes.length; i++) {
this.nodes[i].clear();
}
}
};
/* Easier variable naming */
var selection = methods.selection;
/*******************************************************************************
NEAT
*******************************************************************************/
function Neat (input, output, fitness, options) {
this.input = input; // The input size of the networks
this.output = output; // The output size of the networks
this.fitness = fitness; // The fitness function to evaluate the networks
// Configure options
options = options || {};
this.equal = options.equal || false;
this.clear = options.clear || false;
this.popsize = options.popsize || 50;
this.elitism = options.elitism || 0;
this.provenance = options.provenance || 0;
this.mutationRate = options.mutationRate || 0.3;
this.mutationAmount = options.mutationAmount || 1;
this.fitnessPopulation = options.fitnessPopulation || false;
this.selection = options.selection || methods.selection.POWER;
this.crossover = options.crossover || [
methods.crossover.SINGLE_POINT,
methods.crossover.TWO_POINT,
methods.crossover.UNIFORM,
methods.crossover.AVERAGE
];
this.mutation = options.mutation || methods.mutation.FFW;
this.template = options.network || false;
this.maxNodes = options.maxNodes || Infinity;
this.maxConns = options.maxConns || Infinity;
this.maxGates = options.maxGates || Infinity;
// Custom mutation selection function if given
this.selectMutationMethod = typeof options.mutationSelection === 'function' ? options.mutationSelection.bind(this) : this.selectMutationMethod;
// Generation counter
this.generation = 0;
// Initialise the genomes
this.createPool(this.template);
}
Neat.prototype = {
/**
* Create the initial pool of genomes
*/
createPool: function (network) {
this.population = [];
for (var i = 0; i < this.popsize; i++) {
var copy;
if (this.template) {
copy = Network.fromJSON(network.toJSON());
} else {
copy = new Network(this.input, this.output);
}
copy.score = undefined;
this.population.push(copy);
}
},
/**
* Evaluates, selects, breeds and mutates population
*/
evolve: function () {
// Check if evaluated, sort the population
if (typeof this.population[this.population.length - 1].score === 'undefined') {
this.evaluate();
}
this.sort();
var fittest = Network.fromJSON(this.population[0].toJSON());
fittest.score = this.population[0].score;
var newPopulation = [];
// Elitism
var elitists = [];
for (var i = 0; i < this.elitism; i++) {
elitists.push(this.population[i]);
}
// Provenance
for (i = 0; i < this.provenance; i++) {
newPopulation.push(Network.fromJSON(this.template.toJSON()));
}
// Breed the next individuals
for (i = 0; i < this.popsize - this.elitism - this.provenance; i++) {
newPopulation.push(this.getOffspring());
}
// Replace the old population with the new population
this.population = newPopulation;
this.mutate();
// this.population.push(...elitists);
var _this$population;
(_this$population = this.population).push.apply(_this$population, elitists);
// Reset the scores
for (i = 0; i < this.population.length; i++) {
this.population[i].score = undefined;
}
this.generation++;
return fittest;
},
/**
* Breeds two parents into an offspring, population MUST be surted
*/
getOffspring: function () {
var parent1 = this.getParent();
var parent2 = this.getParent();
return Network.crossOver(parent1, parent2, this.equal);
},
/**
* Selects a random mutation method for a genome according to the parameters
*/
selectMutationMethod: function (genome) {
var mutationMethod = this.mutation[Math.floor(Math.random() * this.mutation.length)];
if (mutationMethod === methods.mutation.ADD_NODE && genome.nodes.length >= this.maxNodes) {
if (config.warnings) console.warn('maxNodes exceeded!');
return;
}
if (mutationMethod === methods.mutation.ADD_CONN && genome.connections.length >= this.maxConns) {
if (config.warnings) console.warn('maxConns exceeded!');
return;
}
if (mutationMethod === methods.mutation.ADD_GATE && genome.gates.length >= this.maxGates) {
if (config.warnings) console.warn('maxGates exceeded!');
return;
}
return mutationMethod;
},
/**
* Mutates the given (or current) population
*/
mutate: function () {
// Elitist genomes should not be included
for (var i = 0; i < this.population.length; i++) {
if (Math.random() <= this.mutationRate) {
for (var j = 0; j < this.mutationAmount; j++) {
var mutationMethod = this.selectMutationMethod(this.population[i]);
this.population[i].mutate(mutationMethod);
}
}
}
},
/**
* Evaluates the current population
*/
evaluate: function () {
var i;
if (this.fitnessPopulation) {
if (this.clear) {
for (i = 0; i < this.population.length; i++) {
this.population[i].clear();
}
}
this.fitness(this.population);
} else {
for (i = 0; i < this.population.length; i++) {
var genome = this.population[i];
if (this.clear) genome.clear();
genome.score = this.fitness(genome);
}
}
},
/**
* Sorts the population by score
*/
sort: function () {
this.population.sort(function (a, b) {
return b.score - a.score;
});
},
/**
* Returns the fittest genome of the current population
*/
getFittest: function () {
// Check if evaluated
if (typeof this.population[this.population.length - 1].score === 'undefined') {
this.evaluate();
}
if (this.population[0].score < this.population[1].score) {
this.sort();
}
return this.population[0];
},
/**
* Returns the average fitness of the current population
*/
getAverage: function () {
if (typeof this.population[this.population.length - 1].score === 'undefined') {
this.evaluate();
}
var score = 0;
for (var i = 0; i < this.population.length; i++) {
score += this.population[i].score;
}
return score / this.population.length;
},
/**
* Gets a genome based on the selection function
* @return {Network} genome
*/
getParent: function () {
var i;
switch (this.selection) {
case selection.POWER:
if (this.population[0].score < this.population[1].score) this.sort();
var index = Math.floor(Math.pow(Math.random(), this.selection.power) * this.population.length);
return this.population[index];
case selection.FITNESS_PROPORTIONATE:
// As negative fitnesses are possible
// https://stackoverflow.com/questions/16186686/genetic-algorithm-handling-negative-fitness-values
// this is unnecessarily run for every individual, should be changed
var totalFitness = 0;
var minimalFitness = 0;
for (i = 0; i < this.population.length; i++) {
var score = this.population[i].score;
minimalFitness = score < minimalFitness ? score : minimalFitness;
totalFitness += score;
}
minimalFitness = Math.abs(minimalFitness);
totalFitness += minimalFitness * this.population.length;
var random = Math.random() * totalFitness;
var value = 0;
for (i = 0; i < this.population.length; i++) {
var genome = this.population[i];
value += genome.score + minimalFitness;
if (random < value) return genome;
}
// if all scores equal, return random genome
return this.population[Math.floor(Math.random() * this.population.length)];
case selection.TOURNAMENT:
if (this.selection.size > this.popsize) {
throw new Error('Your tournament size should be lower than the population size, please change methods.selection.TOURNAMENT.size');
}
// Create a tournament
var individuals = [];
for (i = 0; i < this.selection.size; i++) {
var random = this.population[Math.floor(Math.random() * this.population.length)];
individuals.push(random);
}
// Sort the tournament individuals by score
individuals.sort(function (a, b) {
return b.score - a.score;
});
// Select an individual
for (i = 0; i < this.selection.size; i++) {
if (Math.random() < this.selection.probability || i === this.selection.size - 1) {
return individuals[i];
}
}
}
},
test: function (L,data) {
},
/**
* Export the current population to a json object
*/
export: function () {
var json = [];
for (var i = 0; i < this.population.length; i++) {
var genome = this.population[i];
json.push(genome.toJSON());
}
return json;
},
/**
* Import population from a json object
*/
import: function (json) {
var population = [];
for (var i = 0; i < json.length; i++) {
var genome = json[i];
population.push(Network.fromJSON(genome));
}
this.population = population;
this.popsize = population.length;
}
};
var Neataptic = {
methods: methods,
Connection: Connection,
architect: architect,
Network: Network,
config: config,
Group: Group,
Layer: Layer,
Node: Node,
Neat: Neat
};
module.exports = Neataptic
};
BundleModuleCode['ml/pca']=function (module,exports){
// https://github.com/bitanath/pca
var PCA = (function () {
var options = {};
/**
* The first step is to subtract the mean and center data
*
* @param {Array} matrix - data in an mXn matrix format
* @returns
*/
function computeDeviationMatrix(matrix) {
var unit = unitSquareMatrix(matrix.length);
return subtract(matrix, scale(multiply(unit, matrix), 1 / matrix.length));
}
/**
* Computes variance from deviation
*
* @param {Array} deviation - data minus mean as calculated from computeDeviationMatrix
* @returns
*/
function computeDeviationScores(deviation) {
var devSumOfSquares = multiply(transpose(deviation), deviation);
return devSumOfSquares;
}
/**
* Calculates the var covar square matrix using either population or sample
*
* @param {Array} devSumOfSquares
* @param {boolean} sample - true/false whether data is from sample or not
* @returns
*/
function computeVarianceCovariance(devSumOfSquares, sample) {
var varianceCovariance;
if (sample)
varianceCovariance = scale(devSumOfSquares, 1 / (devSumOfSquares.length - 1));
else
varianceCovariance = scale(devSumOfSquares, 1 / (devSumOfSquares.length));
return varianceCovariance;
}
/**
* Matrix is the deviation sum of squares as computed earlier
*
* @param {Array} matrix - output of computeDeviationScores
* @returns
*/
function computeSVD(matrix) {
var result = svd(matrix);
if (options.verbose) console.log(result)
var eigenvectors = result.U;
var eigenvalues = result.S;
var results = eigenvalues.map(function (value, i) {
var obj = {};
obj.eigenvalue = value;
obj.vector = eigenvectors.map(function (vector, j) {
return -1 * vector[i]; //HACK prevent completely negative vectors
});
return obj;
});
return results;
}
/**
* Get reduced dataset after removing some dimensions
*
* @param {Array} data - initial matrix started out with
* @param {rest} vectors - eigenvectors selected as part of process
* @returns
*/
function computeAdjustedData(data) {
for (var _len = arguments.length, vectorObjs = new Array(_len > 1 ? _len - 1 : 0),
_key = 1; _key < _len; _key++) {
vectorObjs[_key - 1] = arguments[_key];
}
//FIXME no need to transpose vectors since they're already in row normal form
var vectors = vectorObjs.map(function(v){return v.vector});
var matrixMinusMean = computeDeviationMatrix(data);
var adjustedData = multiply(vectors, transpose(matrixMinusMean));
var unit = unitSquareMatrix(data.length);
var avgData = scale(multiply(unit, data), -1 / data.length); //NOTE get the averages to add back
var formattedAdjustData = formatData(adjustedData, 2);
return {
adjustedData: adjustedData,
formattedAdjustedData: formattedAdjustData,
avgData: avgData,
selectedVectors: vectors
};
}
/**
* Get original data set from reduced data set (decompress)
* @param {*} adjustedData = formatted or unformatted adjusted data
* @param {*} vectors = selectedVectors
* @param {*} avgData = avgData
*/
function computeOriginalData(adjustedData, vectors, avgData) {
var originalWithoutMean = transpose(multiply(transpose(vectors), adjustedData));
var originalWithMean = subtract(originalWithoutMean, avgData);
var formattedData = formatData(originalWithMean, 2);
return {
originalData: originalWithMean,
formattedOriginalData: formattedData
}
}
/**
* Get percentage explained, or loss
* @param {*} vectors
* @param {*} selected
*/
function computePercentageExplained(vectors) {
for (var _len = arguments.length, selected = new Array(_len > 1 ? _len - 1 : 0),
_key = 1; _key < _len; _key++) {
selected[_key - 1] = arguments[_key];
}
var total = vectors.map(function (v) {
return v.eigenvalue
}).reduce(function (a, b) {
return a + b;
});
var explained = selected.map(function (v) {
return v.eigenvalue
}).reduce(function (a, b) {
return a + b;
});
return (explained / total);
}
function getEigenVectors(data) {
return computeSVD(computeVarianceCovariance(computeDeviationScores(computeDeviationMatrix(data)), false));
}
function analyseTopResult(data) {
var eigenVectors = getEigenVectors(data);
var sorted = eigenVectors.sort(function (a, b) {
return b.eigenvalue - a.eigenvalue;
});
console.log('Sorted Vectors', sorted);
var selected = sorted[0].vector;
return computeAdjustedData(data, selected);
}
function formatData(data, precision) {
var TEN = Math.pow(10, precision || 2);
return data.map(function (d, i) {
return d.map(function (n) {
return Math.round(n * TEN) / TEN;
})
})
}
/**
* Multiplies AxB, where A and B are matrices of nXm and mXn dimensions
* @param {} a
* @param {*} b
*/
function multiply(a, b) {
if (!a[0] || !b[0] || !a.length || !b.length) {
throw new Error('Both A and B should be matrices');
}
if (b.length !== a[0].length) {
throw new Error('Columns in A should be the same as the number of rows in B');
}
var product = [];
for (var i = 0; i < a.length; i++) {
product[i] = []; //initialize a new row
for (var j = 0; j < b[0].length; j++) {
for (var k = 0; k < a[0].length; k++) {
(product[i])[j] = !!(product[i])[j] ? (product[i])[j] + (a[i])[k] * (b[k])[j] : (a[i])[k] * (b[k])[j];
}
}
}
return product;
}
/**
* Utility function to subtract matrix b from a
*
* @param {any} a
* @param {any} b
* @returns
*/
function subtract(a, b) {
if (!(a.length === b.length && a[0].length === b[0].length))
throw new Error('Both A and B should have the same dimensions');
var result = [];
for (var i = 0; i < a.length; i++) {
result[i] = [];
for (var j = 0; j < b[0].length; j++) {
(result[i])[j] = (a[i])[j] - (b[i])[j];
}
}
return result;
}
/**
* Multiplies a matrix into a factor
*
* @param {any} matrix
* @param {any} factor
* @returns
*/
function scale(matrix, factor) {
var result = [];
for (var i = 0; i < matrix.length; i++) {
result[i] = [];
for (var j = 0; j < matrix[0].length; j++) {
(result[i])[j] = (matrix[i])[j] * factor;
}
}
return result;
}
/**
* Generates a unit square matrix
* @param {*} rows = number of rows to fill
*/
function unitSquareMatrix(rows) {
var result = [];
for (var i = 0; i < rows; i++) {
result[i] = [];
for (var j = 0; j < rows; j++) {
(result[i])[j] = 1;
}
}
return result;
}
/**
* Transposes a matrix, converts rows to columns
* @param {*} matrix
*/
function transpose(matrix) {
var operated = clone(matrix);
return operated[0].map(function (m, c) {
return matrix.map(function (r) {
return r[c];
});
});
}
/**
* Deep Clones a matrix
* @param {*} arr
*/
function clone(arr) {
var string = JSON.stringify(arr);
var result = JSON.parse(string);
return result;
}
/**
* Compute the thin SVD from G. H. Golub and C. Reinsch, Numer. Math. 14, 403-420 (1970)
* From the Numeric JS Implementation Copyright (C) 2011 by Sébastien Loisel
* The C implementation from which this has been taken may be found here: http://www.public.iastate.edu/~dicook/JSS/paper/code/svd.c
* @param {*} A = m*n matrix
*/
function svd(A) {
var temp;
var prec = Math.pow(2, -52) // assumes double prec
var tolerance = 1.e-64 / prec;
var itmax = 50;
var c = 0;
var i = 0;
var j = 0;
var k = 0;
var l = 0;
var u = clone(A);
var m = u.length;
var n = u[0].length;
if (m < n) throw "Need more rows than columns"
var e = new Array(n); //vector1
var q = new Array(n); //vector2
for (i = 0; i < n; i++) e[i] = q[i] = 0.0;
var v = rep([n, n], 0);
function pythag(a, b) {
a = Math.abs(a)
b = Math.abs(b)
if (a > b)
return a * Math.sqrt(1.0 + (b * b / a / a))
else if (b == 0.0)
return a
return b * Math.sqrt(1.0 + (a * a / b / b))
}
//rep function
function rep(s, v, k) {
if (typeof k === "undefined") {
k = 0;
}
var n = s[k],
ret = Array(n),
i;
if (k === s.length - 1) {
for (i = n - 2; i >= 0; i -= 2) {
ret[i + 1] = v;
ret[i] = v;
}
if (i === -1) {
ret[0] = v;
}
return ret;
}
for (i = n - 1; i >= 0; i--) {
ret[i] = rep(s, v, k + 1);
}
return ret;
}
//Householder's reduction to bidiagonal form
var f = 0.0;
var g = 0.0;
var h = 0.0;
var x = 0.0;
var y = 0.0;
var z = 0.0;
var s = 0.0;
for (i = 0; i < n; i++) {
e[i] = g; //vector
s = 0.0; //sum
l = i + 1; //stays i+1
for (j = i; j < m; j++)
s += (u[j][i] * u[j][i]);
if (s <= tolerance)
g = 0.0;
else {
f = u[i][i];
g = Math.sqrt(s);
if (f >= 0.0) g = -g;
h = f * g - s
u[i][i] = f - g;
for (j = l; j < n; j++) {
s = 0.0
for (k = i; k < m; k++)
s += u[k][i] * u[k][j]
f = s / h
for (k = i; k < m; k++)
u[k][j] += f * u[k][i]
}
}
q[i] = g
s = 0.0
for (j = l; j < n; j++)
s = s + u[i][j] * u[i][j]
if (s <= tolerance)
g = 0.0
else {
f = u[i][i + 1]
g = Math.sqrt(s)
if (f >= 0.0) g = -g
h = f * g - s
u[i][i + 1] = f - g;
for (j = l; j < n; j++) e[j] = u[i][j] / h
for (j = l; j < m; j++) {
s = 0.0
for (k = l; k < n; k++)
s += (u[j][k] * u[i][k])
for (k = l; k < n; k++)
u[j][k] += s * e[k]
}
}
y = Math.abs(q[i]) + Math.abs(e[i])
if (y > x)
x = y
}
// accumulation of right hand transformations
for (i = n - 1; i != -1; i += -1) {
if (g != 0.0) {
h = g * u[i][i + 1]
for (j = l; j < n; j++)
v[j][i] = u[i][j] / h //u is array, v is square of columns
for (j = l; j < n; j++) {
s = 0.0
for (k = l; k < n; k++)
s += u[i][k] * v[k][j]
for (k = l; k < n; k++)
v[k][j] += (s * v[k][i])
}
}
for (j = l; j < n; j++) {
v[i][j] = 0;
v[j][i] = 0;
}
v[i][i] = 1;
g = e[i]
l = i
}
// accumulation of left hand transformations
for (i = n - 1; i != -1; i += -1) {
l = i + 1
g = q[i]
for (j = l; j < n; j++)
u[i][j] = 0;
if (g != 0.0) {
h = u[i][i] * g
for (j = l; j < n; j++) {
s = 0.0
for (k = l; k < m; k++) s += u[k][i] * u[k][j];
f = s / h
for (k = i; k < m; k++) u[k][j] += f * u[k][i];
}
for (j = i; j < m; j++) u[j][i] = u[j][i] / g;
} else
for (j = i; j < m; j++) u[j][i] = 0;
u[i][i] += 1;
}
// diagonalization of the bidiagonal form
prec = prec * x
for (k = n - 1; k != -1; k += -1) {
for (var iteration = 0; iteration < itmax; iteration++) { // test f splitting
var test_convergence = false
for (l = k; l != -1; l += -1) {
if (Math.abs(e[l]) <= prec) {
test_convergence = true
break
}
if (Math.abs(q[l - 1]) <= prec)
break
}
if (!test_convergence) { // cancellation of e[l] if l>0
c = 0.0
s = 1.0
var l1 = l - 1
for (i = l; i < k + 1; i++) {
f = s * e[i]
e[i] = c * e[i]
if (Math.abs(f) <= prec)
break
g = q[i]
h = pythag(f, g)
q[i] = h
c = g / h
s = -f / h
for (j = 0; j < m; j++) {
y = u[j][l1]
z = u[j][i]
u[j][l1] = y * c + (z * s)
u[j][i] = -y * s + (z * c)
}
}
}
// test f convergence
z = q[k]
if (l == k) { //convergence
if (z < 0.0) { //q[k] is made non-negative
q[k] = -z
for (j = 0; j < n; j++)
v[j][k] = -v[j][k]
}
break //break out of iteration loop and move on to next k value
}
if (iteration >= itmax - 1)
throw 'Error: no convergence.'
// shift from bottom 2x2 minor
x = q[l]
y = q[k - 1]
g = e[k - 1]
h = e[k]
f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y)
g = pythag(f, 1.0)
if (f < 0.0)
f = ((x - z) * (x + z) + h * (y / (f - g) - h)) / x
else
f = ((x - z) * (x + z) + h * (y / (f + g) - h)) / x
// next QR transformation
c = 1.0
s = 1.0
for (i = l + 1; i < k + 1; i++) {
g = e[i]
y = q[i]
h = s * g
g = c * g
z = pythag(f, h)
e[i - 1] = z
c = f / z
s = h / z
f = x * c + g * s
g = -x * s + g * c
h = y * s
y = y * c
for (j = 0; j < n; j++) {
x = v[j][i - 1]
z = v[j][i]
v[j][i - 1] = x * c + z * s
v[j][i] = -x * s + z * c
}
z = pythag(f, h)
q[i - 1] = z
c = f / z
s = h / z
f = c * g + s * y
x = -s * g + c * y
for (j = 0; j < m; j++) {
y = u[j][i - 1]
z = u[j][i]
u[j][i - 1] = y * c + z * s
u[j][i] = -y * s + z * c
}
}
e[l] = 0.0
e[k] = f
q[k] = x
}
}
for (i = 0; i < q.length; i++)
if (q[i] < prec) q[i] = 0
//sort eigenvalues
for (i = 0; i < n; i++) {
for (j = i - 1; j >= 0; j--) {
if (q[j] < q[i]) {
c = q[j]
q[j] = q[i]
q[i] = c
for (k = 0; k < u.length; k++) {
temp = u[k][i];
u[k][i] = u[k][j];
u[k][j] = temp;
}
for (k = 0; k < v.length; k++) {
temp = v[k][i];
v[k][i] = v[k][j];
v[k][j] = temp;
}
i = j
}
}
}
return {
U: u,
S: q,
V: v
}
}
return {
computeDeviationScores: computeDeviationScores,
computeDeviationMatrix: computeDeviationMatrix,
computeSVD: computeSVD,
computePercentageExplained: computePercentageExplained,
computeOriginalData: computeOriginalData,
computeVarianceCovariance: computeVarianceCovariance,
computeAdjustedData: computeAdjustedData,
getEigenVectors: getEigenVectors,
analyseTopResult: analyseTopResult,
transpose: transpose,
multiply: multiply,
clone: clone,
scale: scale,
options:options
}
})();
if(typeof module !== 'undefined')
module.exports = PCA;
};
BundleModuleCode['nn/nn']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2017 bLAB
** $CREATED: 27-06-17 by sbosse.
** $RCS: $Id$
** $VERSION: 1.1.4
**
** $INFO:
**
** Neuronal Network Module implementing agent defined neurons and neuronal networks.
** An agent must only save the state/configuration of neurons and
** network configurations. Neuronal Network configurations are processed by the
** agent plattform.
**
** Example network with thre neuron nodes:
**
** var n1 = nn.neuron({
** a:{decay:0.1,integrate:0.2},
** b:{},
** threshold:0.5 // Binary output
** });
** var n2 = nn.neuron({
** x:{decay:0.1,integrate:0.2,threshold:0.1},
** y:{weight:0.5}
** });
** var n3 = nn.neuron({
** a:{decay:0.1,integrate:0.2},
** b:{decay:0.1}
** });
**
** var nw = nn.network([
** n1,n2,n3
** ],[
** // Connect ouput of internal neurons to other neuron inputs
** // Read as: output(n1) -> input.x(n2)
** {output:n1,x:n2},
** {output:n2,b:n3},
** // Define and connect network inputs to internal neuron inputs
** {input:n1,v1:'a',v2:'b'},
** {input:n2,v3:'y'},
** {input:n3,v4:'a'}
** ]);
**
** nn.compute(nw);
**
** Input parameters: weight, decay, integrate, threshold (discriminator), invert
** Output parameter: threshold (binary output)
** Output range: [-1.0,1.0] | {-1,0,1}
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var current=none;
var Aios=none;
function inputFunction(flags) {
return function (x,y0) {
var p,y=0,w=1,c,i;
if (x==undefined) return y0;
for(p in flags) {
var k=flags[p];
switch (p) {
case 'invert': x = -x; break;
case 'threshold': x = (x>=k?x:0); break;
case 'weight': w = k; break;
case 'decay': c = k; break;
case 'integrate': i = k; break;
}
}
if (c!=undefined) y=y0-y0*c;
if (i!=undefined) y=(c==undefined?y0:y)+x*i;
else y=y+x;
return {y:Math.max(-1.0,Math.min(1.0,y)),w:w};
}
}
var nn = {
compute: function (node,input) {
var i,ys=0,yw,neuron,neuron_input,next,computed,more;
// All input variables reday (values computed/available)?
function ready(node) {
var p;
for(p in node.input) if (node.input[p].x==undefined) return false;
return true;
}
switch (node.type) {
case 'neuron':
// console.log('compute '+node.id);
if (!input)
// Get internal node input values; neuronal network nodes only
{input={};for(p in node.input) input[p]=node.input[p].x};
for(i in node.input) {
if (input[i] == undefined) continue;
yw=node.input[i].f(input[i],node.input[i].y);
node.input[i].y=yw.y;
node.input[i].x=undefined;
ys += (yw.y*yw.w);
}
if (node.threshold != undefined)
node.output = (ys>=node.threshold?1:0);
else
node.output = Math.max(-1.0,Math.min(1.0,ys));
break;
case 'network':
// Set inputs
for(p in input) {
if (node.input[p])
neuron_input=node.input[p].param; // local neuron input
neuron=node.nodes[node.input[p].node]; // target neuron
if (neuron) neuron.input[neuron_input].x=input[p];
}
// Compute all nodes with a complete set of inputs
more=1;
while (more) {
computed=0;
for(i in node.nodes) {
neuron=node.nodes[i];
if (ready(neuron)) {
nn.compute(neuron);
computed++;
if (neuron.connect)
for(p in neuron.connect) {
next=node.nodes[p];
if (next) next.input[neuron.connect[p]].x=neuron.output;
}
}
}
more=(computed != node.nodes.length && computed>0);
}
break;
}
},
connect: function (node1,node2,input) {
var c={};
node1.connect[node2.id]=input;
},
/** Compose a network graph from neuron nodes.
** The network object will not contain recursive references or deep nested structures
** to insure mobility.
**
*/
network: function (nodes,connect) {
var i,n,p,conn,nw={type:'network',input:{}};
// Remap neuron ids...
for(i in nodes) nodes[i].id=i;
function getNode (o) {
var p;
for(p in o) if (p!='output' && p!='input') return o[p];
}
function getInput(o) {
var p;
for(p in o) if (p!='output' && p!='input') return p;
}
function getIndex(o) {
var i;
for(i in nodes) if (nodes[i].id==o.id) return i;
}
nw.nodes=nodes;
for(i in connect) {
conn=connect[i];
if (conn.output) {
nn.connect(conn.output,getNode(conn),getInput(conn));
}
else if (conn.input) {
for(p in conn) {
if (p!='input') nw.input[p]={node:getIndex(conn.input),param:conn[p]};
}
}
}
return nw;
},
/** neuron(a:{invert:true,weight:0.5,decay:0.2,integrate:0.1,threshold:0.5},
** threshold:0.9)
**
** function neuron() -> {type,id:number,threshold?,connect:{},input:{},output:number}
**
** type of connect = {<nodeid>:<input>,..}
*/
neuron: function (settings) {
var p,i,input,
o= {
type:'neuron',
id:(Math.random()*1000000)|0,
threshold:settings.threshold,
// connect this output to other node inputs - spawns a computation graph
connect:{},
input:{},
output:settings.init||0
}
for(p in settings) {
if (p=='init' || p=='threshold') continue;
input=settings[p];
o.input[p]={x:undefined,y:0,f:inputFunction(input)};
}
return o;
}
}
/**
*
*/
module.exports = {
agent:nn,
compute:nn.compute,
current:function (module) { current=module.current; Aios=module; }
}
};
BundleModuleCode['csp/csp']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2019 BSSLAB
** $CREATED: 29-5-19 by sbosse.
** $VERSION: 1.1.1
**
** $INFO:
**
** JavaScript AIOS Constraint Solver Programming API
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var simple = Require('csp/cspS');
var current=none;
var Aios=none;
var CSP = {
SIMPLE:'SIMPLE',
}
var options = {
version: '1.1.1'
}
var csp = {
/* Add constraint */
C : function (model,v1,v2,f) {
switch (model.algorithm) {
case CSP.SIMPLE:
model.constraints.push([v1,v2,f]);
break;
}
},
V : function (model,name,val) {
switch (model.algorithm) {
case CSP.SIMPLE:
model.variables[name]=val;
break;
}
},
range : function (a,b,step) {
var res=[];
if (step==undefined) step=1;
for(var i=a;i<=b;i=i+step) res.push(i);
return res;
},
/* Create a new solver */
solver : function (options) {
var model={}
options=checkOptions(options,{});
options.algorithm=checkOption(options.algorithm,CSP.SIMPLE);
model.algorithm=options.algorithm;
switch (options.algorithm) {
case CSP.SIMPLE:
model.variables= {}
model.constraints = []
break;
}
return model
},
/* Solve the problem, return solutions */
solve : function (model,options) {
switch (model.algorithm) {
case CSP.SIMPLE:
return simple.solve(model)
}
},
CSP:CSP,
}
module.exports = {
agent:csp,
CSP:CSP,
current:function (module) { current=module.current; Aios=module; }
}
};
BundleModuleCode['csp/cspS']=function (module,exports){
var CSP = {},
FAILURE = 'FAILURE',
stepCounter = 0;
CSP.solve = function solve(csp) {
// Solves a constraint satisfaction problem.
// `csp` is an object that should have the properties:
// `variables` : object that holds variable names and their domain.
// `constraints`: list of constraints where each element is an
// array of [head node, tail node, constraint function]
// `cb`: optional callback function.
var result = backtrack({}, csp.variables, csp);
if (result == FAILURE) { return result; }
// Unwrap values from array containers.
for (var key in result) {
result[key] = result[key][0];
}
if (csp.cb) csp.cb(result);
return result;
}
function backtrack(_assigned, unassigned, csp) {
// Backtracking search.
// Copying assigned in necessary because we modify it. Without copying
// the object over, modifying assigned would also change values for old
// assigned objects (which are used in callbacks).
var assigned = {};
for (var key in _assigned) { assigned[key] = _assigned[key]; }
if (finished(unassigned)) { return assigned; } // Base case.
var nextKey = selectUnassignedVariable(unassigned),
values = orderValues(nextKey, assigned, unassigned, csp);
delete unassigned[nextKey];
for (var i = 0; i < values.length; i++) {
stepCounter++;
assigned[nextKey] = [values[i]]; // Assign a value to a variable.
var consistent = enforceConsistency(assigned, unassigned, csp);
var newUnassigned = {}, newAssigned = {};
for (var key in consistent) {
if (assigned[key]) { newAssigned[key] = assigned[key].slice(); }
else { newUnassigned[key] = consistent[key].slice(); }
}
if (anyEmpty(consistent)) { continue; } // Empty domains means failure.
var result = backtrack(newAssigned, newUnassigned, csp);
if (result != FAILURE) { return result; }
}
return FAILURE;
}
function finished(unassigned) {
// Checks if there are no more variables to assign.
return Object.keys(unassigned).length == 0;
}
function anyEmpty(consistent) {
// Checks if any variable's domain is empty.
for (var key in consistent) {
if (consistent[key].length == 0) { return true; }
}
return false;
}
function partialAssignment(assigned, unassigned) {
// Combine unassigned and assigned for use in enforceConsistency.
var partial = {};
for (var key in unassigned) { partial[key] = unassigned[key].slice(); }
for (var key in assigned) { partial[key] = assigned[key].slice(); }
return partial;
}
function enforceConsistency(assigned, unassigned, csp) {
// Enforces arc consistency by removing inconsistent values from
// every constraint's tail node.
function removeInconsistentValues(head, tail, constraint, variables) {
var hv,tv,validHeadValues,validTailValues,removed;
if (tail) {
// Removes inconsistent values from the tail node. A value is
// inconsistent when if the `tail` is assigned that value, there are
// no values in `head`'s domain that satisfies the constraint.
// - binray constraint
hv = variables[head], tv = variables[tail];
validTailValues = tv.filter(function (t) {
return hv.some(function (h) {
return constraint(h, t);
});
});
removed = tv.length != validTailValues.length;
variables[tail] = validTailValues;
} else {
// unary constraint - modify head
hv = variables[head];
validHeadValues = hv.filter(function (h) {
return constraint(h);
});
removed = hv.length != validHeadValues.length;
variables[head] = validHeadValues;
}
return removed;
}
function incomingConstraints(node) {
// Returns all the constraints where `node` is the head node.
return csp.constraints.filter(function (c) {
return c[0] == node;
});
}
var queue = csp.constraints.slice(),
variables = partialAssignment(assigned, unassigned);
while (queue.length) { // While there are more constraints to test.
var c = queue.shift(), head = c[0], tail = c[1], constraint = c[2];
if (removeInconsistentValues(head, tail, constraint, variables)) {
// If values from the tail have been removed, incoming constraints
// to the tail must be rechecked.
queue = queue.concat(incomingConstraints(tail));
}
}
return variables;
}
function selectUnassignedVariable(unassigned) {
// Picks the next variable to assign according to the Minimum
// Remaining Values heuristic. Pick the variable with the fewest
// values remaining in its domain. This helps identify domain
// failures earlier.
var minKey = null, minLen = Number.POSITIVE_INFINITY;
for (var key in unassigned) {
var len = unassigned[key].length;
if (len < minLen) { minKey = key, minLen = len; }
}
return minKey;
}
function orderValues(nextKey, assigned, unassigned, csp) {
// Orders the values of an unassigned variable according to the
// Least Constraining Values heuristic. Perform arc consistency
// on each possible value, and order variables according to the
// how many values were eliminated from all the domains (fewest
// eliminated in the front). This helps makes success more likely
// by keeping future options open.
function countValues(vars) {
var sum = 0;
for (var key in vars) { sum += vars[key].length; }
return sum;
}
function valuesEliminated(val) {
assigned[nextKey] = [val];
var newLength = countValues(enforceConsistency(assigned, unassigned, csp));
delete assigned[nextKey];
return newLength;
}
// Cache valuesEliminated to be used in sort.
var cache = {}, values = unassigned[nextKey];
values.forEach(function(val) {
cache[val] = valuesEliminated(val);
});
// Descending order based on the number of domain values remaining.
values.sort(function (a, b) { return cache[b] - cache[a]; });
return values;
}
module.exports = CSP;
};
BundleModuleCode['logic/sat']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2019 BSSLAB
** $CREATED: 29-5-19 by sbosse.
** $VERSION: 1.1.1
**
** $INFO:
**
** JavaScript AIOS SAT Logic Solver API
**
** $ENDOFINFO
*/
var current=none;
var Aios=none;
var options = {
version: '1.1.1'
}
var Logic = Require('logic/logic-solver');
var Solved = [];
var sat = {
create : function (options) {
var model = {
id : Aios.aidgen(), // or agent id?
life : Aios.time()
}
return model;
},
eval: function (model,what) {
sat.setup(model);
if (!Solved[model.id]) return;
Solved[model.id].life=Aios.time();
return Solved[model.id].evaluate(what)
},
gc : function () {
var time = Aios.time();
for(var p in Solved) {
if (Solved[p] && (Solved[p].time+sat.lifetime)<time) {
delete Solved[p];
delete Logic._minisatSolvers[p];
delete Logic._termifiers[p];
}
}
},
// forbid rule
F: function (model,logic) {
sat.setup(model);
Logic.Solver.forbid(model,logic)
},
L : Logic,
// require rule
R: function (model,logic) {
sat.setup(model);
Logic.Solver.require(model,logic)
},
solver : function (options) {
var model = sat.create();
Logic.Solver(model);
return model
},
// solve current logic formulas
solve : function (model,assume) {
sat.setup(model);
if (!Solved[model.id]) {
model._numClausesAddedToMiniSat=0;
}
if (assume)
Solved[model.id]=Logic.Solver.solveAssuming(model,assume)
else
Solved[model.id]=Logic.Solver.solve(model);
if (Solved[model.id]) Solved[model.id].life=Aios.time();
return Solved[model.id]?Solved[model.id].getTrueVars():null
},
lifetime : 1E9,
// complete the solver environemnt
setup : function (model) {
if (!Logic._termifiers[model.id] || !Logic._minisatSolvers[model.id]) {
model.id=Aios.aidgen();
model.life=Aios.time();
Logic._termifiers[model.id]=new Logic.Termifier(model);
model._numClausesAddedToMiniSat=0;
Logic._minisatSolvers[model.id]=new Logic.MiniSat();
}
},
true : function (model) {
sat.setup(model);
if (!Solved[model.id]) return;
Solved[model.id].life=Aios.time();
return Solved[model.id].getTrueVars()
}
}
sat.Logic = sat.L;
sat.require = sat.R;
sat.forbid = sat.F;
module.exports = {
agent:sat,
Logic:Logic,
current:function (module) { current=module.current; Aios=module; }
}
};
BundleModuleCode['logic/logic-solver']=function (module,exports){
/**
* MiniSat Logic Solver - Procedural Version (dividing code and data)
*
* https://github.com/meteor/logic-solver
*
*/
var MiniSat = Require("logic/minisat_wrapper.js");
var _ = Require("ext/underscore");
var parse = Require('ext/jsep');
var Logic;
Logic = {};
Logic.MiniSat=MiniSat;
////////// TYPE TESTERS
// Set the `description` property of a tester function and return the function.
var withDescription = function (description, tester) {
tester.description = description;
return tester;
};
// Create a function (x) => (x instanceof constructor), but possibly before
// constructor is available. For example, if Logic.Formula hasn't been
// assigned yet, passing Logic for `obj` and "Formula" for `constructorName`
// will still work.
var lazyInstanceofTester = function (description, obj, constructorName) {
return withDescription(description, function (x) {
return x instanceof obj[constructorName];
});
};
///// PUBLIC TYPE TESTERS
// All variables have a name and a number. The number is mainly used
// internally, and it's what's given to MiniSat. Names and numbers
// are interchangeable, which is convenient for doing manipulation
// of terms in a way that works before or after variable names are
// converted to numbers.
// Term: a variable name or variable number, optionally
// negated (meaning "boolean not"). For example,
// `1`, `-1`, `"foo"`, or `"-foo"`. All variables have
// internal numbers that start at 1, so "foo" might be
// variable number 1, for example. Any number of leading
// "-" will be parsed in the string form, but we try to
// keep it to either one or zero of them.
Logic.isNumTerm = withDescription('a NumTerm (non-zero integer)',
function (x) {
// 32-bit integer, but not 0
return (x === (x | 0)) && x !== 0;
});
// NameTerm must not be empty, or just `-` characters, or look like a
// number. Specifically, it can't be zero or more `-` followed by
// zero or more digits.
Logic.isNameTerm = withDescription('a NameTerm (string)',
function (x) {
return (typeof x === 'string') &&
! /^-*[0-9]*$/.test(x);
});
Logic.isTerm = withDescription('a Term (appropriate string or number)',
function (x) {
return Logic.isNumTerm(x) ||
Logic.isNameTerm(x);
});
// WholeNumber: a non-negative integer (0 is allowed)
Logic.isWholeNumber = withDescription('a whole number (integer >= 0)',
function (x) {
return (x === (x | 0)) && x >= 0;
});
Logic.isFormula = lazyInstanceofTester('a Formula', Logic, 'Formula');
Logic.isClause = function (o) { return o && o.tag=='Clause' }// lazyInstanceofTester('a Clause', Logic, 'Clause');
Logic.isBits = lazyInstanceofTester('a Bits', Logic, 'Bits');
///// UNDOCUMENTED TYPE TESTERS
Logic._isInteger = withDescription(
'an integer', function (x) { return x === (x | 0); });
Logic._isFunction = withDescription(
'a Function', function (x) { return typeof x === 'function'; });
Logic._isString = withDescription(
'a String', function (x) { return typeof x === 'string'; });
Logic._isArrayWhere = function (tester) {
var description = 'an array';
if (tester.description) {
description += ' of ' + tester.description;
}
return withDescription(description, function (x) {
if (! _.isArray(x)) {
return false;
} else {
for (var i = 0; i < x.length; i++) {
if (! tester(x[i])) {
return false;
}
}
return true;
}
});
};
Logic._isFormulaOrTerm = withDescription('a Formula or Term',
function (x) {
return Logic.isFormula(x) ||
Logic.isTerm(x);
});
Logic._isFormulaOrTermOrBits = withDescription('a Formula, Term, or Bits',
function (x) {
return Logic.isFormula(x) ||
Logic.isBits(x) ||
Logic.isTerm(x);
});
Logic._MiniSat = MiniSat; // Expose for testing and poking around
// import the private testers from types.js
var isInteger = Logic._isInteger;
var isFunction = Logic._isFunction;
var isString = Logic._isString;
var isArrayWhere = Logic._isArrayWhere;
var isFormulaOrTerm = Logic._isFormulaOrTerm;
var isFormulaOrTermOrBits = Logic._isFormulaOrTermOrBits;
Logic._assert = function (value, tester, description) {
if (! tester(value)) {
var displayValue = (typeof value === 'string' ? JSON.stringify(value) :
value);
throw new Error(displayValue + " is not " +
(tester.description || description));
}
};
// Call this as `if (assert) assertNumArgs(...)`
var assertNumArgs = function (actual, expected, funcName) {
if (actual !== expected) {
throw new Error("Expected " + expected + " args in " + funcName +
", got " + actual);
}
};
// Call `assert` as: `if (assert) assert(...)`.
// This local variable temporarily set to `null` inside
// `Logic.disablingAssertions`.
var assert = Logic._assert;
// Like `if (assert) assert(...)` but usable from other files in the package.
Logic._assertIfEnabled = function (value, tester, description) {
if (assert) assert(value, tester, description);
};
// Disabling runtime assertions speeds up clause generation. Assertions
// are disabled when the local variable `assert` is null instead of
// `Logic._assert`.
Logic.disablingAssertions = function (f) {
var oldAssert = assert;
try {
assert = null;
return f();
} finally {
assert = oldAssert;
}
};
// Back-compat.
Logic._disablingTypeChecks = Logic.disablingAssertions;
////////////////////
// Takes a Formula or Term, returns a Formula or Term.
// Unlike other operators, if you give it a Term,
// you will get a Term back (of the same type, NameTerm
// or NumTerm).
Logic.not = function (operand) {
if (assert) assert(operand, isFormulaOrTerm);
if (operand instanceof Logic.Formula) {
return new Logic.NotFormula(operand);
} else {
// Term
if (typeof operand === 'number') {
return -operand;
} else if (operand.charAt(0) === '-') {
return operand.slice(1);
} else {
return '-' + operand;
}
}
};
Logic.NAME_FALSE = "$F";
Logic.NAME_TRUE = "$T";
Logic.NUM_FALSE = 1;
Logic.NUM_TRUE = 2;
Logic.TRUE = Logic.NAME_TRUE;
Logic.FALSE = Logic.NAME_FALSE;
// Abstract base class. Subclasses are created using _defineFormula.
Logic.Formula = function () {};
Logic._defineFormula = function (constructor, typeName, methods) {
if (assert) assert(constructor, isFunction);
if (assert) assert(typeName, isString);
constructor.prototype = new Logic.Formula();
constructor.prototype.type = typeName;
if (methods) {
_.extend(constructor.prototype, methods);
}
};
// Returns a list of Clauses that together require the Formula to be
// true, or false (depending on isTrue; both cases must be
// implemented). A single Clause may also be returned. The
// implementation should call the termifier to convert terms and
// formulas to NumTerms specific to a solver instance, and use them to
// construct a Logic.Clause.
Logic.Formula.prototype.generateClauses = function (isTrue, termifier) {
throw new Error("Cannot generate this Formula; it must be expanded");
};
// All Formulas have a globally-unique id so that Solvers can track them.
// It is assigned lazily.
Logic.Formula._nextGuid = 1;
Logic.Formula.prototype._guid = null;
Logic.Formula.prototype.guid = function () {
if (this._guid === null) {
this._guid = Logic.Formula._nextGuid++;
}
return this._guid;
};
// A "clause" is a disjunction of terms, e.g. "A or B or (not C)",
// which we write "A v B v -C". Logic.Clause is mainly an internal
// Solver data structure, which is the final result of formula
// generation and mapping variable names to numbers, before passing
// the clauses to MiniSat.
Logic.Clause = function (/*formulaOrArray, ...*/) {
var terms = _.flatten(arguments);
if (assert) assert(terms, isArrayWhere(Logic.isNumTerm));
// this.terms = terms; // immutable [NumTerm]
return {
tag : 'Clause',
terms : terms,
}
};
// Returns a new Clause with the extra term or terms appended
if (0) Logic.Clause.prototype.append = function (/*formulaOrArray, ...*/) {
return new Logic.Clause(this.terms.concat(_.flatten(arguments)));
};
Logic.Clause.append = function (/*formulaOrArray, ...*/) {
var args = _.flatten(arguments);
var self = args[0]; args=args.slice(1);
return Logic.Clause(self.terms.concat(args));
};
var FormulaInfo = function () {
// We generate a variable when a Formula is used.
this.varName = null; // string name of variable
this.varNum = null; // number of variable (always positive)
// A formula variable that is used only in the positive or only
// in the negative doesn't need the full set of clauses that
// establish a bidirectional implication between the formula and the
// variable. For example, in the formula `Logic.or("A", "B")`, with the
// formula variable `$or1`, the full set of clauses is `A v B v
// -$or1; -A v $or1; -B v $or1`. If both `$or1` and `-$or1` appear
// elsewhere in the set of clauses, then all three of these clauses
// are required. However, somewhat surprisingly, if only `$or1` appears,
// then only the first is necessary. If only `-$or1` appears, then only
// the second and third are necessary.
//
// Suppose the formula A v B is represented by the variable $or1,
// and $or1 is only used positively. It's important that A v B being
// false forces $or1 to be false, so that when $or1 is used it has
// the appropriate effect. For example, if we have the clause $or1 v
// C, then A v B being false should force $or1 to be false, which
// forces C to be true. So we generate the clause A v B v
// -$or1. (The implications of this clause are: If A v B is false,
// $or1 must be false. If $or1 is true, A v B must be true.)
//
// However, in the case where A v B is true, we don't actually
// need to insist that the solver set $or1 to true, as long as we
// are ok with relaxing the relationship between A v B and $or1
// and getting a "wrong" value for $or1 in the solution. Suppose
// the solver goes to work and at some point determines A v B to
// be true. It could set $or1 to true, satisfying all the clauses
// where it appears, or it could set $or1 to false, which only
// constrains the solution space and doesn't open up any new
// solutions for other variables. If the solver happens to find a
// solution where A v B is true and $or1 is false, we know there
// is a similar solution that makes all the same assignments
// except it assigns $or1 to true.
//
// If a formula is used only negatively, a similar argument applies
// but with signs flipped, and if it is used both positively and
// negatively, both kinds of clauses must be generated.
//
// See the mention of "polarity" in the MiniSat+ paper
// (http://minisat.se/downloads/MiniSat+.pdf).
//
// These flags are set when generation has been done for the positive
// case or the negative case, so that we only generate each one once.
this.occursPositively = false;
this.occursNegatively = false;
// If a Formula has been directly required or forbidden, we can
// replace it by TRUE or FALSE in subsequent clauses. Track the
// information here.
this.isRequired = false;
this.isForbidden = false;
};
// The "termifier" interface is provided to a Formula's
// generateClauses method, which must use it to generate Clause
// objects.
//
// The reason for this approach is that it gives the Formula control
// over the clauses returned, but it gives the Solver control over
// Formula generation.
Logic.Termifier = function (solver) {
this.solver = solver;
};
// The main entry point, the `clause` method takes a list of
// FormulaOrTerms and converts it to a Clause containing NumTerms, *by
// replacing Formulas with their variables*, creating the variable if
// necessary. For example, if an OrFormula is represented by the
// variable `$or1`, it will be replaced by the numeric version of
// `$or1` to make the Clause. When the Clause is actually used, it
// will trigger generation of the clauses that relate `$or1` to the
// operands of the OrFormula.
Logic.Termifier.prototype.clause = function (/*args*/) {
var self = this;
var formulas = _.flatten(arguments);
if (assert) assert(formulas, isArrayWhere(isFormulaOrTerm));
return new Logic.Clause(_.map(formulas, function (f) {
return self.term(f);
}));
};
// The `term` method performs the mapping from FormulaOrTerm to
// NumTerm. It's called by `clause` and could be called directly
// from a Formula's generateClauses if it was useful for some
// reason.
Logic.Termifier.prototype.term = function (formula) {
return Logic.Solver._formulaToTerm(this.solver,formula);
};
// The `generate` method generates clauses for a Formula (or
// Term). It should be used carefully, because it works quite
// differently from passing a Formula into `clause`, which is the
// normal way for one Formula to refer to another. When you use a
// Formula in `clause`, it is replaced by the Formula's variable,
// and the Solver handles generating the Formula's clauses once.
// When you use `generate`, this system is bypassed, and the
// Formula's generateClauses method is called pretty much directly,
// returning the array of Clauses.
Logic.Termifier.prototype.generate = function (isTrue, formula) {
return Logic.Solver._generateFormula(this.solver, isTrue, formula, this);
};
Logic._minisatSolvers=[];
Logic._termifiers=[];
/******************** LOGIC SOLVER **********************/
/* procedural version */
Logic.Solver = function (model) {
var solver = model||{};
solver.tag='Solver';
solver.clauses = []; // mutable [Clause]
solver._num2name = [null]; // no 0th var
solver._name2num = {}; // (' '+vname) -> vnum
// true and false
var F = Logic.Solver.getVarNum(solver,Logic.NAME_FALSE, false, true); // 1
var T = Logic.Solver.getVarNum(solver,Logic.NAME_TRUE, false, true); // 2
if (F !== Logic.NUM_FALSE || T !== Logic.NUM_TRUE) {
throw new Error("Assertion failure: $T and $F have wrong numeric value");
}
solver._F_used = false;
solver._T_used = false;
// It's important that these clauses are elements 0 and 1
// of the clauses array, so that they can optionally be stripped
// off. For example, _clauseData takes advantage of this fact.
solver.clauses.push(new Logic.Clause(-Logic.NUM_FALSE));
solver.clauses.push(new Logic.Clause(Logic.NUM_TRUE));
solver._formulaInfo = {}; // Formula guid -> FormulaInfo
// For generating formula variables like "$or1", "$or2", "$and1", "$and2"
solver._nextFormulaNumByType = {}; // Formula type -> next var id
// Map of Formulas whose info has `false` for either
// `occursPositively` or `occursNegatively`
solver._ungeneratedFormulas = {}; // varNum -> Formula
solver._numClausesAddedToMiniSat = 0;
solver._unsat = false; // once true, no solution henceforth
Logic._minisatSolvers[model.id] = new MiniSat(); // this takes some time; keep it decoupled from solver object
Logic._termifiers[model.id] = new Logic.Termifier(solver);
return solver;
};
// Get a var number for vname, assigning it a number if it is new.
// Setting "noCreate" to true causes the function to return 0 instead of
// creating a new variable.
// Setting "_createInternals" to true grants the ability to create $ variables.
Logic.Solver.getVarNum = function (solver, vname, noCreate, _createInternals) {
var key = ' '+vname;
if (_.has(solver._name2num, key)) {
return solver._name2num[key];
} else if (noCreate) {
return 0;
} else {
if (vname.charAt(0) === "$" && ! _createInternals) {
throw new Error("Only generated variable names can start with $");
}
var vnum = solver._num2name.length;
solver._name2num[key] = vnum;
solver._num2name.push(vname);
return vnum;
}
};
Logic.Solver.getVarName = function (solver,vnum) {
if (assert) assert(vnum, isInteger);
var num2name = solver._num2name;
if (vnum < 1 || vnum >= num2name.length) {
throw new Error("Bad variable num: " + vnum);
} else {
return num2name[vnum];
}
};
// Converts a Term to a NumTerm (if it isn't already). This is done
// when a Formula creates Clauses for a Solver, since Clauses require
// NumTerms. NumTerms stay the same, while a NameTerm like "-foo"
// might become (say) the number -3. If a NameTerm names a variable
// that doesn't exist, it is automatically created, unless noCreate
// is passed, in which case 0 is returned instead.
Logic.Solver.toNumTerm = function (solver, t, noCreate) {
if (assert) assert(t, Logic.isTerm);
if (typeof t === 'number') {
return t;
} else { // string
var not = false;
while (t.charAt(0) === '-') {
t = t.slice(1);
not = ! not;
}
var n = Logic.Solver.getVarNum(solver,t, noCreate);
if (! n) {
return 0; // must be the noCreate case
} else {
return (not ? -n : n);
}
}
};
// Converts a Term to a NameTerm (if it isn't already).
Logic.Solver.toNameTerm = function (solver,t) {
if (assert) assert(t, Logic.isTerm);
if (typeof t === 'string') {
// canonicalize, removing leading "--"
while (t.slice(0, 2) === '--') {
t = t.slice(2);
}
return t;
} else { // number
var not = false;
if (t < 0) {
not = true;
t = -t;
}
t = Logic.Solver.getVarName(solver,t);
if (not) {
t = '-' + t;
}
return t;
}
};
Logic.Solver._addClause = function (solver,cls, _extraTerms,
_useTermOverride) {
if (assert) assert(cls, Logic.isClause);
var extraTerms = null;
if (_extraTerms) {
extraTerms = _extraTerms;
if (assert) assert(extraTerms, isArrayWhere(Logic.isNumTerm));
}
var usedF = false;
var usedT = false;
var numRealTerms = cls.terms.length;
if (extraTerms) {
// extraTerms are added to the clause as is. Formula variables in
// extraTerms do not cause Formula clause generation, which is
// necessary to implement Formula clause generation.
// cls = cls.append(extraTerms);
cls = Logic.Clause.append(cls,extraTerms);
}
for (var i = 0; i < cls.terms.length; i++) {
var t = cls.terms[i];
var v = (t < 0) ? -t : t;
if (v === Logic.NUM_FALSE) {
usedF = true;
} else if (v === Logic.NUM_TRUE) {
usedT = true;
} else if (v < 1 || v >= solver._num2name.length) {
throw new Error("Bad variable number: " + v);
} else if (i < numRealTerms) {
if (_useTermOverride) {
_useTermOverride(t);
} else {
Logic.Solver._useFormulaTerm(solver,t);
}
}
}
solver._F_used = (solver._F_used || usedF);
solver._T_used = (solver._T_used || usedT);
solver.clauses.push(cls);
};
// When we actually use a Formula variable, generate clauses for it,
// based on whether the usage is positive or negative. For example,
// if the Formula `Logic.or("X", "Y")` is represented by `$or1`, which
// is variable number 5, then when you actually use 5 or -5 in a clause,
// the clauses "X v Y v -5" (when you use 5) or "-X v 5; -Y v 5"
// (when you use -5) will be generated. The clause "X v Y v -5"
// is equivalent to "5 => X v Y" (or -(X v Y) => -5), while the clauses
// "-X v 5; -Y v 5" are equivalent to "-5 => -X; -5 => -Y" (or
// "X => 5; Y => 5").
Logic.Solver._useFormulaTerm = function (solver, t, _addClausesOverride) {
if (assert) assert(t, Logic.isNumTerm);
var v = (t < 0) ? -t : t;
if (! _.has(solver._ungeneratedFormulas, v)) {
return;
}
// using a Formula's var; maybe have to generate clauses
// for the Formula
var formula = solver._ungeneratedFormulas[v];
var info = Logic.Solver._getFormulaInfo(solver, formula);
var positive = t > 0;
// To avoid overflowing the JS stack, defer calls to addClause.
// The way we get overflows is when Formulas are deeply nested
// (which happens naturally when you call Logic.sum or
// Logic.weightedSum on a long list of terms), which causes
// addClause to call useFormulaTerm to call addClause, and so
// on. Approach: The outermost useFormulaTerm keeps a list
// of clauses to add, and then adds them in a loop using a
// special argument to addClause that passes a special argument
// to useFormulaTerm that causes those clauses to go into the
// list too. Code outside of `_useFormulaTerm` and `_addClause(s)`
// does not have to pass these special arguments to call them.
var deferredAddClauses = null;
var addClauses;
if (! _addClausesOverride) {
deferredAddClauses = [];
addClauses = function (clauses, extraTerms) {
deferredAddClauses.push({clauses: clauses,
extraTerms: extraTerms});
};
} else {
addClauses = _addClausesOverride;
}
if (positive && ! info.occursPositively) {
// generate clauses for the formula.
// Eg, if we use variable `X` which represents the formula
// `A v B`, add the clause `A v B v -X`.
// By using the extraTerms argument to addClauses, we avoid
// treating this as a negative occurrence of X.
info.occursPositively = true;
var clauses = Logic.Solver._generateFormula(solver, true, formula);
addClauses(clauses, [-v]);
} else if ((! positive) && ! info.occursNegatively) {
// Eg, if we have the term `-X` where `X` represents the
// formula `A v B`, add the clauses `-A v X` and `-B v X`.
// By using the extraTerms argument to addClauses, we avoid
// treating this as a positive occurrence of X.
info.occursNegatively = true;
var clauses = Logic.Solver._generateFormula(solver, false, formula);
addClauses(clauses, [v]);
}
if (info.occursPositively && info.occursNegatively) {
delete solver._ungeneratedFormulas[v];
}
if (! (deferredAddClauses && deferredAddClauses.length)) {
return;
}
var useTerm = function (t) {
Logic.Solver._useFormulaTerm(solver, t, addClauses);
};
// This is the loop that turns recursion into iteration.
// When addClauses calls useTerm, which calls useFormulaTerm,
// the nested useFormulaTerm will add any clauses to our
// own deferredAddClauses list.
while (deferredAddClauses.length) {
var next = deferredAddClauses.pop();
Logic.Solver._addClauses(solver, next.clauses, next.extraTerms, useTerm);
}
};
Logic.Solver._addClauses = function (solver, array, _extraTerms,
_useTermOverride) {
if (assert) assert(array, isArrayWhere(Logic.isClause));
_.each(array, function (cls) {
Logic.Solver._addClause(solver, cls, _extraTerms, _useTermOverride);
});
};
Logic.Solver.require = function (/*formulaOrArray, ...*/) {
var args = _.flatten(arguments);
var solver = args[0]; args=args.slice(1);
Logic.Solver._requireForbidImpl(solver, true, args);
};
Logic.Solver.forbid = function (/*formulaOrArray, ...*/) {
var args = _.flatten(arguments);
var solver = args[0]; args=args.slice(1);
Logic.Solver._requireForbidImpl(solver, false, args);
};
Logic.Solver._requireForbidImpl = function (solver, isRequire, formulas) {
if (assert) assert(formulas, isArrayWhere(isFormulaOrTerm));
_.each(formulas, function (f) {
if (f instanceof Logic.NotFormula) {
Logic.Solver._requireForbidImpl(solver, !isRequire, [f.operand]);
} else if (f instanceof Logic.Formula) {
var info = Logic.Solver._getFormulaInfo(solver,f);
if (info.varNum !== null) {
var sign = isRequire ? 1 : -1;
Logic.Solver._addClause(solver, new Logic.Clause(sign*info.varNum));
} else {
Logic.Solver._addClauses(solver,Logic.Solver._generateFormula(solver, isRequire, f));
}
if (isRequire) {
info.isRequired = true;
} else {
info.isForbidden = true;
}
} else {
Logic.Solver._addClauses(solver, Logic.Solver._generateFormula(solver, isRequire, f));
}
});
};
Logic.Solver._generateFormula = function (solver, isTrue, formula, _termifier) {
if (assert) assert(formula, isFormulaOrTerm);
if (formula instanceof Logic.NotFormula) {
return Logic.Solver._generateFormula(solver, !isTrue, formula.operand);
} else if (formula instanceof Logic.Formula) {
var info = Logic.Solver._getFormulaInfo(solver, formula);
if ((isTrue && info.isRequired) ||
(!isTrue && info.isForbidden)) {
return [];
} else if ((isTrue && info.isForbidden) ||
(!isTrue && info.isRequired)) {
return [new Logic.Clause()]; // never satisfied clause
} else {
var ret = formula.generateClauses(isTrue,
_termifier || Logic._termifiers[solver.id]);
return _.isArray(ret) ? ret : [ret];
}
} else { // Term
var t = Logic.Solver.toNumTerm(solver, formula);
var sign = isTrue ? 1 : -1;
if (t === sign*Logic.NUM_TRUE || t === -sign*Logic.NUM_FALSE) {
return [];
} else if (t === sign*Logic.NUM_FALSE || t === -sign*Logic.NUM_TRUE) {
return [new Logic.Clause()]; // never satisfied clause
} else {
return [new Logic.Clause(sign*t)];
}
}
};
// Get clause data as an array of arrays of integers,
// for testing and debugging purposes.
Logic.Solver._clauseData = function (solver) {
var clauses = _.pluck(solver.clauses, 'terms');
if (! solver._T_used) {
clauses.splice(1, 1);
}
if (! solver._F_used) {
clauses.splice(0, 1);
}
return clauses;
};
// Get clause data as an array of human-readable strings,
// for testing and debugging purposes.
// A clause might look like "A v -B" (where "v" represents
// and OR operator).
Logic.Solver._clauseStrings = function (solver) {
var clauseData = Logic.Solver._clauseData(solver);
return _.map(clauseData, function (clause) {
return _.map(clause, function (nterm) {
var str = Logic.Solver.toNameTerm(solver,nterm);
if (/\s/.test(str)) {
// write name in quotes for readability. we don't bother
// making this string machine-parsable in the general case.
var sign = '';
if (str.charAt(0) === '-') {
// temporarily remove '-'
sign = '-';
str = str.slice(1);
}
str = sign + '"' + str + '"';
}
return str;
}).join(' v ');
});
};
Logic.Solver._getFormulaInfo = function (solver, formula, _noCreate) {
var guid = formula.guid();
if (!solver._formulaInfo[guid]) {
if (_noCreate) {
return null;
}
solver._formulaInfo[guid] = new FormulaInfo();
}
return solver._formulaInfo[guid];
};
// Takes a Formula or an array of Formulas, returns a NumTerm or
// array of NumTerms.
Logic.Solver._formulaToTerm = function (solver, formula) {
if (_.isArray(formula)) {
if (assert) assert(formula, isArrayWhere(isFormulaOrTerm));
return _.map(formula, _.bind(solver._formulaToTerm, solver)); // Check it!
} else {
if (assert) assert(formula, isFormulaOrTerm);
}
if (formula instanceof Logic.NotFormula) {
// shortcut that avoids creating a variable called
// something like "$not1" when you use Logic.not(formula).
return Logic.not(Logic.Solver._formulaToTerm(solver, formula.operand));
} else if (formula instanceof Logic.Formula) {
var info = Logic.Solver._getFormulaInfo(solver,formula);
if (info.isRequired) {
return Logic.NUM_TRUE;
} else if (info.isForbidden) {
return Logic.NUM_FALSE;
} else if (info.varNum === null) {
// generate a Solver-local formula variable like "$or1"
var type = formula.type;
if (! solver._nextFormulaNumByType[type]) {
solver._nextFormulaNumByType[type] = 1;
}
var numForVarName = solver._nextFormulaNumByType[type]++;
info.varName = "$" + formula.type + numForVarName;
info.varNum = Logic.Solver.getVarNum(solver, info.varName, false, true);
solver._ungeneratedFormulas[info.varNum] = formula;
}
return info.varNum;
} else {
// formula is a Term
return Logic.Solver.toNumTerm(solver, formula);
}
};
Logic.or = function (/*formulaOrArray, ...*/) {
var args = _.flatten(arguments);
if (args.length === 0) {
return Logic.FALSE;
} else if (args.length === 1) {
if (assert) assert(args[0], isFormulaOrTerm);
return args[0];
} else {
return new Logic.OrFormula(args);
}
};
Logic.OrFormula = function (operands) {
if (assert) assert(operands, isArrayWhere(isFormulaOrTerm));
this.operands = operands;
};
Logic._defineFormula(Logic.OrFormula, 'or', {
generateClauses: function (isTrue, t) {
if (isTrue) {
// eg A v B v C
return t.clause(this.operands);
} else {
// eg -A; -B; -C
var result = [];
_.each(this.operands, function (o) {
result.push.apply(result, t.generate(false, o));
});
return result;
}
}
});
Logic.NotFormula = function (operand) {
if (assert) assert(operand, isFormulaOrTerm);
this.operand = operand;
};
// No generation or simplification for 'not'; it is
// simplified away by the solver itself.
Logic._defineFormula(Logic.NotFormula, 'not');
Logic.and = function (/*formulaOrArray, ...*/) {
var args = _.flatten(arguments);
if (args.length === 0) {
return Logic.TRUE;
} else if (args.length === 1) {
if (assert) assert(args[0], isFormulaOrTerm);
return args[0];
} else {
return new Logic.AndFormula(args);
}
};
Logic.AndFormula = function (operands) {
if (assert) assert(operands, isArrayWhere(isFormulaOrTerm));
this.operands = operands;
};
Logic._defineFormula(Logic.AndFormula, 'and', {
generateClauses: function (isTrue, t) {
if (isTrue) {
// eg A; B; C
var result = [];
_.each(this.operands, function (o) {
result.push.apply(result, t.generate(true, o));
});
return result;
} else {
// eg -A v -B v -C
return t.clause(_.map(this.operands, Logic.not));
}
}
});
// Group `array` into groups of N, where the last group
// may be shorter than N. group([a,b,c,d,e], 3) => [[a,b,c],[d,e]]
var group = function (array, N) {
var ret = [];
for (var i = 0; i < array.length; i += N) {
ret.push(array.slice(i, i+N));
}
return ret;
};
Logic.xor = function (/*formulaOrArray, ...*/) {
var args = _.flatten(arguments);
if (args.length === 0) {
return Logic.FALSE;
} else if (args.length === 1) {
if (assert) assert(args[0], isFormulaOrTerm);
return args[0];
} else {
return new Logic.XorFormula(args);
}
};
Logic.XorFormula = function (operands) {
if (assert) assert(operands, isArrayWhere(isFormulaOrTerm));
this.operands = operands;
};
Logic._defineFormula(Logic.XorFormula, 'xor', {
generateClauses: function (isTrue, t) {
var args = this.operands;
var not = Logic.not;
if (args.length > 3) {
return t.generate(
isTrue,
Logic.xor(
_.map(group(this.operands, 3), function (group) {
return Logic.xor(group);
})));
} else if (isTrue) { // args.length <= 3
if (args.length === 0) {
return t.clause(); // always fail
} else if (args.length === 1) {
return t.clause(args[0]);
} else if (args.length === 2) {
var A = args[0], B = args[1];
return [t.clause(A, B), // A v B
t.clause(not(A), not(B))]; // -A v -B
} else if (args.length === 3) {
var A = args[0], B = args[1], C = args[2];
return [t.clause(A, B, C), // A v B v C
t.clause(A, not(B), not(C)), // A v -B v -C
t.clause(not(A), B, not(C)), // -A v B v -C
t.clause(not(A), not(B), C)]; // -A v -B v C
}
} else { // !isTrue, args.length <= 3
if (args.length === 0) {
return []; // always succeed
} else if (args.length === 1) {
return t.clause(not(args[0]));
} else if (args.length === 2) {
var A = args[0], B = args[1];
return [t.clause(A, not(B)), // A v -B
t.clause(not(A), B)]; // -A v B
} else if (args.length === 3) {
var A = args[0], B = args[1], C = args[2];
return [t.clause(not(A), not(B), not(C)), // -A v -B v -C
t.clause(not(A), B, C), // -A v B v C
t.clause(A, not(B), C), // A v -B v C
t.clause(A, B, not(C))]; // A v B v -C
}
}
}
});
Logic.atMostOne = function (/*formulaOrArray, ...*/) {
var args = _.flatten(arguments);
if (args.length <= 1) {
return Logic.TRUE;
} else {
return new Logic.AtMostOneFormula(args);
}
};
Logic.AtMostOneFormula = function (operands) {
if (assert) assert(operands, isArrayWhere(isFormulaOrTerm));
this.operands = operands;
};
Logic._defineFormula(Logic.AtMostOneFormula, 'atMostOne', {
generateClauses: function (isTrue, t) {
var args = this.operands;
var not = Logic.not;
if (args.length <= 1) {
return []; // always succeed
} else if (args.length === 2) {
return t.generate(isTrue, Logic.not(Logic.and(args)));
} else if (isTrue && args.length === 3) {
// Pick any two args; at least one is false (they aren't
// both true). This strategy would also work for
// N>3, and could provide a speed-up by having more clauses
// (N^2) but fewer propagation steps. No speed-up was
// observed on the Sudoku test from using this strategy
// up to N=10.
var clauses = [];
for (var i = 0; i < args.length; i++) {
for (var j = i+1; j < args.length; j++) {
clauses.push(t.clause(not(args[i]), not(args[j])));
}
}
return clauses;
} else if ((! isTrue) && args.length === 3) {
var A = args[0], B = args[1], C = args[2];
// Pick any two args; at least one is true (they aren't
// both false). This only works for N=3.
return [t.clause(A, B), t.clause(A, C), t.clause(B, C)];
} else {
// See the "commander variables" technique from:
// http://www.cs.cmu.edu/~wklieber/papers/2007_efficient-cnf-encoding-for-selecting-1.pdf
// But in short: At most one group has at least one "true",
// and each group has at most one "true". Formula generation
// automatically generates the right implications.
var groups = group(args, 3);
var ors = _.map(groups, function (g) { return Logic.or(g); });
if (groups[groups.length - 1].length < 2) {
// Remove final group of length 1 so we don't generate
// no-op clauses of one sort or another
groups.pop();
}
var atMostOnes = _.map(groups, function (g) {
return Logic.atMostOne(g);
});
return t.generate(isTrue, Logic.and(Logic.atMostOne(ors), atMostOnes));
}
}
});
Logic.implies = function (A, B) {
if (assert) assertNumArgs(arguments.length, 2, "Logic.implies");
return new Logic.ImpliesFormula(A, B);
};
Logic.ImpliesFormula = function (A, B) {
if (assert) assert(A, isFormulaOrTerm);
if (assert) assert(B, isFormulaOrTerm);
if (assert) assertNumArgs(arguments.length, 2, "Logic.implies");
this.A = A;
this.B = B;
};
Logic._defineFormula(Logic.ImpliesFormula, 'implies', {
generateClauses: function (isTrue, t) {
return t.generate(isTrue, Logic.or(Logic.not(this.A), this.B));
}
});
Logic.equiv = function (A, B) {
if (assert) assertNumArgs(arguments.length, 2, "Logic.equiv");
return new Logic.EquivFormula(A, B);
};
Logic.EquivFormula = function (A, B) {
if (assert) assert(A, isFormulaOrTerm);
if (assert) assert(B, isFormulaOrTerm);
if (assert) assertNumArgs(arguments.length, 2, "Logic.equiv");
this.A = A;
this.B = B;
};
Logic._defineFormula(Logic.EquivFormula, 'equiv', {
generateClauses: function (isTrue, t) {
return t.generate(!isTrue, Logic.xor(this.A, this.B));
}
});
Logic.exactlyOne = function (/*formulaOrArray, ...*/) {
var args = _.flatten(arguments);
if (args.length === 0) {
return Logic.FALSE;
} else if (args.length === 1) {
if (assert) assert(args[0], isFormulaOrTerm);
return args[0];
} else {
return new Logic.ExactlyOneFormula(args);
}
};
Logic.ExactlyOneFormula = function (operands) {
if (assert) assert(operands, isArrayWhere(isFormulaOrTerm));
this.operands = operands;
};
Logic._defineFormula(Logic.ExactlyOneFormula, 'exactlyOne', {
generateClauses: function (isTrue, t) {
var args = this.operands;
if (args.length < 3) {
return t.generate(isTrue, Logic.xor(args));
} else {
return t.generate(isTrue, Logic.and(Logic.atMostOne(args),
Logic.or(args)));
}
}
});
// List of 0 or more formulas or terms, which together represent
// a non-negative integer. Least significant bit is first. That is,
// the kth array element has a place value of 2^k.
Logic.Bits = function (formulaArray) {
if (assert) assert(formulaArray, isArrayWhere(isFormulaOrTerm));
this.bits = formulaArray; // public, immutable
};
Logic.constantBits = function (wholeNumber) {
if (assert) assert(wholeNumber, Logic.isWholeNumber);
var result = [];
while (wholeNumber) {
result.push((wholeNumber & 1) ? Logic.TRUE : Logic.FALSE);
wholeNumber >>>= 1;
}
return new Logic.Bits(result);
};
Logic.variableBits = function (baseName, nbits) {
if (assert) assert(nbits, Logic.isWholeNumber);
var result = [];
for (var i = 0; i < nbits; i++) {
result.push(baseName + '$' + i);
}
return new Logic.Bits(result);
};
// bits1 <= bits2
Logic.lessThanOrEqual = function (bits1, bits2) {
return new Logic.LessThanOrEqualFormula(bits1, bits2);
};
Logic.LessThanOrEqualFormula = function (bits1, bits2) {
if (assert) assert(bits1, Logic.isBits);
if (assert) assert(bits2, Logic.isBits);
if (assert) assertNumArgs(arguments.length, 2, "Bits comparison function");
this.bits1 = bits1;
this.bits2 = bits2;
};
var genLTE = function (bits1, bits2, t, notEqual) {
var ret = [];
// clone so we can mutate them in place
var A = bits1.bits.slice();
var B = bits2.bits.slice();
if (notEqual && ! bits2.bits.length) {
// can't be less than 0
return t.clause();
}
// if A is longer than B, the extra (high) bits
// must be 0.
while (A.length > B.length) {
var hi = A.pop();
ret.push(t.clause(Logic.not(hi)));
}
// now B.length >= A.length
// Let xors[i] be (A[i] xor B[i]), or just
// B[i] if A is too short.
var xors = _.map(B, function (b, i) {
if (i < A.length) {
return Logic.xor(A[i], b);
} else {
return b;
}
});
// Suppose we are comparing 3-bit numbers, requiring
// that ABC <= XYZ. Here is what we require:
//
// * It is false that A=1 and X=0.
// * It is false that A=X, B=1, and Y=0.
// * It is false that A=X, B=Y, C=1, and Y=0.
//
// Translating these into clauses using DeMorgan's law:
//
// * A=0 or X=1
// * (A xor X) or B=0 or Y=1
// * (A xor X) or (B xor Y) or C=0 or Y=1
//
// Since our arguments are LSB first, in the example
// we would be given [C, B, A] and [Z, Y, X] as input.
// We iterate over the first argument starting from
// the right, and build up a clause by iterating over
// the xors from the right.
//
// If we have ABC <= VWXYZ, then we still have three clauses,
// but each one is prefixed with "V or W or", because V and W
// are at the end of the xors array. This is equivalent to
// padding ABC with two zeros.
for (var i = A.length-1; i >= 0; i--) {
ret.push(t.clause(xors.slice(i+1), Logic.not(A[i]), B[i]));
}
if (notEqual) {
ret.push.apply(ret, t.generate(true, Logic.or(xors)));
}
return ret;
};
Logic._defineFormula(Logic.LessThanOrEqualFormula, 'lte', {
generateClauses: function (isTrue, t) {
if (isTrue) {
// bits1 <= bits2
return genLTE(this.bits1, this.bits2, t, false);
} else {
// bits2 < bits1
return genLTE(this.bits2, this.bits1, t, true);
}
}
});
// bits1 < bits2
Logic.lessThan = function (bits1, bits2) {
return new Logic.LessThanFormula(bits1, bits2);
};
Logic.LessThanFormula = function (bits1, bits2) {
if (assert) assert(bits1, Logic.isBits);
if (assert) assert(bits2, Logic.isBits);
if (assert) assertNumArgs(arguments.length, 2, "Bits comparison function");
this.bits1 = bits1;
this.bits2 = bits2;
};
Logic._defineFormula(Logic.LessThanFormula, 'lt', {
generateClauses: function (isTrue, t) {
if (isTrue) {
// bits1 < bits2
return genLTE(this.bits1, this.bits2, t, true);
} else {
// bits2 <= bits1
return genLTE(this.bits2, this.bits1, t, false);
}
}
});
Logic.greaterThan = function (bits1, bits2) {
return Logic.lessThan(bits2, bits1);
};
Logic.greaterThanOrEqual = function (bits1, bits2) {
return Logic.lessThanOrEqual(bits2, bits1);
};
Logic.equalBits = function (bits1, bits2) {
return new Logic.EqualBitsFormula(bits1, bits2);
};
Logic.EqualBitsFormula = function (bits1, bits2) {
if (assert) assert(bits1, Logic.isBits);
if (assert) assert(bits2, Logic.isBits);
if (assert) assertNumArgs(arguments.length, 2, "Logic.equalBits");
this.bits1 = bits1;
this.bits2 = bits2;
};
Logic._defineFormula(Logic.EqualBitsFormula, 'equalBits', {
generateClauses: function (isTrue, t) {
var A = this.bits1.bits;
var B = this.bits2.bits;
var nbits = Math.max(A.length, B.length);
var facts = [];
for (var i = 0; i < nbits; i++) {
if (i >= A.length) {
facts.push(Logic.not(B[i]));
} else if (i >= B.length) {
facts.push(Logic.not(A[i]));
} else {
facts.push(Logic.equiv(A[i], B[i]));
}
}
return t.generate(isTrue, Logic.and(facts));
}
});
// Definition of full-adder and half-adder:
//
// A full-adder is a 3-input, 2-output gate producing the sum of its
// inputs as a 2-bit binary number. The most significant bit is called
// "carry", the least significant "sum". A half-adder does the same
// thing, but has only 2 inputs (and can therefore never output a
// "3").
//
// The half-adder sum bit is really just an XOR, and the carry bit
// is really just an AND. However, they get their own formula types
// here to enhance readability of the generated clauses.
Logic.HalfAdderSum = function (formula1, formula2) {
if (assert) assert(formula1, isFormulaOrTerm);
if (assert) assert(formula2, isFormulaOrTerm);
if (assert) assertNumArgs(arguments.length, 2, "Logic.HalfAdderSum");
this.a = formula1;
this.b = formula2;
};
Logic._defineFormula(Logic.HalfAdderSum, 'hsum', {
generateClauses: function (isTrue, t) {
return t.generate(isTrue, Logic.xor(this.a, this.b));
}
});
Logic.HalfAdderCarry = function (formula1, formula2) {
if (assert) assert(formula1, isFormulaOrTerm);
if (assert) assert(formula2, isFormulaOrTerm);
if (assert) assertNumArgs(arguments.length, 2, "Logic.HalfAdderCarry");
this.a = formula1;
this.b = formula2;
};
Logic._defineFormula(Logic.HalfAdderCarry, 'hcarry', {
generateClauses: function (isTrue, t) {
return t.generate(isTrue, Logic.and(this.a, this.b));
}
});
Logic.FullAdderSum = function (formula1, formula2, formula3) {
if (assert) assert(formula1, isFormulaOrTerm);
if (assert) assert(formula2, isFormulaOrTerm);
if (assert) assert(formula3, isFormulaOrTerm);
if (assert) assertNumArgs(arguments.length, 3, "Logic.FullAdderSum");
this.a = formula1;
this.b = formula2;
this.c = formula3;
};
Logic._defineFormula(Logic.FullAdderSum, 'fsum', {
generateClauses: function (isTrue, t) {
return t.generate(isTrue, Logic.xor(this.a, this.b, this.c));
}
});
Logic.FullAdderCarry = function (formula1, formula2, formula3) {
if (assert) assert(formula1, isFormulaOrTerm);
if (assert) assert(formula2, isFormulaOrTerm);
if (assert) assert(formula3, isFormulaOrTerm);
if (assert) assertNumArgs(arguments.length, 3, "Logic.FullAdderCarry");
this.a = formula1;
this.b = formula2;
this.c = formula3;
};
Logic._defineFormula(Logic.FullAdderCarry, 'fcarry', {
generateClauses: function (isTrue, t) {
return t.generate(! isTrue,
Logic.atMostOne(this.a, this.b, this.c));
}
});
// Implements the Adder strategy from the MiniSat+ paper:
// http://minisat.se/downloads/MiniSat+.pdf
// "Translating Pseudo-boolean Constraints into SAT"
//
// Takes a list of list of Formulas. The first list is bits
// to give weight 1; the second is bits to give weight 2;
// the third is bits to give weight 4; and so on.
//
// Returns an array of Logic.FormulaOrTerm.
var binaryWeightedSum = function (varsByWeight) {
if (assert) assert(varsByWeight,
isArrayWhere(isArrayWhere(isFormulaOrTerm)));
// initialize buckets to a two-level clone of varsByWeight
var buckets = _.map(varsByWeight, _.clone);
var lowestWeight = 0; // index of the first non-empty array
var output = [];
while (lowestWeight < buckets.length) {
var bucket = buckets[lowestWeight];
if (! bucket.length) {
output.push(Logic.FALSE);
lowestWeight++;
} else if (bucket.length === 1) {
output.push(bucket[0]);
lowestWeight++;
} else if (bucket.length === 2) {
var sum = new Logic.HalfAdderSum(bucket[0], bucket[1]);
var carry = new Logic.HalfAdderCarry(bucket[0], bucket[1]);
bucket.length = 0;
bucket.push(sum);
pushToNth(buckets, lowestWeight+1, carry);
} else {
// Whether we take variables from the start or end of the
// bucket (i.e. `pop` or `shift`) determines the shape of the tree.
// Empirically, some logic problems are faster with `shift` (2x or so),
// but `pop` gives an order-of-magnitude speed-up on the Meteor Version
// Solver "benchmark-tests" suite (Slava's benchmarks based on data from
// Rails). So, `pop` it is.
var c = bucket.pop();
var b = bucket.pop();
var a = bucket.pop();
var sum = new Logic.FullAdderSum(a, b, c);
var carry = new Logic.FullAdderCarry(a, b, c);
bucket.push(sum);
pushToNth(buckets, lowestWeight+1, carry);
}
}
return output;
};
// Push `newItem` onto the array at arrayOfArrays[n],
// first ensuring that it exists by pushing empty
// arrays onto arrayOfArrays.
var pushToNth = function (arrayOfArrays, n, newItem) {
while (n >= arrayOfArrays.length) {
arrayOfArrays.push([]);
}
arrayOfArrays[n].push(newItem);
};
var checkWeightedSumArgs = function (formulas, weights) {
if (assert) assert(formulas, isArrayWhere(isFormulaOrTerm));
if (typeof weights === 'number') {
if (assert) assert(weights, Logic.isWholeNumber);
} else {
if (assert) assert(weights, isArrayWhere(Logic.isWholeNumber));
if (formulas.length !== weights.length) {
throw new Error("Formula array and weight array must be same length" +
"; they are " + formulas.length + " and " + weights.length);
}
}
};
Logic.weightedSum = function (formulas, weights) {
checkWeightedSumArgs(formulas, weights);
if (formulas.length === 0) {
return new Logic.Bits([]);
}
if (typeof weights === 'number') {
weights = _.map(formulas, function () { return weights; });
}
var binaryWeighted = [];
_.each(formulas, function (f, i) {
var w = weights[i];
var whichBit = 0;
while (w) {
if (w & 1) {
pushToNth(binaryWeighted, whichBit, f);
}
w >>>= 1;
whichBit++;
}
});
return new Logic.Bits(binaryWeightedSum(binaryWeighted));
};
Logic.sum = function (/*formulaOrBitsOrArray, ...*/) {
var things = _.flatten(arguments);
if (assert) assert(things, isArrayWhere(isFormulaOrTermOrBits));
var binaryWeighted = [];
_.each(things, function (x) {
if (x instanceof Logic.Bits) {
_.each(x.bits, function (b, i) {
pushToNth(binaryWeighted, i, b);
});
} else {
pushToNth(binaryWeighted, 0, x);
}
});
return new Logic.Bits(binaryWeightedSum(binaryWeighted));
};
// Parse text
// TODO: only simple expressions can be parsed
Logic.Parse = function (formulas) {
var res=[];
if (!(formulas instanceof Array)) formulas=[formulas];
formulas.forEach(function (formula) {
var ast = parse(formula);
if (ast && ast.type == 'BinaryExpression') {
switch (ast.operator) {
case '^':
res.push(Logic.atMostOne(ast.left.name, ast.right.name))
break;
case '|':
res.push(Logic.or(ast.left.name, ast.right.name))
break;
case '&':
res.push(Logic.and(ast.left.name, ast.right.name))
break;
}
}
});
if (res.length==1) return res[0]; else return res;
}
////////////////////////////////////////
Logic.Solver.solve = function (solver, _assumpVar) {
if (_assumpVar !== undefined) {
if (! (_assumpVar >= 1)) {
throw new Error("_assumpVar must be a variable number");
}
}
if (solver._unsat) {
return null;
}
while (solver._numClausesAddedToMiniSat < solver.clauses.length) {
var i = solver._numClausesAddedToMiniSat;
var terms = solver.clauses[i].terms;
if (assert) assert(terms, isArrayWhere(Logic.isNumTerm));
var stillSat = Logic._minisatSolvers[solver.id].addClause(terms);
solver._numClausesAddedToMiniSat++;
if (! stillSat) {
solver._unsat = true;
return null;
}
}
if (assert) assert(solver._num2name.length - 1, Logic.isWholeNumber);
Logic._minisatSolvers[solver.id].ensureVar(solver._num2name.length - 1);
var stillSat = (_assumpVar ?
Logic._minisatSolvers[solver.id].solveAssuming(_assumpVar) :
Logic._minisatSolvers[solver.id].solve());
if (! stillSat) {
if (! _assumpVar) {
solver._unsat = true;
}
return null;
}
return new Logic.Solution(solver, Logic._minisatSolvers[solver.id].getSolution());
};
Logic.Solver.solveAssuming = function (solver,formula) {
if (assert) assert(formula, isFormulaOrTerm);
// Wrap the formula in a formula of type Assumption, so that
// we always generate a var like `$assump123`, regardless
// of whether `formula` is a Term, a NotFormula, an already
// required or forbidden Formula, etc.
var assump = new Logic.Assumption(formula);
var assumpVar = Logic.Solver._formulaToTerm(solver,assump);
if (! (typeof assumpVar === 'number' && assumpVar > 0)) {
throw new Error("Assertion failure: not a positive numeric term");
}
// Generate clauses as if we used the assumption variable in a
// clause, in the positive. So if we assume "A v B", we might get a
// clause like "A v B v -$assump123" (or actually, "$or1 v
// -$assump123"), as if we had used $assump123 in a clause. Instead
// of using it in a clause, though, we temporarily assume it to be
// true.
Logic.Solver._useFormulaTerm(solver,assumpVar);
var result = Logic.Solver.solve(solver,assumpVar);
// Tell MiniSat that we will never use assumpVar again.
// The formula may be used again, however. (For example, you
// can solve assuming a formula F, and if it works, require F.)
Logic._minisatSolvers[solver.id].retireVar(assumpVar);
return result;
};
Logic.Assumption = function (formula) {
if (assert) assert(formula, isFormulaOrTerm);
this.formula = formula;
};
Logic._defineFormula(Logic.Assumption, 'assump', {
generateClauses: function (isTrue, t) {
if (isTrue) {
return t.clause(this.formula);
} else {
return t.clause(Logic.not(this.formula));
}
}
});
Logic.Solution = function (_solver, _assignment) {
var self = this;
self._solver = _solver;
self._assignment = _assignment;
// save a snapshot of which formulas have variables designated
// for them, but where we haven't generated clauses that constrain
// those variables in both the positive and the negative direction.
self._ungeneratedFormulas = _.clone(_solver._ungeneratedFormulas);
self._formulaValueCache = {};
self._termifier = new Logic.Termifier(self._solver);
// Normally, when a Formula uses a Termifier to generate clauses that
// refer to other Formulas, the Termifier replaces the Formulas with
// their variables. We hijack this mechanism to replace the Formulas
// with their truth variables instead, leading to recursive evaluation.
// Note that we cache the evaluated truth values of Formulas to avoid
// redundant evaluation.
self._termifier.term = function (formula) {
return self.evaluate(formula) ? Logic.NUM_TRUE : Logic.NUM_FALSE;
};
// When true, evaluation doesn't throw errors when
// `evaluate` or `getWeightedSum` encounter named variables that are
// unknown or variables that weren't present when this Solution was
// generated. Instead, the unknown variables are assumed to be false.
self._ignoreUnknownVariables = false;
};
Logic.Solution.prototype.ignoreUnknownVariables = function () {
// We only make this settable one way (false to true).
// Setting it back and forth would be questionable, since we keep
// a cache of Formula evaluations.
this._ignoreUnknownVariables = true;
};
// Get a map of variables to their assignments,
// such as `{A: true, B: false, C: true}`.
// Internal variables are excluded.
Logic.Solution.prototype.getMap = function () {
var solver = this._solver;
var assignment = this._assignment;
var result = {};
for (var i = 1; i < assignment.length; i++) {
var name = Logic.Solver.getVarName(solver,i);
if (name && name.charAt(0) !== '$') {
result[name] = assignment[i];
}
}
return result;
};
// Get an array of variables that are assigned
// `true` by this solution, sorted by name.
// Internal variables are excluded.
Logic.Solution.prototype.getTrueVars = function () {
var solver = this._solver;
var assignment = this._assignment;
var result = [];
for (var i = 1; i < assignment.length; i++) {
if (assignment[i]) {
var name = Logic.Solver.getVarName(solver,i);
if (name && name.charAt(0) !== '$') {
result.push(name);
}
}
}
result.sort();
return result;
};
// Get a Formula that says that the variables are assigned
// according to this solution. (Internal variables are
// excluded.) By forbidding this Formula and solving again,
// you can see if there are other solutions.
Logic.Solution.prototype.getFormula = function () {
var solver = this._solver;
var assignment = this._assignment;
var terms = [];
for (var i = 1; i < assignment.length; i++) {
var name = Logic.Solver.getVarName(solver,i);
if (name && name.charAt(0) !== '$') {
terms.push(assignment[i] ? i : -i);
}
}
return Logic.and(terms);
};
// Returns a boolean if the argument is a Formula (or Term), and an integer
// if the argument is a Logic.Bits.
Logic.Solution.prototype.evaluate = function (formulaOrBits) {
var self = this;
if (assert) assert(formulaOrBits, isFormulaOrTermOrBits);
if (formulaOrBits instanceof Logic.Bits) {
// Evaluate to an integer
var ret = 0;
_.each(formulaOrBits.bits, function (f, i) {
if (self.evaluate(f)) {
ret += 1 << i;
}
});
return ret;
}
var solver = self._solver;
var ignoreUnknownVariables = self._ignoreUnknownVariables;
var assignment = self._assignment;
var formula = formulaOrBits;
if (formula instanceof Logic.NotFormula) {
return ! self.evaluate(formula.operand);
} else if (formula instanceof Logic.Formula) {
var cachedResult = self._formulaValueCache[formula.guid()];
if (typeof cachedResult === 'boolean') {
return cachedResult;
} else {
var value;
var info = Logic.Solver._getFormulaInfo(solver, formula, true);
if (info && info.varNum && info.varNum < assignment.length &&
! _.has(self._ungeneratedFormulas, info.varNum)) {
// as an optimization, read the value of the formula directly
// from a variable if the formula's clauses were completely
// generated at the time of solving. (We must be careful,
// because if we didn't generate both the positive and the
// negative polarity clauses for the formula, then the formula
// variable is not actually constrained to have the right
// value.)
value = assignment[info.varNum];
} else {
var clauses = Logic.Solver._generateFormula(solver, true, formula, self._termifier);
var value = _.all(clauses, function (cls) {
return _.any(cls.terms, function (t) {
return self.evaluate(t);
});
});
}
self._formulaValueCache[formula.guid()] = value;
return value;
}
} else {
// Term; convert to numeric (possibly negative), but throw
// an error if the name is not found. If `ignoreUnknownVariables`
// is set, return false instead.
var numTerm = Logic.Solver.toNumTerm(solver, formula, true);
if (! numTerm) {
if (ignoreUnknownVariables) {
return false;
} else {
// formula must be a NameTerm naming a variable that doesn't exist
var vname = String(formula).replace(/^-*/, '');
throw new Error("No such variable: " + vname);
}
}
var v = numTerm;
var isNot = false;
if (numTerm < 0) {
v = -v;
isNot = true;
}
if (v < 1 || v >= assignment.length) {
var vname = v;
if (v >= 1 && v < solver._num2name.length) {
vname = solver._num2name[v];
}
if (ignoreUnknownVariables) {
return false;
} else {
throw new Error("Variable not part of solution: " + vname);
}
}
var ret = assignment[v];
if (isNot) {
ret = ! ret;
}
return ret;
}
};
Logic.Solution.prototype.getWeightedSum = function (formulas, weights) {
checkWeightedSumArgs(formulas, weights);
var total = 0;
if (typeof weights === 'number') {
for (var i = 0; i < formulas.length; i++) {
total += weights * (this.evaluate(formulas[i]) ? 1 : 0);
}
} else {
for (var i = 0; i < formulas.length; i++) {
total += weights[i] * (this.evaluate(formulas[i]) ? 1 : 0);
}
}
return total;
};
var getNonZeroWeightedTerms = function (costTerms, costWeights) {
if (typeof costWeights === 'number') {
return costWeights ? costTerms : [];
} else {
var terms = [];
for (var i = 0; i < costTerms.length; i++) {
if (costWeights[i]) {
terms.push(costTerms[i]);
}
}
return terms;
}
};
// See comments on minimizeWeightedSum and maximizeWeightedSum.
var minMaxWS = function (solver, solution, costTerms, costWeights, options,
isMin) {
var curSolution = solution;
var curCost = curSolution.getWeightedSum(costTerms, costWeights);
var optFormula = options && options.formula;
var weightedSum = (optFormula || Logic.weightedSum(costTerms, costWeights));
var progress = options && options.progress;
var strategy = options && options.strategy;
// array of terms with non-zero weights, populated on demand
var nonZeroTerms = null;
if (isMin && curCost > 0) {
// try to skip straight to 0 cost, because if it works, it could
// save us some time
if (progress) {
progress('trying', 0);
}
var zeroSolution = null;
nonZeroTerms = getNonZeroWeightedTerms(costTerms, costWeights);
var zeroSolution = Logic.Solver.solveAssuming(solver,Logic.not(Logic.or(nonZeroTerms)));
if (zeroSolution) {
curSolution = zeroSolution;
curCost = 0;
}
}
if (isMin && strategy === 'bottom-up') {
for (var trialCost = 1; trialCost < curCost; trialCost++) {
if (progress) {
progress('trying', trialCost);
}
var costIsTrialCost = Logic.equalBits(
weightedSum, Logic.constantBits(trialCost));
var newSolution = Logic.Solver.solveAssuming(solver,costIsTrialCost);
if (newSolution) {
curSolution = newSolution;
curCost = trialCost;
break;
}
}
} else if (strategy && strategy !== 'default') {
throw new Error("Bad strategy: " + strategy);
} else {
strategy = 'default';
}
if (strategy === 'default') {
// for minimization, count down from current cost. for maximization,
// count up.
while (isMin ? curCost > 0 : true) {
if (progress) {
progress('improving', curCost);
}
var improvement = (isMin ? Logic.lessThan : Logic.greaterThan)(
weightedSum, Logic.constantBits(curCost));
var newSolution = Logic.Solver.solveAssuming(solver, improvement);
if (! newSolution) {
break;
}
Logic.Solver.require(solver,improvement);
curSolution = newSolution;
curCost = curSolution.getWeightedSum(costTerms, costWeights);
}
}
if (isMin && curCost === 0) {
// express the requirement that the weighted sum be 0 in an efficient
// way for the solver (all terms with non-zero weights must be 0)
if (! nonZeroTerms) {
nonZeroTerms = getNonZeroWeightedTerms(costTerms, costWeights);
}
Logic.Solver.forbid(solver,nonZeroTerms);
} else {
Logic.Solver.require(solver,Logic.equalBits(weightedSum, Logic.constantBits(curCost)));
}
if (progress) {
progress('finished', curCost);
}
return curSolution;
};
// Minimize (or maximize) the dot product of costTerms and
// costWeights, and further, require (as in solver.require) that the
// value of the dot product be equal to the optimum found. Returns a
// valid solution where this optimum is achieved.
//
// `solution` must be a current valid solution as returned from
// `solve` or `solveAssuming`. It is used as a starting point (to
// evaluate the current cost).
//
// costWeights is an array (of same length as costTerms) or a single
// WholeNumber.
//
// if the caller passes options.formula, it should be the formula
// Logic.weightedSum(costTerms, costWeights). The optimizer will use
// this existing formula rather than generating a new one (for
// efficiency). The optimizer still wants to know the terms and
// weights, because it is more efficient for it to evaluate the
// current cost using them directly rather than the formula.
//
// options.progress: a function that takes two arguments, to call at
// particular times during optimization. Called with arguments
// ('improving', cost) when about to search for a way to improve on
// `cost`, and called with arguments ('finished', cost) when the
// optimum is reached. There's also ('trying', cost) when a cost
// is tried directly (which is usually done with 0 right off the bat).
//
// options.strategy: a string hinting how to go about the optimization.
// the default strategy (option absent or 'default') is to work down
// from the current cost for minimization or up from the current cost
// for maximization, and iteratively insist that the cost be made lower
// if possible. For minimization, the alternate strategy 'bottom-up' is
// available, which starts at 0 and tries ever higher costs until one
// works. All strategies first try and see if a cost of 0 is possible.
// ("costTerms" is kind of a misnomer since they may be Formulas or Terms.)
Logic.Solver.minimizeWeightedSum = function (solver, solution, costTerms,
costWeights, options) {
return minMaxWS(solver, solution, costTerms, costWeights, options, true);
};
Logic.Solver.maximizeWeightedSum = function (solver, solution, costTerms,
costWeights, options) {
return minMaxWS(solver, solution, costTerms, costWeights, options, false);
};
module.exports = Logic;
};
BundleModuleCode['logic/minisat_wrapper.js']=function (module,exports){
var C_MINISAT = Require("logic/minisat.js");
var _ = Require("ext/underscore");
var MiniSat;
MiniSat = function () {
// A MiniSat object wraps an instance of "native" MiniSat. You can
// have as many MiniSat objects as you want, and they are all
// independent.
//
// C is the "module" object created by Emscripten. We wrap the
// output of Emscripten in a closure, so each call to C_MINISAT()
// actually instantiates a separate C environment, including
// the "native" heap.
//
// The methods available on `C` include the global functions we
// define in `logic-solver.cc`, each prefixed with `_`, and a varied
// assortment of helpers put there by Emscripten, some of which are
// documented here:
// http://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html
//
// See the README in the meteor/minisat repo for more notes about
// our build of MiniSat.
var C = this._C = C_MINISAT();
this._native = {
getStackPointer: function () {
return C.Runtime.stackSave();
},
setStackPointer: function (ptr) {
C.Runtime.stackRestore(ptr);
},
allocateBytes: function (len) {
return C.allocate(len, 'i8', C.ALLOC_STACK);
},
pushString: function (str) {
return this.allocateBytes(C.intArrayFromString(str));
},
savingStack: function (func) {
var SP = this.getStackPointer();
try {
return func(this, C);
} finally {
this.setStackPointer(SP);
}
}
};
C._createTheSolver();
// useful log for debugging and testing
this._clauses = [];
};
// Make sure MiniSat has allocated space in its model for v,
// even if v is unused. If we have variables A,B,C,D which
// are numbers 1,2,3,4, for example, but we never actually use
// C and D, calling ensureVar(4) will make MiniSat give us
// solution values for them anyway.
MiniSat.prototype.ensureVar = function (v) {
this._C._ensureVar(v);
};
// Add a clause, in the form of an array of Logic.NumTerms.
// Returns true if the problem is still satisfiable
// (as far as we know without doing more work), and false if
// we can already tell that it is unsatisfiable.
MiniSat.prototype.addClause = function (terms) {
this._clauses.push(terms);
return this._native.savingStack(function (native, C) {
var termsPtr = C.allocate((terms.length+1)*4, 'i32', C.ALLOC_STACK);
_.each(terms, function (t, i) {
C.setValue(termsPtr + i*4, t, 'i32');
});
C.setValue(termsPtr + terms.length*4, 0, 'i32'); // 0-terminate
return C._addClause(termsPtr) ? true : false;
});
};
MiniSat.prototype.solve = function () {
return this._C._solve() ? true : false;
};
MiniSat.prototype.solveAssuming = function (v) {
return this._C._solveAssuming(v) ? true : false;
};
MiniSat.prototype.getSolution = function () {
var solution = [null]; // no 0th var
var C = this._C;
var numVars = C._getNumVars();
var solPtr = C._getSolution();
for (var i = 0; i < numVars; i++) {
// 0 is Minisat::l_True (lifted "true").
// Internally, Minisat has three states for a variable:
// true, false, and undetermined. It doesn't distinguish
// between "false" and "undetermined" in solutions though
// (I think it sets undetermined variables to false).
solution[i+1] = (C.getValue(solPtr+i, 'i8') === 0);
}
return solution;
};
MiniSat.prototype.retireVar = function (v) {
this._C._retireVar(v);
};
// The "conflict clause" feature of MiniSat is not what it sounds
// like, unfortunately -- it doesn't help explain conflicts.
// It only tells us which assumption vars are to blame for a failed
// solveAssuming (and we only ever pass one var).
// We keep this function around in case we discover a use for it.
MiniSat.prototype.getConflictClause = function () {
var C = this._C;
var numTerms = C._getConflictClauseSize();
var clausePtr = C._getConflictClause();
var terms = [];
for (var i = 0; i < numTerms; i++) {
var t = C.getValue(clausePtr + i*4, 'i32');
var v = (t >>> 1);
var s = (t & 1) ? -1 : 1;
terms[i] = v * s;
}
return terms;
};
module.exports = MiniSat;
};
BundleModuleCode['logic/minisat.js']=function (module,exports){
var C_MINISAT;
// This file is generated by the meteor/minisat repo.
// See that repo's README for instructions for building it.
C_MINISAT=(function(){var module={};var require=(function(){});var process={argv:["node","minisat"],on:(function(){}),stdout:{write:(function(str){console.log("MINISAT-out:",str.replace(/\n$/,""))})},stderr:{write:(function(str){console.log("MINISAT-err:",str.replace(/\n$/,""))})}};var window=0;var Module;if(!Module)Module=(typeof Module!=="undefined"?Module:null)||{};var moduleOverrides={};for(var key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function";var ENVIRONMENT_IS_WEB=typeof window==="object";var ENVIRONMENT_IS_WORKER=typeof importScripts==="function";var ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;if(ENVIRONMENT_IS_NODE){if(!Module["print"])Module["print"]=function print(x){process["stdout"].write(x+"\n")};if(!Module["printErr"])Module["printErr"]=function printErr(x){process["stderr"].write(x+"\n")};var nodeFS=require("fs");var nodePath=require("path");Module["read"]=function read(filename,binary){filename=nodePath["normalize"](filename);var ret=nodeFS["readFileSync"](filename);if(!ret&&filename!=nodePath["resolve"](filename)){filename=path.join(__dirname,"..","src",filename);ret=nodeFS["readFileSync"](filename)}if(ret&&!binary)ret=ret.toString();return ret};Module["readBinary"]=function readBinary(filename){return Module["read"](filename,true)};Module["load"]=function load(f){globalEval(read(f))};if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}else{Module["thisProgram"]="unknown-program"}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}))}else if(ENVIRONMENT_IS_SHELL){if(!Module["print"])Module["print"]=print;if(typeof printErr!="undefined")Module["printErr"]=printErr;if(typeof read!="undefined"){Module["read"]=read}else{Module["read"]=function read(){throw"no read() available (jsc?)"}}Module["readBinary"]=function readBinary(f){if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}var data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}this["Module"]=Module}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof console!=="undefined"){if(!Module["print"])Module["print"]=function print(x){console.log(x)};if(!Module["printErr"])Module["printErr"]=function printErr(x){console.log(x)}}else{var TRY_USE_DUMP=false;if(!Module["print"])Module["print"]=TRY_USE_DUMP&&typeof dump!=="undefined"?(function(x){dump(x)}):(function(x){})}if(ENVIRONMENT_IS_WEB){window["Module"]=Module}else{Module["load"]=importScripts}}else{throw"Unknown runtime environment. Where are we?"}function globalEval(x){eval.call(null,x)}if(!Module["load"]&&Module["read"]){Module["load"]=function load(f){globalEval(Module["read"](f))}}if(!Module["print"]){Module["print"]=(function(){})}if(!Module["printErr"]){Module["printErr"]=Module["print"]}if(!Module["arguments"]){Module["arguments"]=[]}if(!Module["thisProgram"]){Module["thisProgram"]="./this.program"}Module.print=Module["print"];Module.printErr=Module["printErr"];Module["preRun"]=[];Module["postRun"]=[];for(var key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}var Runtime={setTempRet0:(function(value){tempRet0=value}),getTempRet0:(function(){return tempRet0}),stackSave:(function(){return STACKTOP}),stackRestore:(function(stackTop){STACKTOP=stackTop}),getNativeTypeSize:(function(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return Runtime.QUANTUM_SIZE}else if(type[0]==="i"){var bits=parseInt(type.substr(1));assert(bits%8===0);return bits/8}else{return 0}}}}),getNativeFieldSize:(function(type){return Math.max(Runtime.getNativeTypeSize(type),Runtime.QUANTUM_SIZE)}),STACK_ALIGN:16,getAlignSize:(function(type,size,vararg){if(!vararg&&(type=="i64"||type=="double"))return 8;if(!type)return Math.min(size,8);return Math.min(size||(type?Runtime.getNativeFieldSize(type):0),Runtime.QUANTUM_SIZE)}),dynCall:(function(sig,ptr,args){if(args&&args.length){if(!args.splice)args=Array.prototype.slice.call(args);args.splice(0,0,ptr);return Module["dynCall_"+sig].apply(null,args)}else{return Module["dynCall_"+sig].call(null,ptr)}}),functionPointers:[],addFunction:(function(func){for(var i=0;i<Runtime.functionPointers.length;i++){if(!Runtime.functionPointers[i]){Runtime.functionPointers[i]=func;return 2*(1+i)}}throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS."}),removeFunction:(function(index){Runtime.functionPointers[(index-2)/2]=null}),getAsmConst:(function(code,numArgs){if(!Runtime.asmConstCache)Runtime.asmConstCache={};var func=Runtime.asmConstCache[code];if(func)return func;var args=[];for(var i=0;i<numArgs;i++){args.push(String.fromCharCode(36)+i)}var source=Pointer_stringify(code);if(source[0]==='"'){if(source.indexOf('"',1)===source.length-1){source=source.substr(1,source.length-2)}else{abort("invalid EM_ASM input |"+source+"|. Please use EM_ASM(..code..) (no quotes) or EM_ASM({ ..code($0).. }, input) (to input values)")}}try{var evalled=eval("(function(Module, FS) { return function("+args.join(",")+"){ "+source+" } })")(Module,typeof FS!=="undefined"?FS:null)}catch(e){Module.printErr("error in executing inline EM_ASM code: "+e+" on: \n\n"+source+"\n\nwith args |"+args+"| (make sure to use the right one out of EM_ASM, EM_ASM_ARGS, etc.)");throw e}return Runtime.asmConstCache[code]=evalled}),warnOnce:(function(text){if(!Runtime.warnOnce.shown)Runtime.warnOnce.shown={};if(!Runtime.warnOnce.shown[text]){Runtime.warnOnce.shown[text]=1;Module.printErr(text)}}),funcWrappers:{},getFuncWrapper:(function(func,sig){assert(sig);if(!Runtime.funcWrappers[sig]){Runtime.funcWrappers[sig]={}}var sigCache=Runtime.funcWrappers[sig];if(!sigCache[func]){sigCache[func]=function dynCall_wrapper(){return Runtime.dynCall(sig,func,arguments)}}return sigCache[func]}),UTF8Processor:(function(){var buffer=[];var needed=0;this.processCChar=(function(code){code=code&255;if(buffer.length==0){if((code&128)==0){return String.fromCharCode(code)}buffer.push(code);if((code&224)==192){needed=1}else if((code&240)==224){needed=2}else{needed=3}return""}if(needed){buffer.push(code);needed--;if(needed>0)return""}var c1=buffer[0];var c2=buffer[1];var c3=buffer[2];var c4=buffer[3];var ret;if(buffer.length==2){ret=String.fromCharCode((c1&31)<<6|c2&63)}else if(buffer.length==3){ret=String.fromCharCode((c1&15)<<12|(c2&63)<<6|c3&63)}else{var codePoint=(c1&7)<<18|(c2&63)<<12|(c3&63)<<6|c4&63;ret=String.fromCharCode(((codePoint-65536)/1024|0)+55296,(codePoint-65536)%1024+56320)}buffer.length=0;return ret});this.processJSString=function processJSString(string){string=unescape(encodeURIComponent(string));var ret=[];for(var i=0;i<string.length;i++){ret.push(string.charCodeAt(i))}return ret}}),getCompilerSetting:(function(name){throw"You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work"}),stackAlloc:(function(size){var ret=STACKTOP;STACKTOP=STACKTOP+size|0;STACKTOP=STACKTOP+15&-16;return ret}),staticAlloc:(function(size){var ret=STATICTOP;STATICTOP=STATICTOP+size|0;STATICTOP=STATICTOP+15&-16;return ret}),dynamicAlloc:(function(size){var ret=DYNAMICTOP;DYNAMICTOP=DYNAMICTOP+size|0;DYNAMICTOP=DYNAMICTOP+15&-16;if(DYNAMICTOP>=TOTAL_MEMORY)enlargeMemory();return ret}),alignMemory:(function(size,quantum){var ret=size=Math.ceil(size/(quantum?quantum:16))*(quantum?quantum:16);return ret}),makeBigInt:(function(low,high,unsigned){var ret=unsigned?+(low>>>0)+ +(high>>>0)*+4294967296:+(low>>>0)+ +(high|0)*+4294967296;return ret}),GLOBAL_BASE:8,QUANTUM_SIZE:4,__dummy__:0};Module["Runtime"]=Runtime;var __THREW__=0;var ABORT=false;var EXITSTATUS=0;var undef=0;var tempValue,tempInt,tempBigInt,tempInt2,tempBigInt2,tempPair,tempBigIntI,tempBigIntR,tempBigIntS,tempBigIntP,tempBigIntD,tempDouble,tempFloat;var tempI64,tempI64b;var tempRet0,tempRet1,tempRet2,tempRet3,tempRet4,tempRet5,tempRet6,tempRet7,tempRet8,tempRet9;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var globalScope=this;function getCFunc(ident){var func=Module["_"+ident];if(!func){try{func=eval("_"+ident)}catch(e){}}assert(func,"Cannot call unknown function "+ident+" (perhaps LLVM optimizations or closure removed it?)");return func}var cwrap,ccall;((function(){var JSfuncs={"stackSave":(function(){Runtime.stackSave()}),"stackRestore":(function(){Runtime.stackRestore()}),"arrayToC":(function(arr){var ret=Runtime.stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}),"stringToC":(function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=Runtime.stackAlloc((str.length<<2)+1);writeStringToMemory(str,ret)}return ret})};var toC={"string":JSfuncs["stringToC"],"array":JSfuncs["arrayToC"]};ccall=function ccallFunc(ident,returnType,argTypes,args){var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=Runtime.stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);if(returnType==="string")ret=Pointer_stringify(ret);if(stack!==0)Runtime.stackRestore(stack);return ret};var sourceRegex=/^function\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/;function parseJSFunc(jsfunc){var parsed=jsfunc.toString().match(sourceRegex).slice(1);return{arguments:parsed[0],body:parsed[1],returnValue:parsed[2]}}var JSsource={};for(var fun in JSfuncs){if(JSfuncs.hasOwnProperty(fun)){JSsource[fun]=parseJSFunc(JSfuncs[fun])}}cwrap=function cwrap(ident,returnType,argTypes){argTypes=argTypes||[];var cfunc=getCFunc(ident);var numericArgs=argTypes.every((function(type){return type==="number"}));var numericRet=returnType!=="string";if(numericRet&&numericArgs){return cfunc}var argNames=argTypes.map((function(x,i){return"$"+i}));var funcstr="(function("+argNames.join(",")+") {";var nargs=argTypes.length;if(!numericArgs){funcstr+="var stack = "+JSsource["stackSave"].body+";";for(var i=0;i<nargs;i++){var arg=argNames[i],type=argTypes[i];if(type==="number")continue;var convertCode=JSsource[type+"ToC"];funcstr+="var "+convertCode.arguments+" = "+arg+";";funcstr+=convertCode.body+";";funcstr+=arg+"="+convertCode.returnValue+";"}}var cfuncname=parseJSFunc((function(){return cfunc})).returnValue;funcstr+="var ret = "+cfuncname+"("+argNames.join(",")+");";if(!numericRet){var strgfy=parseJSFunc((function(){return Pointer_stringify})).returnValue;funcstr+="ret = "+strgfy+"(ret);"}if(!numericArgs){funcstr+=JSsource["stackRestore"].body.replace("()","(stack)")+";"}funcstr+="return ret})";return eval(funcstr)}}))();Module["cwrap"]=cwrap;Module["ccall"]=ccall;function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=+1?tempDouble>+0?(Math_min(+Math_floor(tempDouble/+4294967296),+4294967295)|0)>>>0:~~+Math_ceil((tempDouble- +(~~tempDouble>>>0))/+4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}Module["setValue"]=setValue;function getValue(ptr,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":return HEAP8[ptr>>0];case"i8":return HEAP8[ptr>>0];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP32[ptr>>2];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];default:abort("invalid type for setValue: "+type)}return null}Module["getValue"]=getValue;var ALLOC_NORMAL=0;var ALLOC_STACK=1;var ALLOC_STATIC=2;var ALLOC_DYNAMIC=3;var ALLOC_NONE=4;Module["ALLOC_NORMAL"]=ALLOC_NORMAL;Module["ALLOC_STACK"]=ALLOC_STACK;Module["ALLOC_STATIC"]=ALLOC_STATIC;Module["ALLOC_DYNAMIC"]=ALLOC_DYNAMIC;Module["ALLOC_NONE"]=ALLOC_NONE;function allocate(slab,types,allocator,ptr){var zeroinit,size;if(typeof slab==="number"){zeroinit=true;size=slab}else{zeroinit=false;size=slab.length}var singleType=typeof types==="string"?types:null;var ret;if(allocator==ALLOC_NONE){ret=ptr}else{ret=[_malloc,Runtime.stackAlloc,Runtime.staticAlloc,Runtime.dynamicAlloc][allocator===undefined?ALLOC_STATIC:allocator](Math.max(size,singleType?1:types.length))}if(zeroinit){var ptr=ret,stop;assert((ret&3)==0);stop=ret+(size&~3);for(;ptr<stop;ptr+=4){HEAP32[ptr>>2]=0}stop=ret+size;while(ptr<stop){HEAP8[ptr++>>0]=0}return ret}if(singleType==="i8"){if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var i=0,type,typeSize,previousType;while(i<size){var curr=slab[i];if(typeof curr==="function"){curr=Runtime.getFunctionIndex(curr)}type=singleType||types[i];if(type===0){i++;continue}if(type=="i64")type="i32";setValue(ret+i,curr,type);if(previousType!==type){typeSize=Runtime.getNativeTypeSize(type);previousType=type}i+=typeSize}return ret}Module["allocate"]=allocate;function Pointer_stringify(ptr,length){if(length===0||!ptr)return"";var hasUtf=false;var t;var i=0;while(1){t=HEAPU8[ptr+i>>0];if(t>=128)hasUtf=true;else if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(!hasUtf){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}var utf8=new Runtime.UTF8Processor;for(i=0;i<length;i++){t=HEAPU8[ptr+i>>0];ret+=utf8.processCChar(t)}return ret}Module["Pointer_stringify"]=Pointer_stringify;function UTF16ToString(ptr){var i=0;var str="";while(1){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)return str;++i;str+=String.fromCharCode(codeUnit)}}Module["UTF16ToString"]=UTF16ToString;function stringToUTF16(str,outPtr){for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr+i*2>>1]=codeUnit}HEAP16[outPtr+str.length*2>>1]=0}Module["stringToUTF16"]=stringToUTF16;function UTF32ToString(ptr){var i=0;var str="";while(1){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)return str;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}}Module["UTF32ToString"]=UTF32ToString;function stringToUTF32(str,outPtr){var iChar=0;for(var iCodeUnit=0;iCodeUnit<str.length;++iCodeUnit){var codeUnit=str.charCodeAt(iCodeUnit);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++iCodeUnit);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr+iChar*4>>2]=codeUnit;++iChar}HEAP32[outPtr+iChar*4>>2]=0}Module["stringToUTF32"]=stringToUTF32;function demangle(func){var hasLibcxxabi=!!Module["___cxa_demangle"];if(hasLibcxxabi){try{var buf=_malloc(func.length);writeStringToMemory(func.substr(1),buf);var status=_malloc(4);var ret=Module["___cxa_demangle"](buf,0,0,status);if(getValue(status,"i32")===0&&ret){return Pointer_stringify(ret)}}catch(e){}finally{if(buf)_free(buf);if(status)_free(status);if(ret)_free(ret)}}var i=3;var basicTypes={"v":"void","b":"bool","c":"char","s":"short","i":"int","l":"long","f":"float","d":"double","w":"wchar_t","a":"signed char","h":"unsigned char","t":"unsigned short","j":"unsigned int","m":"unsigned long","x":"long long","y":"unsigned long long","z":"..."};var subs=[];var first=true;function dump(x){if(x)Module.print(x);Module.print(func);var pre="";for(var a=0;a<i;a++)pre+=" ";Module.print(pre+"^")}function parseNested(){i++;if(func[i]==="K")i++;var parts=[];while(func[i]!=="E"){if(func[i]==="S"){i++;var next=func.indexOf("_",i);var num=func.substring(i,next)||0;parts.push(subs[num]||"?");i=next+1;continue}if(func[i]==="C"){parts.push(parts[parts.length-1]);i+=2;continue}var size=parseInt(func.substr(i));var pre=size.toString().length;if(!size||!pre){i--;break}var curr=func.substr(i+pre,size);parts.push(curr);subs.push(curr);i+=pre+size}i++;return parts}function parse(rawList,limit,allowVoid){limit=limit||Infinity;var ret="",list=[];function flushList(){return"("+list.join(", ")+")"}var name;if(func[i]==="N"){name=parseNested().join("::");limit--;if(limit===0)return rawList?[name]:name}else{if(func[i]==="K"||first&&func[i]==="L")i++;var size=parseInt(func.substr(i));if(size){var pre=size.toString().length;name=func.substr(i+pre,size);i+=pre+size}}first=false;if(func[i]==="I"){i++;var iList=parse(true);var iRet=parse(true,1,true);ret+=iRet[0]+" "+name+"<"+iList.join(", ")+">"}else{ret=name}paramLoop:while(i<func.length&&limit-->0){var c=func[i++];if(c in basicTypes){list.push(basicTypes[c])}else{switch(c){case"P":list.push(parse(true,1,true)[0]+"*");break;case"R":list.push(parse(true,1,true)[0]+"&");break;case"L":{i++;var end=func.indexOf("E",i);var size=end-i;list.push(func.substr(i,size));i+=size+2;break};case"A":{var size=parseInt(func.substr(i));i+=size.toString().length;if(func[i]!=="_")throw"?";i++;list.push(parse(true,1,true)[0]+" ["+size+"]");break};case"E":break paramLoop;default:ret+="?"+c;break paramLoop}}}if(!allowVoid&&list.length===1&&list[0]==="void")list=[];if(rawList){if(ret){list.push(ret+"?")}return list}else{return ret+flushList()}}var parsed=func;try{if(func=="Object._main"||func=="_main"){return"main()"}if(typeof func==="number")func=Pointer_stringify(func);if(func[0]!=="_")return func;if(func[1]!=="_")return func;if(func[2]!=="Z")return func;switch(func[3]){case"n":return"operator new()";case"d":return"operator delete()"}parsed=parse()}catch(e){parsed+="?"}if(parsed.indexOf("?")>=0&&!hasLibcxxabi){Runtime.warnOnce("warning: a problem occurred in builtin C++ name demangling; build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling")}return parsed}function demangleAll(text){return text.replace(/__Z[\w\d_]+/g,(function(x){var y=demangle(x);return x===y?x:x+" ["+y+"]"}))}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error(0)}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){return demangleAll(jsStackTrace())}Module["stackTrace"]=stackTrace;var PAGE_SIZE=4096;function alignMemoryPage(x){return x+4095&-4096}var HEAP;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var STATIC_BASE=0,STATICTOP=0,staticSealed=false;var STACK_BASE=0,STACKTOP=0,STACK_MAX=0;var DYNAMIC_BASE=0,DYNAMICTOP=0;function enlargeMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.")}var TOTAL_STACK=Module["TOTAL_STACK"]||5242880;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||67108864;var FAST_MEMORY=Module["FAST_MEMORY"]||2097152;var totalMemory=64*1024;while(totalMemory<TOTAL_MEMORY||totalMemory<2*TOTAL_STACK){if(totalMemory<16*1024*1024){totalMemory*=2}else{totalMemory+=16*1024*1024}}if(totalMemory!==TOTAL_MEMORY){Module.printErr("increasing TOTAL_MEMORY to "+totalMemory+" to be compliant with the asm.js spec");TOTAL_MEMORY=totalMemory}assert(typeof Int32Array!=="undefined"&&typeof Float64Array!=="undefined"&&!!(new Int32Array(1))["subarray"]&&!!(new Int32Array(1))["set"],"JS engine does not provide full typed array support");var buffer=new ArrayBuffer(TOTAL_MEMORY);HEAP8=new Int8Array(buffer);HEAP16=new Int16Array(buffer);HEAP32=new Int32Array(buffer);HEAPU8=new Uint8Array(buffer);HEAPU16=new Uint16Array(buffer);HEAPU32=new Uint32Array(buffer);HEAPF32=new Float32Array(buffer);HEAPF64=new Float64Array(buffer);HEAP32[0]=255;assert(HEAPU8[0]===255&&HEAPU8[3]===0,"Typed arrays 2 must be run on a little-endian system");Module["HEAP"]=HEAP;Module["buffer"]=buffer;Module["HEAP8"]=HEAP8;Module["HEAP16"]=HEAP16;Module["HEAP32"]=HEAP32;Module["HEAPU8"]=HEAPU8;Module["HEAPU16"]=HEAPU16;Module["HEAPU32"]=HEAPU32;Module["HEAPF32"]=HEAPF32;Module["HEAPF64"]=HEAPF64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Runtime.dynCall("v",func)}else{Runtime.dynCall("vi",func,[callback.arg])}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}Module["addOnPreRun"]=Module.addOnPreRun=addOnPreRun;function addOnInit(cb){__ATINIT__.unshift(cb)}Module["addOnInit"]=Module.addOnInit=addOnInit;function addOnPreMain(cb){__ATMAIN__.unshift(cb)}Module["addOnPreMain"]=Module.addOnPreMain=addOnPreMain;function addOnExit(cb){__ATEXIT__.unshift(cb)}Module["addOnExit"]=Module.addOnExit=addOnExit;function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}Module["addOnPostRun"]=Module.addOnPostRun=addOnPostRun;function intArrayFromString(stringy,dontAddNull,length){var ret=(new Runtime.UTF8Processor).processJSString(stringy);if(length){ret.length=length}if(!dontAddNull){ret.push(0)}return ret}Module["intArrayFromString"]=intArrayFromString;function intArrayToString(array){var ret=[];for(var i=0;i<array.length;i++){var chr=array[i];if(chr>255){chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}Module["intArrayToString"]=intArrayToString;function writeStringToMemory(string,buffer,dontAddNull){var array=intArrayFromString(string,dontAddNull);var i=0;while(i<array.length){var chr=array[i];HEAP8[buffer+i>>0]=chr;i=i+1}}Module["writeStringToMemory"]=writeStringToMemory;function writeArrayToMemory(array,buffer){for(var i=0;i<array.length;i++){HEAP8[buffer+i>>0]=array[i]}}Module["writeArrayToMemory"]=writeArrayToMemory;function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i<str.length;i++){HEAP8[buffer+i>>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer+str.length>>0]=0}Module["writeAsciiToMemory"]=writeAsciiToMemory;function unSign(value,bits,ignore){if(value>=0){return value}return bits<=32?2*Math.abs(1<<bits-1)+value:Math.pow(2,bits)+value}function reSign(value,bits,ignore){if(value<=0){return value}var half=bits<=32?Math.abs(1<<bits-1):Math.pow(2,bits-1);if(value>=half&&(bits<=32||value>half)){value=-2*half+value}return value}if(!Math["imul"]||Math["imul"](4294967295,5)!==-5)Math["imul"]=function imul(a,b){var ah=a>>>16;var al=a&65535;var bh=b>>>16;var bl=b&65535;return al*bl+(ah*bl+al*bh<<16)|0};Math.imul=Math["imul"];var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_min=Math.min;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}Module["addRunDependency"]=addRunDependency;function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["removeRunDependency"]=removeRunDependency;Module["preloadedImages"]={};Module["preloadedAudios"]={};var memoryInitializer=null;STATIC_BASE=8;STATICTOP=STATIC_BASE+5664;__ATINIT__.push({func:(function(){__GLOBAL__I_a()})},{func:(function(){__GLOBAL__I_a127()})});allocate([78,55,77,105,110,105,115,97,116,50,48,79,117,116,79,102,77,101,109,111,114,121,69,120,99,101,112,116,105,111,110,69,0,0,0,0,0,0,0,0,88,18,0,0,8,0,0,0,78,55,77,105,110,105,115,97,116,54,79,112,116,105,111,110,69,0,0,0,0,0,0,0,88,18,0,0,56,0,0,0,10,32,32,32,32,32,32,32,32,37,115,10,0,0,0,0,0,0,0,0,80,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,1,0,0,0,78,55,77,105,110,105,115,97,116,49,48,66,111,111,108,79,112,116,105,111,110,69,0,0,128,18,0,0,176,0,0,0,80,0,0,0,0,0,0,0,32,32,45,37,115,44,32,45,110,111,45,37,115,0,0,0,40,100,101,102,97,117,108,116,58,32,37,115,41,10,0,0,111,110,0,0,0,0,0,0,111,102,102,0,0,0,0,0,110,111,45,0,0,0,0,0,0,0,0,0,64,1,0,0,1,0,0,0,4,0,0,0,2,0,0,0,2,0,0,0,78,55,77,105,110,105,115,97,116,57,73,110,116,79,112,116,105,111,110,69,0,0,0,0,128,18,0,0,40,1,0,0,80,0,0,0,0,0,0,0,32,32,45,37,45,49,50,115,32,61,32,37,45,56,115,32,91,0,0,0,0,0,0,0,105,109,105,110,0,0,0,0,37,52,100,0,0,0,0,0,32,46,46,32,0,0,0,0,105,109,97,120,0,0,0,0,93,32,40,100,101,102,97,117,108,116,58,32,37,100,41,10,0,0,0,0,0,0,0,0,69,82,82,79,82,33,32,118,97,108,117,101,32,60,37,115,62,32,105,115,32,116,111,111,32,108,97,114,103,101,32,102,111,114,32,111,112,116,105,111,110,32,34,37,115,34,46,10,0,0,0,0,0,0,0,0,69,82,82,79,82,33,32,118,97,108,117,101,32,60,37,115,62,32,105,115,32,116,111,111,32,115,109,97,108,108,32,102,111,114,32,111,112,116,105,111,110,32,34,37,115,34,46,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,118,97,114,45,100,101,99,97,121,0,0,0,0,0,0,0,84,104,101,32,118,97,114,105,97,98,108,101,32,97,99,116,105,118,105,116,121,32,100,101,99,97,121,32,102,97,99,116,111,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,108,97,45,100,101,99,97,121,0,0,0,0,0,0,0,84,104,101,32,99,108,97,117,115,101,32,97,99,116,105,118,105,116,121,32,100,101,99,97,121,32,102,97,99,116,111,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,110,100,45,102,114,101,113,0,0,0,0,0,0,0,0,84,104,101,32,102,114,101,113,117,101,110,99,121,32,119,105,116,104,32,119,104,105,99,104,32,116,104,101,32,100,101,99,105,115,105,111,110,32,104,101,117,114,105,115,116,105,99,32,116,114,105,101,115,32,116,111,32,99,104,111,111,115,101,32,97,32,114,97,110,100,111,109,32,118,97,114,105,97,98,108,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,110,100,45,115,101,101,100,0,0,0,0,0,0,0,0,85,115,101,100,32,98,121,32,116,104,101,32,114,97,110,100,111,109,32,118,97,114,105,97,98,108,101,32,115,101,108,101,99,116,105,111,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,99,109,105,110,45,109,111,100,101,0,0,0,0,0,0,67,111,110,116,114,111,108,115,32,99,111,110,102,108,105,99,116,32,99,108,97,117,115,101,32,109,105,110,105,109,105,122,97,116,105,111,110,32,40,48,61,110,111,110,101,44,32,49,61,98,97,115,105,99,44,32,50,61,100,101,101,112,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,104,97,115,101,45,115,97,118,105,110,103,0,0,0,0,67,111,110,116,114,111,108,115,32,116,104,101,32,108,101,118,101,108,32,111,102,32,112,104,97,115,101,32,115,97,118,105,110,103,32,40,48,61,110,111,110,101,44,32,49,61,108,105,109,105,116,101,100,44,32,50,61,102,117,108,108,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,110,100,45,105,110,105,116,0,0,0,0,0,0,0,0,82,97,110,100,111,109,105,122,101,32,116,104,101,32,105,110,105,116,105,97,108,32,97,99,116,105,118,105,116,121,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,117,98,121,0,0,0,0,85,115,101,32,116,104,101,32,76,117,98,121,32,114,101,115,116,97,114,116,32,115,101,113,117,101,110,99,101,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,102,105,114,115,116,0,0,84,104,101,32,98,97,115,101,32,114,101,115,116,97,114,116,32,105,110,116,101,114,118,97,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,105,110,99,0,0,0,0,82,101,115,116,97,114,116,32,105,110,116,101,114,118,97,108,32,105,110,99,114,101,97,115,101,32,102,97,99,116,111,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,103,99,45,102,114,97,99,0,84,104,101,32,102,114,97,99,116,105,111,110,32,111,102,32,119,97,115,116,101,100,32,109,101,109,111,114,121,32,97,108,108,111,119,101,100,32,98,101,102,111,114,101,32,97,32,103,97,114,98,97,103,101,32,99,111,108,108,101,99,116,105,111,110,32,105,115,32,116,114,105,103,103,101,114,101,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,105,110,45,108,101,97,114,110,116,115,0,0,0,0,0,77,105,110,105,109,117,109,32,108,101,97,114,110,116,32,99,108,97,117,115,101,32,108,105,109,105,116,0,0,0,0,0,0,0,0,0,192,7,0,0,5,0,0,0,6,0,0,0,7,0,0,0,0,0,0,0,124,32,37,57,100,32,124,32,37,55,100,32,37,56,100,32,37,56,100,32,124,32,37,56,100,32,37,56,100,32,37,54,46,48,102,32,124,32,37,54,46,51,102,32,37,37,32,124,10,0,0,0,0,0,0,0,124,32,32,71,97,114,98,97,103,101,32,99,111,108,108,101,99,116,105,111,110,58,32,32,32,37,49,50,100,32,98,121,116,101,115,32,61,62,32,37,49,50,100,32,98,121,116,101,115,32,32,32,32,32,32,32,32,32,32,32,32,32,124,10,0,0,0,0,0,0,0,0,78,55,77,105,110,105,115,97,116,54,83,111,108,118,101,114,69,0,0,0,0,0,0,0,88,18,0,0,168,7,0,0,60,98,111,111,108,62,0,0,10,32,32,32,32,32,32,32,32,37,115,10,0,0,0,0,60,105,110,116,51,50,62,0,69,82,82,79,82,33,32,118,97,108,117,101,32,60,37,115,62,32,105,115,32,116,111,111,32,108,97,114,103,101,32,102,111,114,32,111,112,116,105,111,110,32,34,37,115,34,46,10,0,0,0,0,0,0,0,0,69,82,82,79,82,33,32,118,97,108,117,101,32,60,37,115,62,32,105,115,32,116,111,111,32,115,109,97,108,108,32,102,111,114,32,111,112,116,105,111,110,32,34,37,115,34,46,10,0,0,0,0,0,0,0,0,67,79,82,69,0,0,0,0,60,100,111,117,98,108,101,62,0,0,0,0,0,0,0,0,0,0,0,0,168,8,0,0,1,0,0,0,8,0,0,0,3,0,0,0,3,0,0,0,78,55,77,105,110,105,115,97,116,49,50,68,111,117,98,108,101,79,112,116,105,111,110,69,0,0,0,0,0,0,0,0,128,18,0,0,136,8,0,0,80,0,0,0,0,0,0,0,32,32,45,37,45,49,50,115,32,61,32,37,45,56,115,32,37,99,37,52,46,50,103,32,46,46,32,37,52,46,50,103,37,99,32,40,100,101,102,97,117,108,116,58,32,37,103,41,10,0,0,0,0,0,0,0,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,91,32,83,101,97,114,99,104,32,83,116,97,116,105,115,116,105,99,115,32,93,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,0,124,32,67,111,110,102,108,105,99,116,115,32,124,32,32,32,32,32,32,32,32,32,32,79,82,73,71,73,78,65,76,32,32,32,32,32,32,32,32,32,124,32,32,32,32,32,32,32,32,32,32,76,69,65,82,78,84,32,32,32,32,32,32,32,32,32,32,124,32,80,114,111,103,114,101,115,115,32,124,0,124,32,32,32,32,32,32,32,32,32,32,32,124,32,32,32,32,86,97,114,115,32,32,67,108,97,117,115,101,115,32,76,105,116,101,114,97,108,115,32,124,32,32,32,32,76,105,109,105,116,32,32,67,108,97,117,115,101,115,32,76,105,116,47,67,108,32,124,32,32,32,32,32,32,32,32,32,32,124,0,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,97,115,121,109,109,0,0,0,83,104,114,105,110,107,32,99,108,97,117,115,101,115,32,98,121,32,97,115,121,109,109,101,116,114,105,99,32,98,114,97,110,99,104,105,110,103,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,99,104,101,99,107,0,0,67,104,101,99,107,32,105,102,32,97,32,99,108,97,117,115,101,32,105,115,32,97,108,114,101,97,100,121,32,105,109,112,108,105,101,100,46,32,40,99,111,115,116,108,121,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,101,108,105,109,0,0,0,0,80,101,114,102,111,114,109,32,118,97,114,105,97,98,108,101,32,101,108,105,109,105,110,97,116,105,111,110,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,103,114,111,119,0,0,0,0,65,108,108,111,119,32,97,32,118,97,114,105,97,98,108,101,32,101,108,105,109,105,110,97,116,105,111,110,32,115,116,101,112,32,116,111,32,103,114,111,119,32,98,121,32,97,32,110,117,109,98,101,114,32,111,102,32,99,108,97,117,115,101,115,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,108,45,108,105,109,0,0,86,97,114,105,97,98,108,101,115,32,97,114,101,32,110,111,116,32,101,108,105,109,105,110,97,116,101,100,32,105,102,32,105,116,32,112,114,111,100,117,99,101,115,32,97,32,114,101,115,111,108,118,101,110,116,32,119,105,116,104,32,97,32,108,101,110,103,116,104,32,97,98,111,118,101,32,116,104,105,115,32,108,105,109,105,116,46,32,45,49,32,109,101,97,110,115,32,110,111,32,108,105,109,105,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,117,98,45,108,105,109,0,68,111,32,110,111,116,32,99,104,101,99,107,32,105,102,32,115,117,98,115,117,109,112,116,105,111,110,32,97,103,97,105,110,115,116,32,97,32,99,108,97,117,115,101,32,108,97,114,103,101,114,32,116,104,97,110,32,116,104,105,115,46,32,45,49,32,109,101,97,110,115,32,110,111,32,108,105,109,105,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,105,109,112,45,103,99,45,102,114,97,99,0,0,0,0,84,104,101,32,102,114,97,99,116,105,111,110,32,111,102,32,119,97,115,116,101,100,32,109,101,109,111,114,121,32,97,108,108,111,119,101,100,32,98,101,102,111,114,101,32,97,32,103,97,114,98,97,103,101,32,99,111,108,108,101,99,116,105,111,110,32,105,115,32,116,114,105,103,103,101,114,101,100,32,100,117,114,105,110,103,32,115,105,109,112,108,105,102,105,99,97,116,105,111,110,46,0,0,0,0,0,0,0,120,14,0,0,9,0,0,0,10,0,0,0,11,0,0,0,0,0,0,0,115,117,98,115,117,109,112,116,105,111,110,32,108,101,102,116,58,32,37,49,48,100,32,40,37,49,48,100,32,115,117,98,115,117,109,101,100,44,32,37,49,48,100,32,100,101,108,101,116,101,100,32,108,105,116,101,114,97,108,115,41,13,0,0,101,108,105,109,105,110,97,116,105,111,110,32,108,101,102,116,58,32,37,49,48,100,13,0,124,32,32,69,108,105,109,105,110,97,116,101,100,32,99,108,97,117,115,101,115,58,32,32,32,32,32,37,49,48,46,50,102,32,77,98,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,124,10,0,0,0,0,124,32,32,71,97,114,98,97,103,101,32,99,111,108,108,101,99,116,105,111,110,58,32,32,32,37,49,50,100,32,98,121,116,101,115,32,61,62,32,37,49,50,100,32,98,121,116,101,115,32,32,32,32,32,32,32,32,32,32,32,32,32,124,10,0,0,0,0,0,0,0,0,78,55,77,105,110,105,115,97,116,49,48,83,105,109,112,83,111,108,118,101,114,69,0,0,128,18,0,0,96,14,0,0,192,7,0,0,0,0,0,0,60,100,111,117,98,108,101,62,0,0,0,0,0,0,0,0,60,105,110,116,51,50,62,0,83,73,77,80,0,0,0,0,60,98,111,111,108,62,0,0,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,79,33,0,0,0,0,0,2,0,0,0,0,0,0,0,48,15,0,0,0,0,0,0,117,110,99,97,117,103,104,116,0,0,0,0,0,0,0,0,116,101,114,109,105,110,97,116,105,110,103,32,119,105,116,104,32,37,115,32,101,120,99,101,112,116,105,111,110,32,111,102,32,116,121,112,101,32,37,115,58,32,37,115,0,0,0,0,116,101,114,109,105,110,97,116,105,110,103,32,119,105,116,104,32,37,115,32,101,120,99,101,112,116,105,111,110,32,111,102,32,116,121,112,101,32,37,115,0,0,0,0,0,0,0,0,116,101,114,109,105,110,97,116,105,110,103,32,119,105,116,104,32,37,115,32,102,111,114,101,105,103,110,32,101,120,99,101,112,116,105,111,110,0,0,0,116,101,114,109,105,110,97,116,105,110,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,116,104,114,101,97,100,95,111,110,99,101,32,102,97,105,108,117,114,101,32,105,110,32,95,95,99,120,97,95,103,101,116,95,103,108,111,98,97,108,115,95,102,97,115,116,40,41,0,0,0,0,0,0,0,0,99,97,110,110,111,116,32,99,114,101,97,116,101,32,112,116,104,114,101,97,100,32,107,101,121,32,102,111,114,32,95,95,99,120,97,95,103,101,116,95,103,108,111,98,97,108,115,40,41,0,0,0,0,0,0,0,99,97,110,110,111,116,32,122,101,114,111,32,111,117,116,32,116,104,114,101,97,100,32,118,97,108,117,101,32,102,111,114,32,95,95,99,120,97,95,103,101,116,95,103,108,111,98,97,108,115,40,41,0,0,0,0,0,0,0,0,200,16,0,0,12,0,0,0,13,0,0,0,1,0,0,0,0,0,0,0,115,116,100,58,58,98,97,100,95,97,108,108,111,99,0,0,83,116,57,98,97,100,95,97,108,108,111,99,0,0,0,0,128,18,0,0,184,16,0,0,80,17,0,0,0,0,0,0,116,101,114,109,105,110,97,116,101,95,104,97,110,100,108,101,114,32,117,110,101,120,112,101,99,116,101,100,108,121,32,114,101,116,117,114,110,101,100,0,116,101,114,109,105,110,97,116,101,95,104,97,110,100,108,101,114,32,117,110,101,120,112,101,99,116,101,100,108,121,32,116,104,114,101,119,32,97,110,32,101,120,99,101,112,116,105,111,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,83,116,57,101,120,99,101,112,116,105,111,110,0,0,0,0,88,18,0,0,64,17,0,0,83,116,57,116,121,112,101,95,105,110,102,111,0,0,0,0,88,18,0,0,88,17,0,0,78,49,48,95,95,99,120,120,97,98,105,118,49,49,54,95,95,115,104,105,109,95,116,121,112,101,95,105,110,102,111,69,0,0,0,0,0,0,0,0,128,18,0,0,112,17,0,0,104,17,0,0,0,0,0,0,78,49,48,95,95,99,120,120,97,98,105,118,49,49,55,95,95,99,108,97,115,115,95,116,121,112,101,95,105,110,102,111,69,0,0,0,0,0,0,0,128,18,0,0,168,17,0,0,152,17,0,0,0,0,0,0,78,49,48,95,95,99,120,120,97,98,105,118,49,49,57,95,95,112,111,105,110,116,101,114,95,116,121,112,101,95,105,110,102,111,69,0,0,0,0,0,78,49,48,95,95,99,120,120,97,98,105,118,49,49,55,95,95,112,98,97,115,101,95,116,121,112,101,95,105,110,102,111,69,0,0,0,0,0,0,0,128,18,0,0,8,18,0,0,152,17,0,0,0,0,0,0,128,18,0,0,224,17,0,0,48,18,0,0,0,0,0,0,0,0,0,0,208,17,0,0,14,0,0,0,15,0,0,0,16,0,0,0,17,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,200,18,0,0,14,0,0,0,18,0,0,0,16,0,0,0,17,0,0,0,1,0,0,0,2,0,0,0,2,0,0,0,2,0,0,0,78,49,48,95,95,99,120,120,97,98,105,118,49,50,48,95,95,115,105,95,99,108,97,115,115,95,116,121,112,101,95,105,110,102,111,69,0,0,0,0,128,18,0,0,160,18,0,0,208,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,1,2,3,4,5,6,7,8,9,255,255,255,255,255,255,255,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,255,255,255,255,255,255,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,1,2,4,7,3,6,5,0,0,0,0,0,0,0,0,105,110,102,105,110,105,116,121,0,0,0,0,0,0,0,0,110,97,110,0,0,0,0,0,95,112,137,0,255,9,47,15,10,0,0,0,100,0,0,0,232,3,0,0,16,39,0,0,160,134,1,0,64,66,15,0,128,150,152,0,0,225,245,5],"i8",ALLOC_NONE,Runtime.GLOBAL_BASE);var tempDoublePtr=Runtime.alignMemory(allocate(12,"i8",ALLOC_STATIC),8);assert(tempDoublePtr%8==0);function copyTempFloat(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3]}function copyTempDouble(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3];HEAP8[tempDoublePtr+4]=HEAP8[ptr+4];HEAP8[tempDoublePtr+5]=HEAP8[ptr+5];HEAP8[tempDoublePtr+6]=HEAP8[ptr+6];HEAP8[tempDoublePtr+7]=HEAP8[ptr+7]}function _atexit(func,arg){__ATEXIT__.unshift({func:func,arg:arg})}function ___cxa_atexit(){return _atexit.apply(null,arguments)}Module["_i64Subtract"]=_i64Subtract;var ___errno_state=0;function ___setErrNo(value){HEAP32[___errno_state>>2]=value;return value}var ERRNO_CODES={EPERM:1,ENOENT:2,ESRCH:3,EINTR:4,EIO:5,ENXIO:6,E2BIG:7,ENOEXEC:8,EBADF:9,ECHILD:10,EAGAIN:11,EWOULDBLOCK:11,ENOMEM:12,EACCES:13,EFAULT:14,ENOTBLK:15,EBUSY:16,EEXIST:17,EXDEV:18,ENODEV:19,ENOTDIR:20,EISDIR:21,EINVAL:22,ENFILE:23,EMFILE:24,ENOTTY:25,ETXTBSY:26,EFBIG:27,ENOSPC:28,ESPIPE:29,EROFS:30,EMLINK:31,EPIPE:32,EDOM:33,ERANGE:34,ENOMSG:42,EIDRM:43,ECHRNG:44,EL2NSYNC:45,EL3HLT:46,EL3RST:47,ELNRNG:48,EUNATCH:49,ENOCSI:50,EL2HLT:51,EDEADLK:35,ENOLCK:37,EBADE:52,EBADR:53,EXFULL:54,ENOANO:55,EBADRQC:56,EBADSLT:57,EDEADLOCK:35,EBFONT:59,ENOSTR:60,ENODATA:61,ETIME:62,ENOSR:63,ENONET:64,ENOPKG:65,EREMOTE:66,ENOLINK:67,EADV:68,ESRMNT:69,ECOMM:70,EPROTO:71,EMULTIHOP:72,EDOTDOT:73,EBADMSG:74,ENOTUNIQ:76,EBADFD:77,EREMCHG:78,ELIBACC:79,ELIBBAD:80,ELIBSCN:81,ELIBMAX:82,ELIBEXEC:83,ENOSYS:38,ENOTEMPTY:39,ENAMETOOLONG:36,ELOOP:40,EOPNOTSUPP:95,EPFNOSUPPORT:96,ECONNRESET:104,ENOBUFS:105,EAFNOSUPPORT:97,EPROTOTYPE:91,ENOTSOCK:88,ENOPROTOOPT:92,ESHUTDOWN:108,ECONNREFUSED:111,EADDRINUSE:98,ECONNABORTED:103,ENETUNREACH:101,ENETDOWN:100,ETIMEDOUT:110,EHOSTDOWN:112,EHOSTUNREACH:113,EINPROGRESS:115,EALREADY:114,EDESTADDRREQ:89,EMSGSIZE:90,EPROTONOSUPPORT:93,ESOCKTNOSUPPORT:94,EADDRNOTAVAIL:99,ENETRESET:102,EISCONN:106,ENOTCONN:107,ETOOMANYREFS:109,EUSERS:87,EDQUOT:122,ESTALE:116,ENOTSUP:95,ENOMEDIUM:123,EILSEQ:84,EOVERFLOW:75,ECANCELED:125,ENOTRECOVERABLE:131,EOWNERDEAD:130,ESTRPIPE:86};function _sysconf(name){switch(name){case 30:return PAGE_SIZE;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 79:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}___setErrNo(ERRNO_CODES.EINVAL);return-1}function __ZSt18uncaught_exceptionv(){return!!__ZSt18uncaught_exceptionv.uncaught_exception}var EXCEPTIONS={last:0,caught:[],infos:{},deAdjust:(function(adjusted){if(!adjusted||EXCEPTIONS.infos[adjusted])return adjusted;for(var ptr in EXCEPTIONS.infos){var info=EXCEPTIONS.infos[ptr];if(info.adjusted===adjusted){return ptr}}return adjusted}),addRef:(function(ptr){if(!ptr)return;var info=EXCEPTIONS.infos[ptr];info.refcount++}),decRef:(function(ptr){if(!ptr)return;var info=EXCEPTIONS.infos[ptr];assert(info.refcount>0);info.refcount--;if(info.refcount===0){if(info.destructor){Runtime.dynCall("vi",info.destructor,[ptr])}delete EXCEPTIONS.infos[ptr];___cxa_free_exception(ptr)}}),clearRef:(function(ptr){if(!ptr)return;var info=EXCEPTIONS.infos[ptr];info.refcount=0})};function ___resumeException(ptr){if(!EXCEPTIONS.last){EXCEPTIONS.last=ptr}EXCEPTIONS.clearRef(EXCEPTIONS.deAdjust(ptr));throw ptr+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch."}function ___cxa_find_matching_catch(){var thrown=EXCEPTIONS.last;if(!thrown){return(asm["setTempRet0"](0),0)|0}var info=EXCEPTIONS.infos[thrown];var throwntype=info.type;if(!throwntype){return(asm["setTempRet0"](0),thrown)|0}var typeArray=Array.prototype.slice.call(arguments);var pointer=Module["___cxa_is_pointer_type"](throwntype);if(!___cxa_find_matching_catch.buffer)___cxa_find_matching_catch.buffer=_malloc(4);HEAP32[___cxa_find_matching_catch.buffer>>2]=thrown;thrown=___cxa_find_matching_catch.buffer;for(var i=0;i<typeArray.length;i++){if(typeArray[i]&&Module["___cxa_can_catch"](typeArray[i],throwntype,thrown)){thrown=HEAP32[thrown>>2];info.adjusted=thrown;return(asm["setTempRet0"](typeArray[i]),thrown)|0}}thrown=HEAP32[thrown>>2];return(asm["setTempRet0"](throwntype),thrown)|0}function ___cxa_throw(ptr,type,destructor){EXCEPTIONS.infos[ptr]={ptr:ptr,adjusted:ptr,type:type,destructor:destructor,refcount:0};EXCEPTIONS.last=ptr;if(!("uncaught_exception"in __ZSt18uncaught_exceptionv)){__ZSt18uncaught_exceptionv.uncaught_exception=1}else{__ZSt18uncaught_exceptionv.uncaught_exception++}throw ptr+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch."}Module["_memset"]=_memset;Module["_bitshift64Shl"]=_bitshift64Shl;function _abort(){Module["abort"]()}var FS=undefined;var SOCKFS=undefined;function _send(fd,buf,len,flags){var sock=SOCKFS.getSocket(fd);if(!sock){___setErrNo(ERRNO_CODES.EBADF);return-1}return _write(fd,buf,len)}function _pwrite(fildes,buf,nbyte,offset){var stream=FS.getStream(fildes);if(!stream){___setErrNo(ERRNO_CODES.EBADF);return-1}try{var slab=HEAP8;return FS.write(stream,slab,buf,nbyte,offset)}catch(e){FS.handleFSError(e);return-1}}function _write(fildes,buf,nbyte){var stream=FS.getStream(fildes);if(!stream){___setErrNo(ERRNO_CODES.EBADF);return-1}try{var slab=HEAP8;return FS.write(stream,slab,buf,nbyte)}catch(e){FS.handleFSError(e);return-1}}function _fileno(stream){stream=FS.getStreamFromPtr(stream);if(!stream)return-1;return stream.fd}function _fwrite(ptr,size,nitems,stream){var bytesToWrite=nitems*size;if(bytesToWrite==0)return 0;var fd=_fileno(stream);var bytesWritten=_write(fd,ptr,bytesToWrite);if(bytesWritten==-1){var streamObj=FS.getStreamFromPtr(stream);if(streamObj)streamObj.error=true;return 0}else{return bytesWritten/size|0}}Module["_strlen"]=_strlen;function __reallyNegative(x){return x<0||x===0&&1/x===-Infinity}function __formatString(format,varargs){var textIndex=format;var argIndex=0;function getNextArg(type){var ret;if(type==="double"){ret=(HEAP32[tempDoublePtr>>2]=HEAP32[varargs+argIndex>>2],HEAP32[tempDoublePtr+4>>2]=HEAP32[varargs+(argIndex+4)>>2],+HEAPF64[tempDoublePtr>>3])}else if(type=="i64"){ret=[HEAP32[varargs+argIndex>>2],HEAP32[varargs+(argIndex+4)>>2]]}else{type="i32";ret=HEAP32[varargs+argIndex>>2]}argIndex+=Runtime.getNativeFieldSize(type);return ret}var ret=[];var curr,next,currArg;while(1){var startTextIndex=textIndex;curr=HEAP8[textIndex>>0];if(curr===0)break;next=HEAP8[textIndex+1>>0];if(curr==37){var flagAlwaysSigned=false;var flagLeftAlign=false;var flagAlternative=false;var flagZeroPad=false;var flagPadSign=false;flagsLoop:while(1){switch(next){case 43:flagAlwaysSigned=true;break;case 45:flagLeftAlign=true;break;case 35:flagAlternative=true;break;case 48:if(flagZeroPad){break flagsLoop}else{flagZeroPad=true;break};case 32:flagPadSign=true;break;default:break flagsLoop}textIndex++;next=HEAP8[textIndex+1>>0]}var width=0;if(next==42){width=getNextArg("i32");textIndex++;next=HEAP8[textIndex+1>>0]}else{while(next>=48&&next<=57){width=width*10+(next-48);textIndex++;next=HEAP8[textIndex+1>>0]}}var precisionSet=false,precision=-1;if(next==46){precision=0;precisionSet=true;textIndex++;next=HEAP8[textIndex+1>>0];if(next==42){precision=getNextArg("i32");textIndex++}else{while(1){var precisionChr=HEAP8[textIndex+1>>0];if(precisionChr<48||precisionChr>57)break;precision=precision*10+(precisionChr-48);textIndex++}}next=HEAP8[textIndex+1>>0]}if(precision<0){precision=6;precisionSet=false}var argSize;switch(String.fromCharCode(next)){case"h":var nextNext=HEAP8[textIndex+2>>0];if(nextNext==104){textIndex++;argSize=1}else{argSize=2}break;case"l":var nextNext=HEAP8[textIndex+2>>0];if(nextNext==108){textIndex++;argSize=8}else{argSize=4}break;case"L":case"q":case"j":argSize=8;break;case"z":case"t":case"I":argSize=4;break;default:argSize=null}if(argSize)textIndex++;next=HEAP8[textIndex+1>>0];switch(String.fromCharCode(next)){case"d":case"i":case"u":case"o":case"x":case"X":case"p":{var signed=next==100||next==105;argSize=argSize||4;var currArg=getNextArg("i"+argSize*8);var origArg=currArg;var argText;if(argSize==8){currArg=Runtime.makeBigInt(currArg[0],currArg[1],next==117)}if(argSize<=4){var limit=Math.pow(256,argSize)-1;currArg=(signed?reSign:unSign)(currArg&limit,argSize*8)}var currAbsArg=Math.abs(currArg);var prefix="";if(next==100||next==105){if(argSize==8&&i64Math)argText=i64Math.stringify(origArg[0],origArg[1],null);else argText=reSign(currArg,8*argSize,1).toString(10)}else if(next==117){if(argSize==8&&i64Math)argText=i64Math.stringify(origArg[0],origArg[1],true);else argText=unSign(currArg,8*argSize,1).toString(10);currArg=Math.abs(currArg)}else if(next==111){argText=(flagAlternative?"0":"")+currAbsArg.toString(8)}else if(next==120||next==88){prefix=flagAlternative&&currArg!=0?"0x":"";if(argSize==8&&i64Math){if(origArg[1]){argText=(origArg[1]>>>0).toString(16);var lower=(origArg[0]>>>0).toString(16);while(lower.length<8)lower="0"+lower;argText+=lower}else{argText=(origArg[0]>>>0).toString(16)}}else if(currArg<0){currArg=-currArg;argText=(currAbsArg-1).toString(16);var buffer=[];for(var i=0;i<argText.length;i++){buffer.push((15-parseInt(argText[i],16)).toString(16))}argText=buffer.join("");while(argText.length<argSize*2)argText="f"+argText}else{argText=currAbsArg.toString(16)}if(next==88){prefix=prefix.toUpperCase();argText=argText.toUpperCase()}}else if(next==112){if(currAbsArg===0){argText="(nil)"}else{prefix="0x";argText=currAbsArg.toString(16)}}if(precisionSet){while(argText.length<precision){argText="0"+argText}}if(currArg>=0){if(flagAlwaysSigned){prefix="+"+prefix}else if(flagPadSign){prefix=" "+prefix}}if(argText.charAt(0)=="-"){prefix="-"+prefix;argText=argText.substr(1)}while(prefix.length+argText.length<width){if(flagLeftAlign){argText+=" "}else{if(flagZeroPad){argText="0"+argText}else{prefix=" "+prefix}}}argText=prefix+argText;argText.split("").forEach((function(chr){ret.push(chr.charCodeAt(0))}));break};case"f":case"F":case"e":case"E":case"g":case"G":{var currArg=getNextArg("double");var argText;if(isNaN(currArg)){argText="nan";flagZeroPad=false}else if(!isFinite(currArg)){argText=(currArg<0?"-":"")+"inf";flagZeroPad=false}else{var isGeneral=false;var effectivePrecision=Math.min(precision,20);if(next==103||next==71){isGeneral=true;precision=precision||1;var exponent=parseInt(currArg.toExponential(effectivePrecision).split("e")[1],10);if(precision>exponent&&exponent>=-4){next=(next==103?"f":"F").charCodeAt(0);precision-=exponent+1}else{next=(next==103?"e":"E").charCodeAt(0);precision--}effectivePrecision=Math.min(precision,20)}if(next==101||next==69){argText=currArg.toExponential(effectivePrecision);if(/[eE][-+]\d$/.test(argText)){argText=argText.slice(0,-1)+"0"+argText.slice(-1)}}else if(next==102||next==70){argText=currArg.toFixed(effectivePrecision);if(currArg===0&&__reallyNegative(currArg)){argText="-"+argText}}var parts=argText.split("e");if(isGeneral&&!flagAlternative){while(parts[0].length>1&&parts[0].indexOf(".")!=-1&&(parts[0].slice(-1)=="0"||parts[0].slice(-1)==".")){parts[0]=parts[0].slice(0,-1)}}else{if(flagAlternative&&argText.indexOf(".")==-1)parts[0]+=".";while(precision>effectivePrecision++)parts[0]+="0"}argText=parts[0]+(parts.length>1?"e"+parts[1]:"");if(next==69)argText=argText.toUpperCase();if(currArg>=0){if(flagAlwaysSigned){argText="+"+argText}else if(flagPadSign){argText=" "+argText}}}while(argText.length<width){if(flagLeftAlign){argText+=" "}else{if(flagZeroPad&&(argText[0]=="-"||argText[0]=="+")){argText=argText[0]+"0"+argText.slice(1)}else{argText=(flagZeroPad?"0":" ")+argText}}}if(next<97)argText=argText.toUpperCase();argText.split("").forEach((function(chr){ret.push(chr.charCodeAt(0))}));break};case"s":{var arg=getNextArg("i8*");var argLength=arg?_strlen(arg):"(null)".length;if(precisionSet)argLength=Math.min(argLength,precision);if(!flagLeftAlign){while(argLength<width--){ret.push(32)}}if(arg){for(var i=0;i<argLength;i++){ret.push(HEAPU8[arg++>>0])}}else{ret=ret.concat(intArrayFromString("(null)".substr(0,argLength),true))}if(flagLeftAlign){while(argLength<width--){ret.push(32)}}break};case"c":{if(flagLeftAlign)ret.push(getNextArg("i8"));while(--width>0){ret.push(32)}if(!flagLeftAlign)ret.push(getNextArg("i8"));break};case"n":{var ptr=getNextArg("i32*");HEAP32[ptr>>2]=ret.length;break};case"%":{ret.push(curr);break};default:{for(var i=startTextIndex;i<textIndex+2;i++){ret.push(HEAP8[i>>0])}}}textIndex+=2}else{ret.push(curr);textIndex+=1}}return ret}function _fprintf(stream,format,varargs){var result=__formatString(format,varargs);var stack=Runtime.stackSave();var ret=_fwrite(allocate(result,"i8",ALLOC_STACK),1,result.length,stream);Runtime.stackRestore(stack);return ret}function _printf(format,varargs){var result=__formatString(format,varargs);var string=intArrayToString(result);if(string[string.length-1]==="\n")string=string.substr(0,string.length-1);Module.print(string);return result.length}function _pthread_once(ptr,func){if(!_pthread_once.seen)_pthread_once.seen={};if(ptr in _pthread_once.seen)return;Runtime.dynCall("v",func);_pthread_once.seen[ptr]=1}function _fputc(c,stream){var chr=unSign(c&255);HEAP8[_fputc.ret>>0]=chr;var fd=_fileno(stream);var ret=_write(fd,_fputc.ret,1);if(ret==-1){var streamObj=FS.getStreamFromPtr(stream);if(streamObj)streamObj.error=true;return-1}else{return chr}}var PTHREAD_SPECIFIC={};function _pthread_getspecific(key){return PTHREAD_SPECIFIC[key]||0}Module["_i64Add"]=_i64Add;function _fputs(s,stream){var fd=_fileno(stream);return _write(fd,s,_strlen(s))}var _stdout=allocate(1,"i32*",ALLOC_STATIC);function _puts(s){var result=Pointer_stringify(s);var string=result.substr(0);if(string[string.length-1]==="\n")string=string.substr(0,string.length-1);Module.print(string);return result.length}function _pthread_setspecific(key,value){if(!(key in PTHREAD_SPECIFIC)){return ERRNO_CODES.EINVAL}PTHREAD_SPECIFIC[key]=value;return 0}function __exit(status){Module["exit"](status)}function _exit(status){__exit(status)}var _UItoD=true;function _malloc(bytes){var ptr=Runtime.dynamicAlloc(bytes+8);return ptr+8&4294967288}Module["_malloc"]=_malloc;function ___cxa_allocate_exception(size){return _malloc(size)}function _fmod(x,y){return x%y}function _fmodl(){return _fmod.apply(null,arguments)}Module["_bitshift64Lshr"]=_bitshift64Lshr;function ___cxa_pure_virtual(){ABORT=true;throw"Pure virtual function called!"}function _time(ptr){var ret=Date.now()/1e3|0;if(ptr){HEAP32[ptr>>2]=ret}return ret}var PTHREAD_SPECIFIC_NEXT_KEY=1;function _pthread_key_create(key,destructor){if(key==0){return ERRNO_CODES.EINVAL}HEAP32[key>>2]=PTHREAD_SPECIFIC_NEXT_KEY;PTHREAD_SPECIFIC[PTHREAD_SPECIFIC_NEXT_KEY]=0;PTHREAD_SPECIFIC_NEXT_KEY++;return 0}function ___cxa_guard_acquire(variable){if(!HEAP8[variable>>0]){HEAP8[variable>>0]=1;return 1}return 0}function ___cxa_guard_release(){}function _vfprintf(s,f,va_arg){return _fprintf(s,f,HEAP32[va_arg>>2])}function ___cxa_begin_catch(ptr){__ZSt18uncaught_exceptionv.uncaught_exception--;EXCEPTIONS.caught.push(ptr);EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr));return ptr}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest);return dest}Module["_memcpy"]=_memcpy;var _llvm_pow_f64=Math_pow;function _sbrk(bytes){var self=_sbrk;if(!self.called){DYNAMICTOP=alignMemoryPage(DYNAMICTOP);self.called=true;assert(Runtime.dynamicAlloc);self.alloc=Runtime.dynamicAlloc;Runtime.dynamicAlloc=(function(){abort("cannot dynamically allocate, sbrk now has control")})}var ret=DYNAMICTOP;if(bytes!=0)self.alloc(bytes);return ret}var _fabs=Math_abs;function ___errno_location(){return ___errno_state}var _BItoD=true;function _copysign(a,b){return __reallyNegative(a)===__reallyNegative(b)?a:-a}function _copysignl(){return _copysign.apply(null,arguments)}var ___dso_handle=allocate(1,"i32*",ALLOC_STATIC);var _stderr=allocate(1,"i32*",ALLOC_STATIC);___errno_state=Runtime.staticAlloc(4);HEAP32[___errno_state>>2]=0;_fputc.ret=allocate([0],"i8",ALLOC_STATIC);STACK_BASE=STACKTOP=Runtime.alignMemory(STATICTOP);staticSealed=true;STACK_MAX=STACK_BASE+TOTAL_STACK;DYNAMIC_BASE=DYNAMICTOP=Runtime.alignMemory(STACK_MAX);assert(DYNAMIC_BASE<TOTAL_MEMORY,"TOTAL_MEMORY not big enough for stack");var ctlz_i8=allocate([8,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"i8",ALLOC_DYNAMIC);var cttz_i8=allocate([8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0],"i8",ALLOC_DYNAMIC);function invoke_iiii(index,a1,a2,a3){try{return Module["dynCall_iiii"](index,a1,a2,a3)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){try{Module["dynCall_viiiii"](index,a1,a2,a3,a4,a5)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_vi(index,a1){try{Module["dynCall_vi"](index,a1)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_vii(index,a1,a2){try{Module["dynCall_vii"](index,a1,a2)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_ii(index,a1){try{return Module["dynCall_ii"](index,a1)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_v(index){try{Module["dynCall_v"](index)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){try{Module["dynCall_viiiiii"](index,a1,a2,a3,a4,a5,a6)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_iii(index,a1,a2){try{return Module["dynCall_iii"](index,a1,a2)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_viiii(index,a1,a2,a3,a4){try{Module["dynCall_viiii"](index,a1,a2,a3,a4)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}Module.asmGlobalArg={"Math":Math,"Int8Array":Int8Array,"Int16Array":Int16Array,"Int32Array":Int32Array,"Uint8Array":Uint8Array,"Uint16Array":Uint16Array,"Uint32Array":Uint32Array,"Float32Array":Float32Array,"Float64Array":Float64Array};Module.asmLibraryArg={"abort":abort,"assert":assert,"min":Math_min,"invoke_iiii":invoke_iiii,"invoke_viiiii":invoke_viiiii,"invoke_vi":invoke_vi,"invoke_vii":invoke_vii,"invoke_ii":invoke_ii,"invoke_v":invoke_v,"invoke_viiiiii":invoke_viiiiii,"invoke_iii":invoke_iii,"invoke_viiii":invoke_viiii,"_fabs":_fabs,"_llvm_pow_f64":_llvm_pow_f64,"_send":_send,"_fmod":_fmod,"___cxa_guard_acquire":___cxa_guard_acquire,"___setErrNo":___setErrNo,"_vfprintf":_vfprintf,"___cxa_allocate_exception":___cxa_allocate_exception,"___cxa_find_matching_catch":___cxa_find_matching_catch,"___cxa_guard_release":___cxa_guard_release,"_pwrite":_pwrite,"__reallyNegative":__reallyNegative,"_sbrk":_sbrk,"___cxa_begin_catch":___cxa_begin_catch,"_emscripten_memcpy_big":_emscripten_memcpy_big,"_fileno":_fileno,"___resumeException":___resumeException,"__ZSt18uncaught_exceptionv":__ZSt18uncaught_exceptionv,"_sysconf":_sysconf,"_pthread_getspecific":_pthread_getspecific,"_atexit":_atexit,"_pthread_once":_pthread_once,"_puts":_puts,"_printf":_printf,"_pthread_key_create":_pthread_key_create,"_write":_write,"___errno_location":___errno_location,"_pthread_setspecific":_pthread_setspecific,"___cxa_atexit":___cxa_atexit,"_copysign":_copysign,"_fputc":_fputc,"___cxa_throw":___cxa_throw,"__exit":__exit,"_copysignl":_copysignl,"_abort":_abort,"_fwrite":_fwrite,"_time":_time,"_fprintf":_fprintf,"__formatString":__formatString,"_fputs":_fputs,"_exit":_exit,"___cxa_pure_virtual":___cxa_pure_virtual,"_fmodl":_fmodl,"STACKTOP":STACKTOP,"STACK_MAX":STACK_MAX,"tempDoublePtr":tempDoublePtr,"ABORT":ABORT,"cttz_i8":cttz_i8,"ctlz_i8":ctlz_i8,"NaN":NaN,"Infinity":Infinity,"___dso_handle":___dso_handle,"_stderr":_stderr};// EMSCRIPTEN_START_ASM
var asm=(function(global,env,buffer) {
"use asm";var a=new global.Int8Array(buffer);var b=new global.Int16Array(buffer);var c=new global.Int32Array(buffer);var d=new global.Uint8Array(buffer);var e=new global.Uint16Array(buffer);var f=new global.Uint32Array(buffer);var g=new global.Float32Array(buffer);var h=new global.Float64Array(buffer);var i=env.STACKTOP|0;var j=env.STACK_MAX|0;var k=env.tempDoublePtr|0;var l=env.ABORT|0;var m=env.cttz_i8|0;var n=env.ctlz_i8|0;var o=env.___dso_handle|0;var p=env._stderr|0;var q=0;var r=0;var s=0;var t=0;var u=+env.NaN,v=+env.Infinity;var w=0,x=0,y=0,z=0,A=0.0,B=0,C=0,D=0,E=0.0;var F=0;var G=0;var H=0;var I=0;var J=0;var K=0;var L=0;var M=0;var N=0;var O=0;var P=global.Math.floor;var Q=global.Math.abs;var R=global.Math.sqrt;var S=global.Math.pow;var T=global.Math.cos;var U=global.Math.sin;var V=global.Math.tan;var W=global.Math.acos;var X=global.Math.asin;var Y=global.Math.atan;var Z=global.Math.atan2;var _=global.Math.exp;var $=global.Math.log;var aa=global.Math.ceil;var ba=global.Math.imul;var ca=env.abort;var da=env.assert;var ea=env.min;var fa=env.invoke_iiii;var ga=env.invoke_viiiii;var ha=env.invoke_vi;var ia=env.invoke_vii;var ja=env.invoke_ii;var ka=env.invoke_v;var la=env.invoke_viiiiii;var ma=env.invoke_iii;var na=env.invoke_viiii;var oa=env._fabs;var pa=env._llvm_pow_f64;var qa=env._send;var ra=env._fmod;var sa=env.___cxa_guard_acquire;var ta=env.___setErrNo;var ua=env._vfprintf;var va=env.___cxa_allocate_exception;var wa=env.___cxa_find_matching_catch;var xa=env.___cxa_guard_release;var ya=env._pwrite;var za=env.__reallyNegative;var Aa=env._sbrk;var Ba=env.___cxa_begin_catch;var Ca=env._emscripten_memcpy_big;var Da=env._fileno;var Ea=env.___resumeException;var Fa=env.__ZSt18uncaught_exceptionv;var Ga=env._sysconf;var Ha=env._pthread_getspecific;var Ia=env._atexit;var Ja=env._pthread_once;var Ka=env._puts;var La=env._printf;var Ma=env._pthread_key_create;var Na=env._write;var Oa=env.___errno_location;var Pa=env._pthread_setspecific;var Qa=env.___cxa_atexit;var Ra=env._copysign;var Sa=env._fputc;var Ta=env.___cxa_throw;var Ua=env.__exit;var Va=env._copysignl;var Wa=env._abort;var Xa=env._fwrite;var Ya=env._time;var Za=env._fprintf;var _a=env.__formatString;var $a=env._fputs;var ab=env._exit;var bb=env.___cxa_pure_virtual;var cb=env._fmodl;var db=0.0;
// EMSCRIPTEN_START_FUNCS
function nb(a){a=a|0;var b=0;b=i;i=i+a|0;i=i+15&-16;return b|0}function ob(){return i|0}function pb(a){a=a|0;i=a}function qb(a,b){a=a|0;b=b|0;if(!q){q=a;r=b}}function rb(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0]}function sb(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0];a[k+4>>0]=a[b+4>>0];a[k+5>>0]=a[b+5>>0];a[k+6>>0]=a[b+6>>0];a[k+7>>0]=a[b+7>>0]}function tb(a){a=a|0;F=a}function ub(){return F|0}function vb(a){a=a|0;Ba(a|0)|0;ud()}function wb(a){a=a|0;return}function xb(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0;h=i;c[b>>2]=112;c[b+4>>2]=d;c[b+8>>2]=e;c[b+12>>2]=f;c[b+16>>2]=g;if((a[144]|0)==0?(sa(144)|0)!=0:0){c[32]=0;c[33]=0;c[34]=0;Qa(19,128,o|0)|0;xa(144)}g=c[33]|0;if((g|0)==(c[34]|0)){f=(g>>1)+2&-2;f=(f|0)<2?2:f;if((f|0)>(2147483647-g|0)){d=va(1)|0;Ta(d|0,48,0)}e=c[32]|0;d=f+g|0;c[34]=d;d=Ud(e,d<<2)|0;c[32]=d;if((d|0)==0?(c[(Oa()|0)>>2]|0)==12:0){d=va(1)|0;Ta(d|0,48,0)}g=c[33]|0}c[33]=g+1;g=(c[32]|0)+(g<<2)|0;if(!g){i=h;return}c[g>>2]=b;i=h;return}function yb(a){a=a|0;var b=0;b=i;pd(a);i=b;return}function zb(a){a=a|0;var b=0,d=0;b=i;d=c[a>>2]|0;if(!d){i=b;return}c[a+4>>2]=0;Td(d);c[a>>2]=0;c[a+8>>2]=0;i=b;return}function Ab(a){a=a|0;var b=0;b=i;pd(a);i=b;return}function Bb(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0;e=i;if((a[d>>0]|0)!=45){k=0;i=e;return k|0}f=d+1|0;g=110;j=f;k=0;while(1){h=k+1|0;if((a[j>>0]|0)!=g<<24>>24){g=1;break}j=d+(k+2)|0;if((h|0)==3){g=0;f=j;break}else{g=a[264+h>>0]|0;k=h}}if(ee(f,c[b+4>>2]|0)|0){k=0;i=e;return k|0}a[b+20>>0]=g;k=1;i=e;return k|0}function Cb(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0;h=i;i=i+16|0;e=h;f=c[p>>2]|0;g=b+4|0;j=c[g>>2]|0;c[e>>2]=j;c[e+4>>2]=j;Za(f|0,216,e|0)|0;j=0;while(1){k=j>>>0<(32-((me(c[g>>2]|0)|0)<<1)|0)>>>0;Sa(32,f|0)|0;if(k)j=j+1|0;else break}c[e>>2]=(a[b+20>>0]|0)!=0?248:256;Za(f|0,232,e|0)|0;if(!d){i=h;return}c[e>>2]=c[b+8>>2];Za(f|0,88,e|0)|0;Sa(10,f|0)|0;i=h;return}function Db(a){a=a|0;var b=0;b=i;pd(a);i=b;return}function Eb(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;e=i;i=i+16|0;h=e;g=e+8|0;if((a[d>>0]|0)!=45){n=0;i=e;return n|0}l=d+1|0;f=b+4|0;j=c[f>>2]|0;k=a[j>>0]|0;a:do if(k<<24>>24){m=0;while(1){n=m;m=m+1|0;if((a[l>>0]|0)!=k<<24>>24){b=0;break}k=a[j+m>>0]|0;l=d+(n+2)|0;if(!(k<<24>>24))break a}i=e;return b|0}while(0);if((a[l>>0]|0)!=61){n=0;i=e;return n|0}d=l+1|0;j=de(d,g,10)|0;if(!(c[g>>2]|0)){n=0;i=e;return n|0}if((j|0)>(c[b+24>>2]|0)){n=c[p>>2]|0;m=c[f>>2]|0;c[h>>2]=d;c[h+4>>2]=m;Za(n|0,416,h|0)|0;ab(1)}if((j|0)<(c[b+20>>2]|0)){n=c[p>>2]|0;m=c[f>>2]|0;c[h>>2]=d;c[h+4>>2]=m;Za(n|0,472,h|0)|0;ab(1)}c[b+28>>2]=j;n=1;i=e;return n|0}function Fb(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;d=i;i=i+16|0;e=d;f=c[p>>2]|0;g=c[a+16>>2]|0;c[e>>2]=c[a+4>>2];c[e+4>>2]=g;Za(f|0,336,e|0)|0;g=c[a+20>>2]|0;if((g|0)==-2147483648)Xa(360,4,1,f|0)|0;else{c[e>>2]=g;Za(f|0,368,e|0)|0}Xa(376,4,1,f|0)|0;g=c[a+24>>2]|0;if((g|0)==2147483647)Xa(384,4,1,f|0)|0;else{c[e>>2]=g;Za(f|0,368,e|0)|0}c[e>>2]=c[a+28>>2];Za(f|0,392,e|0)|0;if(!b){i=d;return}c[e>>2]=c[a+8>>2];Za(f|0,88,e|0)|0;Sa(10,f|0)|0;i=d;return}function Gb(b){b=b|0;var d=0,e=0,f=0,g=0,j=0;g=i;c[b>>2]=1816;f=b+4|0;e=b+32|0;j=b+48|0;c[f+0>>2]=0;c[f+4>>2]=0;c[f+8>>2]=0;c[f+12>>2]=0;c[f+16>>2]=0;c[f+20>>2]=0;c[e+0>>2]=0;c[e+4>>2]=0;c[e+8>>2]=0;c[e+12>>2]=0;h[j>>3]=+h[75];h[b+56>>3]=+h[89];h[b+64>>3]=+h[103];h[b+72>>3]=+h[123];a[b+80>>0]=a[1364]|0;c[b+84>>2]=c[269];c[b+88>>2]=c[297];a[b+92>>0]=0;a[b+93>>0]=a[1292]|0;h[b+96>>3]=+h[204];c[b+104>>2]=c[439];c[b+108>>2]=c[359];h[b+112>>3]=+h[191];h[b+120>>3]=.3333333333333333;h[b+128>>3]=1.1;c[b+136>>2]=100;h[b+144>>3]=1.5;j=b+316|0;c[b+332>>2]=0;c[b+336>>2]=0;c[b+340>>2]=0;c[b+348>>2]=0;c[b+352>>2]=0;c[b+356>>2]=0;c[b+364>>2]=0;c[b+368>>2]=0;c[b+372>>2]=0;c[b+380>>2]=0;c[b+384>>2]=0;c[b+388>>2]=0;c[b+396>>2]=0;c[b+400>>2]=0;c[b+404>>2]=0;e=b+544|0;c[b+412>>2]=0;c[b+416>>2]=0;c[b+420>>2]=0;c[b+428>>2]=0;c[b+432>>2]=0;c[b+436>>2]=0;c[b+444>>2]=0;c[b+448>>2]=0;c[b+452>>2]=0;ke(b+152|0,0,176)|0;c[b+456>>2]=e;f=b+460|0;c[f+0>>2]=0;c[f+4>>2]=0;c[f+8>>2]=0;c[f+12>>2]=0;c[f+16>>2]=0;c[f+20>>2]=0;c[b+488>>2]=j;a[b+492>>0]=1;h[b+496>>3]=1.0;h[b+504>>3]=1.0;c[b+512>>2]=0;c[b+516>>2]=-1;j=b+520|0;f=b+536|0;c[j+0>>2]=0;c[j+4>>2]=0;c[j+8>>2]=0;c[j+12>>2]=0;a[f>>0]=1;f=b+540|0;c[f+0>>2]=0;c[f+4>>2]=0;c[f+8>>2]=0;c[f+12>>2]=0;c[f+16>>2]=0;gc(e,1048576);a[b+560>>0]=0;e=b+604|0;f=b+664|0;j=b+564|0;d=j+36|0;do{c[j>>2]=0;j=j+4|0}while((j|0)<(d|0));j=e+0|0;d=j+36|0;do{c[j>>2]=0;j=j+4|0}while((j|0)<(d|0));j=b+680|0;c[f+0>>2]=-1;c[f+4>>2]=-1;c[f+8>>2]=-1;c[f+12>>2]=-1;a[j>>0]=0;i=g;return}function Hb(a){a=a|0;var b=0;b=i;Ib(a);pd(a);i=b;return}function Ib(a){a=a|0;var b=0,d=0,e=0;b=i;c[a>>2]=1816;d=a+628|0;e=c[d>>2]|0;if(e){c[a+632>>2]=0;Td(e);c[d>>2]=0;c[a+636>>2]=0}d=a+616|0;e=c[d>>2]|0;if(e){c[a+620>>2]=0;Td(e);c[d>>2]=0;c[a+624>>2]=0}d=a+604|0;e=c[d>>2]|0;if(e){c[a+608>>2]=0;Td(e);c[d>>2]=0;c[a+612>>2]=0}d=a+588|0;e=c[d>>2]|0;if(e){c[a+592>>2]=0;Td(e);c[d>>2]=0;c[a+596>>2]=0}d=a+576|0;e=c[d>>2]|0;if(e){c[a+580>>2]=0;Td(e);c[d>>2]=0;c[a+584>>2]=0}d=a+564|0;e=c[d>>2]|0;if(e){c[a+568>>2]=0;Td(e);c[d>>2]=0;c[a+572>>2]=0}d=c[a+544>>2]|0;if(d)Td(d);d=a+472|0;e=c[d>>2]|0;if(e){c[a+476>>2]=0;Td(e);c[d>>2]=0;c[a+480>>2]=0}d=a+460|0;e=c[d>>2]|0;if(e){c[a+464>>2]=0;Td(e);c[d>>2]=0;c[a+468>>2]=0}hc(a+412|0);d=a+396|0;e=c[d>>2]|0;if(e){c[a+400>>2]=0;Td(e);c[d>>2]=0;c[a+404>>2]=0}d=a+380|0;e=c[d>>2]|0;if(e){c[a+384>>2]=0;Td(e);c[d>>2]=0;c[a+388>>2]=0}e=a+364|0;d=c[e>>2]|0;if(d){c[a+368>>2]=0;Td(d);c[e>>2]=0;c[a+372>>2]=0}d=a+348|0;e=c[d>>2]|0;if(e){c[a+352>>2]=0;Td(e);c[d>>2]=0;c[a+356>>2]=0}d=a+332|0;e=c[d>>2]|0;if(e){c[a+336>>2]=0;Td(e);c[d>>2]=0;c[a+340>>2]=0}d=a+316|0;e=c[d>>2]|0;if(e){c[a+320>>2]=0;Td(e);c[d>>2]=0;c[a+324>>2]=0}d=a+304|0;e=c[d>>2]|0;if(e){c[a+308>>2]=0;Td(e);c[d>>2]=0;c[a+312>>2]=0}d=a+292|0;e=c[d>>2]|0;if(e){c[a+296>>2]=0;Td(e);c[d>>2]=0;c[a+300>>2]=0}d=a+280|0;e=c[d>>2]|0;if(e){c[a+284>>2]=0;Td(e);c[d>>2]=0;c[a+288>>2]=0}d=a+268|0;e=c[d>>2]|0;if(e){c[a+272>>2]=0;Td(e);c[d>>2]=0;c[a+276>>2]=0}d=a+256|0;e=c[d>>2]|0;if(e){c[a+260>>2]=0;Td(e);c[d>>2]=0;c[a+264>>2]=0}d=a+32|0;e=c[d>>2]|0;if(e){c[a+36>>2]=0;Td(e);c[d>>2]=0;c[a+40>>2]=0}d=a+16|0;e=c[d>>2]|0;if(e){c[a+20>>2]=0;Td(e);c[d>>2]=0;c[a+24>>2]=0}e=a+4|0;d=c[e>>2]|0;if(!d){i=b;return}c[a+8>>2]=0;Td(d);c[e>>2]=0;c[a+12>>2]=0;i=b;return}function Jb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,j=0,k=0,l=0.0,m=0,n=0,o=0,p=0,q=0,r=0;f=i;i=i+16|0;k=f+4|0;j=f;g=b+580|0;m=c[g>>2]|0;if((m|0)>0){o=m+ -1|0;p=c[(c[b+576>>2]|0)+(o<<2)>>2]|0;c[g>>2]=o;g=p}else{p=b+540|0;g=c[p>>2]|0;c[p>>2]=g+1}m=b+412|0;p=g<<1;c[k>>2]=p;ic(m,k);c[j>>2]=p|1;ic(m,j);k=b+332|0;m=a[544]|0;j=g+1|0;jc(k,j);a[(c[k>>2]|0)+g>>0]=m;k=b+396|0;m=b+400|0;if((c[m>>2]|0)<(j|0)){o=b+404|0;p=c[o>>2]|0;if((p|0)<(j|0)){q=g+2-p&-2;n=(p>>1)+2&-2;n=(q|0)>(n|0)?q:n;if((n|0)>(2147483647-p|0)){q=va(1)|0;Ta(q|0,48,0)}r=c[k>>2]|0;q=n+p|0;c[o>>2]=q;q=Ud(r,q<<3)|0;c[k>>2]=q;if((q|0)==0?(c[(Oa()|0)>>2]|0)==12:0){r=va(1)|0;Ta(r|0,48,0)}}o=c[m>>2]|0;if((o|0)<(j|0))do{n=(c[k>>2]|0)+(o<<3)|0;if(n){r=n;c[r>>2]=0;c[r+4>>2]=0}o=o+1|0}while((o|0)!=(j|0));c[m>>2]=j}m=(c[k>>2]|0)+(g<<3)|0;c[m>>2]=-1;c[m+4>>2]=0;m=b+316|0;if(!(a[b+93>>0]|0))l=0.0;else{r=b+72|0;l=+h[r>>3]*1389796.0;l=l- +(~~(l/2147483647.0)|0)*2147483647.0;h[r>>3]=l;l=l/2147483647.0*1.0e-5}k=b+320|0;if((c[k>>2]|0)<(j|0)){n=b+324|0;o=c[n>>2]|0;if((o|0)<(j|0)){r=g+2-o&-2;p=(o>>1)+2&-2;p=(r|0)>(p|0)?r:p;if((p|0)>(2147483647-o|0)){r=va(1)|0;Ta(r|0,48,0)}q=c[m>>2]|0;r=p+o|0;c[n>>2]=r;r=Ud(q,r<<3)|0;c[m>>2]=r;if((r|0)==0?(c[(Oa()|0)>>2]|0)==12:0){r=va(1)|0;Ta(r|0,48,0)}}p=c[k>>2]|0;if((p|0)<(j|0)){n=c[m>>2]|0;do{o=n+(p<<3)|0;if(o)h[o>>3]=0.0;p=p+1|0}while((p|0)!=(j|0))}c[k>>2]=j}h[(c[m>>2]|0)+(g<<3)>>3]=l;kc(b+588|0,g,0);kc(b+348|0,g,1);k=b+364|0;d=a[d>>0]|0;jc(k,j);a[(c[k>>2]|0)+g>>0]=d;k=b+380|0;d=b+384|0;if((c[d>>2]|0)<(j|0)){m=b+388|0;o=c[m>>2]|0;if((o|0)<(j|0)){r=g+2-o&-2;n=(o>>1)+2&-2;n=(r|0)>(n|0)?r:n;if((n|0)>(2147483647-o|0)){r=va(1)|0;Ta(r|0,48,0)}q=c[k>>2]|0;r=n+o|0;c[m>>2]=r;r=Ud(q,r)|0;c[k>>2]=r;if((r|0)==0?(c[(Oa()|0)>>2]|0)==12:0){r=va(1)|0;Ta(r|0,48,0)}}m=c[d>>2]|0;if((m|0)<(j|0))do{n=(c[k>>2]|0)+m|0;if(n)a[n>>0]=0;m=m+1|0}while((m|0)!=(j|0));c[d>>2]=j}d=b+288|0;k=c[d>>2]|0;if((k|0)<(j|0)){r=g+2-k&-2;j=(k>>1)+2&-2;j=(r|0)>(j|0)?r:j;if((j|0)>(2147483647-k|0)){r=va(1)|0;Ta(r|0,48,0)}q=b+280|0;p=c[q>>2]|0;r=j+k|0;c[d>>2]=r;r=Ud(p,r<<2)|0;c[q>>2]=r;if((r|0)==0?(c[(Oa()|0)>>2]|0)==12:0){r=va(1)|0;Ta(r|0,48,0)}}j=b+380|0;d=(c[j>>2]|0)+g|0;k=(a[d>>0]|0)==0;if(e){if(k){r=b+200|0;q=r;q=ne(c[q>>2]|0,c[q+4>>2]|0,1,0)|0;c[r>>2]=q;c[r+4>>2]=F}}else if(!k){r=b+200|0;q=r;q=ne(c[q>>2]|0,c[q+4>>2]|0,-1,-1)|0;c[r>>2]=q;c[r+4>>2]=F}a[d>>0]=e&1;e=b+460|0;if((c[b+476>>2]|0)>(g|0)?(c[(c[b+472>>2]|0)+(g<<2)>>2]|0)>-1:0){i=f;return g|0}if(!(a[(c[j>>2]|0)+g>>0]|0)){i=f;return g|0}lc(e,g);i=f;return g|0}function Kb(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;f=i;i=i+16|0;k=f+1|0;j=f;g=b+492|0;if(!(a[g>>0]|0)){s=0;i=f;return s|0}s=c[e>>2]|0;h=e+4|0;l=c[h>>2]|0;a[k+0>>0]=a[j+0>>0]|0;oc(s,l,k);l=c[h>>2]|0;a:do if((l|0)>0){k=b+332|0;j=a[528]|0;m=0;n=0;p=-2;while(1){s=c[e>>2]|0;o=c[s+(m<<2)>>2]|0;r=d[(c[k>>2]|0)+(o>>1)>>0]|0;t=r^o&1;q=t&255;u=j&255;if((o|0)==(p^1|0)?1:(q<<24>>24==j<<24>>24&(u>>>1^1)|u&2&t|0)!=0){b=1;break}t=a[536]|0;u=t&255;if((o|0)!=(p|0)?((u>>>1^1)&q<<24>>24==t<<24>>24|r&2&u|0)==0:0){c[s+(n<<2)>>2]=o;l=c[h>>2]|0;n=n+1|0}else o=p;m=m+1|0;if((m|0)<(l|0))p=o;else break a}i=f;return b|0}else{m=0;n=0}while(0);j=m-n|0;if((j|0)>0){l=l-j|0;c[h>>2]=l}if(!l){a[g>>0]=0;u=0;i=f;return u|0}else if((l|0)==1){t=c[c[e>>2]>>2]|0;s=t>>1;a[(c[b+332>>2]|0)+s>>0]=(t&1^1)&255^1;u=c[b+296>>2]|0;s=(c[b+396>>2]|0)+(s<<3)|0;c[s>>2]=-1;c[s+4>>2]=u;s=b+284|0;u=c[s>>2]|0;c[s>>2]=u+1;c[(c[b+280>>2]|0)+(u<<2)>>2]=t;u=(Mb(b)|0)==-1;a[g>>0]=u&1;i=f;return u|0}else{e=pc(b+544|0,e,0)|0;h=b+256|0;g=b+260|0;k=c[g>>2]|0;j=b+264|0;if((k|0)==(c[j>>2]|0)){l=(k>>1)+2&-2;l=(l|0)<2?2:l;if((l|0)>(2147483647-k|0)){u=va(1)|0;Ta(u|0,48,0)}t=c[h>>2]|0;u=l+k|0;c[j>>2]=u;u=Ud(t,u<<2)|0;c[h>>2]=u;if((u|0)==0?(c[(Oa()|0)>>2]|0)==12:0){u=va(1)|0;Ta(u|0,48,0)}k=c[g>>2]|0}c[g>>2]=k+1;g=(c[h>>2]|0)+(k<<2)|0;if(g)c[g>>2]=e;Nb(b,e);u=1;i=f;return u|0}return 0}function Lb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0;f=c[d>>2]|0;d=f>>1;a[(c[b+332>>2]|0)+d>>0]=(f&1^1)&255^1;g=c[b+296>>2]|0;d=(c[b+396>>2]|0)+(d<<3)|0;c[d>>2]=e;c[d+4>>2]=g;e=b+284|0;d=c[e>>2]|0;c[e>>2]=d+1;c[(c[b+280>>2]|0)+(d<<2)>>2]=f;return}function Mb(b){b=b|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0;k=i;i=i+16|0;r=k;h=b+512|0;t=c[h>>2]|0;q=b+284|0;if((t|0)>=(c[q>>2]|0)){M=0;K=0;O=-1;N=b+184|0;I=N;L=I;L=c[L>>2]|0;I=I+4|0;I=c[I>>2]|0;I=ne(L|0,I|0,M|0,K|0)|0;L=F;J=N;c[J>>2]=I;N=N+4|0;c[N>>2]=L;N=b+520|0;L=N;J=L;J=c[J>>2]|0;L=L+4|0;L=c[L>>2]|0;K=je(J|0,L|0,M|0,K|0)|0;M=F;L=N;c[L>>2]=K;N=N+4|0;c[N>>2]=M;i=k;return O|0}o=b+280|0;j=b+428|0;g=b+412|0;l=b+332|0;m=b+544|0;n=r+4|0;e=b+396|0;p=b+296|0;f=b+456|0;z=-1;s=0;do{c[h>>2]=t+1;w=c[(c[o>>2]|0)+(t<<2)>>2]|0;if(a[(c[j>>2]|0)+w>>0]|0){u=c[g>>2]|0;t=u+(w*12|0)+4|0;y=c[t>>2]|0;if((y|0)>0){u=u+(w*12|0)|0;v=0;x=0;do{B=c[u>>2]|0;A=B+(v<<3)|0;if((c[(c[c[f>>2]>>2]|0)+(c[A>>2]<<2)>>2]&3|0)!=1){N=A;O=c[N+4>>2]|0;y=B+(x<<3)|0;c[y>>2]=c[N>>2];c[y+4>>2]=O;y=c[t>>2]|0;x=x+1|0}v=v+1|0}while((v|0)<(y|0))}else{v=0;x=0}u=v-x|0;if((u|0)>0)c[t>>2]=y-u;a[(c[j>>2]|0)+w>>0]=0}t=c[g>>2]|0;s=s+1|0;u=c[t+(w*12|0)>>2]|0;t=t+(w*12|0)+4|0;x=c[t>>2]|0;v=u+(x<<3)|0;a:do if(!x){v=u;y=u}else{w=w^1;x=(x<<3)+ -1|0;B=u;y=u;while(1){while(1){b:while(1){H=c[B+4>>2]|0;O=d[(c[l>>2]|0)+(H>>1)>>0]^H&1;J=a[528]|0;I=J&255;K=I&2;I=I>>>1^1;if((O&255)<<24>>24==J<<24>>24&I|K&O){E=19;break}A=c[B>>2]|0;E=c[m>>2]|0;G=E+(A<<2)|0;C=E+(A+1<<2)|0;D=c[C>>2]|0;if((D|0)==(w|0)){O=E+(A+2<<2)|0;D=c[O>>2]|0;c[C>>2]=D;c[O>>2]=w}C=B+8|0;c[r>>2]=A;c[n>>2]=D;if((D|0)!=(H|0)?(O=d[(c[l>>2]|0)+(D>>1)>>0]^D&1,((O&255)<<24>>24==J<<24>>24&I|K&O|0)!=0):0){E=27;break}K=c[G>>2]|0;if(K>>>0<=95){E=31;break}I=c[l>>2]|0;J=a[536]|0;H=J&255;O=H&2;H=H>>>1^1;N=2;while(1){L=G+(N<<2)+4|0;M=c[L>>2]|0;P=d[I+(M>>1)>>0]^M&1;N=N+1|0;if(!((P&255)<<24>>24==J<<24>>24&H|O&P))break;if((N|0)>=(K>>>5|0)){E=32;break b}}P=E+(A+2<<2)|0;c[P>>2]=M;c[L>>2]=w;qc((c[g>>2]|0)+((c[P>>2]^1)*12|0)|0,r);if((C|0)==(v|0))break a;else B=C}if((E|0)==19){E=0;N=B;O=c[N+4>>2]|0;P=y;c[P>>2]=c[N>>2];c[P+4>>2]=O;B=B+8|0;y=y+8|0}else if((E|0)==27){E=0;O=r;P=c[O+4>>2]|0;B=y;c[B>>2]=c[O>>2];c[B+4>>2]=P;B=C;y=y+8|0}else if((E|0)==31){J=a[536]|0;E=32}if((E|0)==32){E=y+8|0;G=r;I=c[G+4>>2]|0;H=y;c[H>>2]=c[G>>2];c[H+4>>2]=I;H=D>>1;I=D&1;G=(c[l>>2]|0)+H|0;P=d[G>>0]^I;O=J&255;if((P&255)<<24>>24==J<<24>>24&(O>>>1^1)|O&2&P)break;a[G>>0]=(I^1)&255^1;y=c[p>>2]|0;B=(c[e>>2]|0)+(H<<3)|0;c[B>>2]=A;c[B+4>>2]=y;B=c[q>>2]|0;c[q>>2]=B+1;c[(c[o>>2]|0)+(B<<2)>>2]=D;B=C;y=E}if((B|0)==(v|0))break a}c[h>>2]=c[q>>2];if(C>>>0<v>>>0){z=(u+(x-C)|0)>>>3;while(1){N=C;C=C+8|0;O=c[N+4>>2]|0;P=E;c[P>>2]=c[N>>2];c[P+4>>2]=O;if(C>>>0>=v>>>0)break;else E=E+8|0}B=B+(z+2<<3)|0;y=y+(z+2<<3)|0}else{B=C;y=E}if((B|0)==(v|0)){z=A;break}else z=A}}while(0);u=v-y|0;if((u|0)>0)c[t>>2]=(c[t>>2]|0)-(u>>3);t=c[h>>2]|0}while((t|0)<(c[q>>2]|0));N=s;L=((s|0)<0)<<31>>31;P=z;O=b+184|0;J=O;M=J;M=c[M>>2]|0;J=J+4|0;J=c[J>>2]|0;J=ne(M|0,J|0,N|0,L|0)|0;M=F;K=O;c[K>>2]=J;O=O+4|0;c[O>>2]=M;O=b+520|0;M=O;K=M;K=c[K>>2]|0;M=M+4|0;M=c[M>>2]|0;L=je(K|0,M|0,N|0,L|0)|0;N=F;M=O;c[M>>2]=L;O=O+4|0;c[O>>2]=N;i=k;return P|0}function Nb(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;d=i;i=i+16|0;k=d+8|0;f=d;g=c[a+544>>2]|0;e=g+(b<<2)|0;h=g+(b+1<<2)|0;j=a+412|0;l=(c[j>>2]|0)+((c[h>>2]^1)*12|0)|0;g=g+(b+2<<2)|0;m=c[g>>2]|0;c[k>>2]=b;c[k+4>>2]=m;qc(l,k);g=(c[j>>2]|0)+((c[g>>2]^1)*12|0)|0;h=c[h>>2]|0;c[f>>2]=b;c[f+4>>2]=h;qc(g,f);if(!(c[e>>2]&4)){m=a+208|0;l=m;l=ne(c[l>>2]|0,c[l+4>>2]|0,1,0)|0;c[m>>2]=l;c[m+4>>2]=F;m=a+224|0;l=m;l=ne((c[e>>2]|0)>>>5|0,0,c[l>>2]|0,c[l+4>>2]|0)|0;c[m>>2]=l;c[m+4>>2]=F;i=d;return}else{m=a+216|0;l=m;l=ne(c[l>>2]|0,c[l+4>>2]|0,1,0)|0;c[m>>2]=l;c[m+4>>2]=F;m=a+232|0;l=m;l=ne((c[e>>2]|0)>>>5|0,0,c[l>>2]|0,c[l+4>>2]|0)|0;c[m>>2]=l;c[m+4>>2]=F;i=d;return}}function Ob(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;g=i;i=i+16|0;l=g+4|0;j=g;h=c[b+544>>2]|0;f=h+(d<<2)|0;k=c[h+(d+1<<2)>>2]^1;if(!e){c[l>>2]=k;e=b+428|0;m=c[e>>2]|0;k=m+k|0;if(!(a[k>>0]|0)){a[k>>0]=1;mc(b+444|0,l);m=c[e>>2]|0}d=c[h+(d+2<<2)>>2]^1;c[j>>2]=d;d=m+d|0;if(!(a[d>>0]|0)){a[d>>0]=1;mc(b+444|0,j)}}else{j=b+412|0;e=c[j>>2]|0;l=e+(k*12|0)|0;h=h+(d+2<<2)|0;k=e+(k*12|0)+4|0;m=c[k>>2]|0;a:do if((m|0)>0){p=c[l>>2]|0;o=0;while(1){n=o+1|0;if((c[p+(o<<3)>>2]|0)==(d|0)){n=o;break a}if((n|0)<(m|0))o=n;else break}}else n=0;while(0);m=m+ -1|0;if((n|0)<(m|0)){do{e=c[l>>2]|0;m=n;n=n+1|0;o=e+(n<<3)|0;p=c[o+4>>2]|0;m=e+(m<<3)|0;c[m>>2]=c[o>>2];c[m+4>>2]=p;m=(c[k>>2]|0)+ -1|0}while((n|0)<(m|0));e=c[j>>2]|0}c[k>>2]=m;j=c[h>>2]^1;h=e+(j*12|0)|0;j=e+(j*12|0)+4|0;k=c[j>>2]|0;b:do if((k|0)>0){e=c[h>>2]|0;m=0;while(1){l=m+1|0;if((c[e+(m<<3)>>2]|0)==(d|0)){l=m;break b}if((l|0)<(k|0))m=l;else break}}else l=0;while(0);d=k+ -1|0;if((l|0)<(d|0))do{n=c[h>>2]|0;d=l;l=l+1|0;o=n+(l<<3)|0;p=c[o+4>>2]|0;d=n+(d<<3)|0;c[d>>2]=c[o>>2];c[d+4>>2]=p;d=(c[j>>2]|0)+ -1|0}while((l|0)<(d|0));c[j>>2]=d}if(!(c[f>>2]&4)){p=b+208|0;o=p;o=ne(c[o>>2]|0,c[o+4>>2]|0,-1,-1)|0;c[p>>2]=o;c[p+4>>2]=F;p=b+224|0;o=p;o=je(c[o>>2]|0,c[o+4>>2]|0,(c[f>>2]|0)>>>5|0,0)|0;c[p>>2]=o;c[p+4>>2]=F;i=g;return}else{p=b+216|0;o=p;o=ne(c[o>>2]|0,c[o+4>>2]|0,-1,-1)|0;c[p>>2]=o;c[p+4>>2]=F;p=b+232|0;o=p;o=je(c[o>>2]|0,c[o+4>>2]|0,(c[f>>2]|0)>>>5|0,0)|0;c[p>>2]=o;c[p+4>>2]=F;i=g;return}}function Pb(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;h=i;g=b+544|0;m=c[g>>2]|0;f=m+(e<<2)|0;Ob(b,e,0);m=c[m+(e+1<<2)>>2]|0;j=m>>1;m=(d[(c[b+332>>2]|0)+j>>0]|0)^m&1;o=a[528]|0;n=o&255;if((((m&255)<<24>>24==o<<24>>24&(n>>>1^1)|n&2&m|0)!=0?(k=(c[b+396>>2]|0)+(j<<3)|0,l=c[k>>2]|0,(l|0)!=-1):0)?((c[g>>2]|0)+(l<<2)|0)==(f|0):0)c[k>>2]=-1;c[f>>2]=c[f>>2]&-4|1;n=c[(c[g>>2]|0)+(e<<2)>>2]|0;o=b+556|0;c[o>>2]=((((n>>>3&1)+(n>>>5)<<2)+4|0)>>>2)+(c[o>>2]|0);i=h;return}function Qb(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0;f=i;g=c[e>>2]|0;if(g>>>0<=31){l=0;i=f;return l|0}h=c[b+332>>2]|0;j=a[528]|0;k=j&255;l=k&2;k=k>>>1^1;b=0;while(1){m=c[e+(b<<2)+4>>2]|0;m=(d[h+(m>>1)>>0]|0)^m&1;b=b+1|0;if((m&255)<<24>>24==j<<24>>24&k|l&m){g=1;e=5;break}if((b|0)>=(g>>>5|0)){g=0;e=5;break}}if((e|0)==5){i=f;return g|0}return 0}function Rb(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;g=i;e=b+296|0;if((c[e>>2]|0)<=(d|0)){i=g;return}f=b+284|0;s=c[f>>2]|0;j=b+292|0;t=c[j>>2]|0;u=c[t+(d<<2)>>2]|0;if((s|0)>(u|0)){r=b+280|0;m=b+332|0;l=b+88|0;k=b+348|0;n=b+460|0;p=b+476|0;q=b+472|0;o=b+380|0;do{s=s+ -1|0;u=c[(c[r>>2]|0)+(s<<2)>>2]>>1;a[(c[m>>2]|0)+u>>0]=a[544]|0;t=c[l>>2]|0;if((t|0)<=1){if((t|0)==1?(s|0)>(c[(c[j>>2]|0)+((c[e>>2]|0)+ -1<<2)>>2]|0):0)h=7}else h=7;if((h|0)==7){h=0;a[(c[k>>2]|0)+u>>0]=c[(c[r>>2]|0)+(s<<2)>>2]&1}if(!((c[p>>2]|0)>(u|0)?(c[(c[q>>2]|0)+(u<<2)>>2]|0)>-1:0))h=11;if((h|0)==11?(h=0,(a[(c[o>>2]|0)+u>>0]|0)!=0):0)lc(n,u);t=c[j>>2]|0;u=c[t+(d<<2)>>2]|0}while((s|0)>(u|0));s=c[f>>2]|0}c[b+512>>2]=u;b=c[t+(d<<2)>>2]|0;if((s-b|0)>0)c[f>>2]=b;if(((c[e>>2]|0)-d|0)<=0){i=g;return}c[e>>2]=d;i=g;return}function Sb(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0;d=i;f=b+72|0;q=+h[f>>3]*1389796.0;q=q- +(~~(q/2147483647.0)|0)*2147483647.0;h[f>>3]=q;l=b+464|0;if(q/2147483647.0<+h[b+64>>3]?(m=c[l>>2]|0,(m|0)!=0):0){q=q*1389796.0;q=q- +(~~(q/2147483647.0)|0)*2147483647.0;h[f>>3]=q;m=c[(c[b+460>>2]|0)+(~~(+(m|0)*(q/2147483647.0))<<2)>>2]|0;o=a[(c[b+332>>2]|0)+m>>0]|0;n=a[544]|0;p=n&255;if(((p>>>1^1)&o<<24>>24==n<<24>>24|o&2&p|0)!=0?(a[(c[b+380>>2]|0)+m>>0]|0)!=0:0){p=b+176|0;o=p;o=ne(c[o>>2]|0,c[o+4>>2]|0,1,0)|0;c[p>>2]=o;c[p+4>>2]=F}}else m=-1;n=b+460|0;p=b+332|0;o=b+380|0;while(1){if(((m|0)!=-1?(r=a[(c[p>>2]|0)+m>>0]|0,j=a[544]|0,e=j&255,g=e>>>1^1,(g&r<<24>>24==j<<24>>24|r&2&e|0)!=0):0)?(a[(c[o>>2]|0)+m>>0]|0)!=0:0)break;if(!(c[l>>2]|0)){e=-2;k=17;break}m=rc(n)|0}if((k|0)==17){i=d;return e|0}l=a[(c[b+364>>2]|0)+m>>0]|0;k=l&255;if(!(g&l<<24>>24==j<<24>>24|e&2&k)){p=a[528]|0;r=p&255;r=((r>>>1^1)&l<<24>>24==p<<24>>24|k&2&r|0)!=0|m<<1;i=d;return r|0}if(!(a[b+92>>0]|0)){r=(a[(c[b+348>>2]|0)+m>>0]|0)!=0|m<<1;i=d;return r|0}else{q=+h[f>>3]*1389796.0;q=q- +(~~(q/2147483647.0)|0)*2147483647.0;h[f>>3]=q;r=q/2147483647.0<.5|m<<1;i=d;return r|0}return 0}function Tb(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0.0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0.0,U=0;j=i;i=i+16|0;p=j+8|0;t=j+4|0;n=j;m=e+4|0;k=c[m>>2]|0;l=e+8|0;if((k|0)==(c[l>>2]|0)){q=(k>>1)+2&-2;q=(q|0)<2?2:q;if((q|0)>(2147483647-k|0)){S=va(1)|0;Ta(S|0,48,0)}R=c[e>>2]|0;S=q+k|0;c[l>>2]=S;S=Ud(R,S<<2)|0;c[e>>2]=S;if((S|0)==0?(c[(Oa()|0)>>2]|0)==12:0){S=va(1)|0;Ta(S|0,48,0)}k=c[m>>2]|0}l=(c[e>>2]|0)+(k<<2)|0;if(l){c[l>>2]=0;k=c[m>>2]|0}c[m>>2]=k+1;q=b+544|0;H=b+280|0;k=b+588|0;l=b+396|0;C=b+504|0;E=b+316|0;D=b+540|0;B=b+476|0;A=b+472|0;z=b+460|0;y=b+488|0;x=b+296|0;v=b+496|0;w=b+272|0;G=b+268|0;J=-2;I=(c[b+284>>2]|0)+ -1|0;K=0;do{L=c[q>>2]|0;d=L+(d<<2)|0;M=c[d>>2]|0;if((M&4|0)!=0?(r=+h[v>>3],S=d+(M>>>5<<2)+4|0,T=r+ +g[S>>2],g[S>>2]=T,T>1.0e20):0){O=c[w>>2]|0;if((O|0)>0){N=c[G>>2]|0;M=0;do{S=L+(c[N+(M<<2)>>2]<<2)|0;S=S+((c[S>>2]|0)>>>5<<2)+4|0;g[S>>2]=+g[S>>2]*1.0e-20;M=M+1|0}while((M|0)!=(O|0))}h[v>>3]=r*1.0e-20}J=(J|0)!=-2&1;if(J>>>0<(c[d>>2]|0)>>>5>>>0)do{M=c[d+(J<<2)+4>>2]|0;c[t>>2]=M;M=M>>1;L=(c[k>>2]|0)+M|0;do if((a[L>>0]|0)==0?(c[(c[l>>2]|0)+(M<<3)+4>>2]|0)>0:0){O=c[E>>2]|0;S=O+(M<<3)|0;T=+h[C>>3]+ +h[S>>3];h[S>>3]=T;if(T>1.0e+100){P=c[D>>2]|0;if((P|0)>0){N=0;do{S=O+(N<<3)|0;h[S>>3]=+h[S>>3]*1.0e-100;N=N+1|0}while((N|0)!=(P|0))}h[C>>3]=+h[C>>3]*1.0e-100}if((c[B>>2]|0)>(M|0)?(u=c[A>>2]|0,s=c[u+(M<<2)>>2]|0,(s|0)>-1):0){N=c[z>>2]|0;O=c[N+(s<<2)>>2]|0;a:do if(!s)R=0;else{S=s;while(1){R=S;S=S+ -1>>1;Q=N+(S<<2)|0;P=c[Q>>2]|0;U=c[c[y>>2]>>2]|0;if(!(+h[U+(O<<3)>>3]>+h[U+(P<<3)>>3]))break a;c[N+(R<<2)>>2]=P;c[u+(c[Q>>2]<<2)>>2]=R;if(!S){R=0;break}}}while(0);c[N+(R<<2)>>2]=O;c[u+(O<<2)>>2]=R}a[L>>0]=1;if((c[(c[l>>2]|0)+(M<<3)+4>>2]|0)<(c[x>>2]|0)){mc(e,t);break}else{K=K+1|0;break}}while(0);J=J+1|0}while((J|0)<((c[d>>2]|0)>>>5|0));d=c[H>>2]|0;L=c[k>>2]|0;do{J=I;I=I+ -1|0;J=c[d+(J<<2)>>2]|0;N=J>>1;M=L+N|0}while((a[M>>0]|0)==0);d=c[(c[l>>2]|0)+(N<<3)>>2]|0;a[M>>0]=0;K=K+ -1|0}while((K|0)>0);c[c[e>>2]>>2]=J^1;t=b+616|0;v=c[t>>2]|0;s=b+620|0;if(!v)w=c[s>>2]|0;else{c[s>>2]=0;w=0}u=c[m>>2]|0;if((w|0)<(u|0)){y=b+624|0;x=c[y>>2]|0;if((x|0)<(u|0)){U=u+1-x&-2;w=(x>>1)+2&-2;w=(U|0)>(w|0)?U:w;if((w|0)>(2147483647-x|0)){U=va(1)|0;Ta(U|0,48,0)}U=w+x|0;c[y>>2]=U;v=Ud(v,U<<2)|0;c[t>>2]=v;if((v|0)==0?(c[(Oa()|0)>>2]|0)==12:0){U=va(1)|0;Ta(U|0,48,0)}}w=c[s>>2]|0;b:do if((w|0)<(u|0))while(1){v=v+(w<<2)|0;if(v)c[v>>2]=0;w=w+1|0;if((w|0)==(u|0))break b;v=c[t>>2]|0}while(0);c[s>>2]=u;u=c[m>>2]|0}if((u|0)>0){w=c[t>>2]|0;v=c[e>>2]|0;x=0;do{c[w+(x<<2)>>2]=c[v+(x<<2)>>2];x=x+1|0;u=c[m>>2]|0}while((x|0)<(u|0))}v=c[b+84>>2]|0;if((v|0)==1)if((u|0)>1){n=c[e>>2]|0;o=1;v=1;while(1){u=c[n+(o<<2)>>2]|0;p=c[l>>2]|0;w=c[p+(u>>1<<3)>>2]|0;c:do if((w|0)!=-1){x=(c[q>>2]|0)+(w<<2)|0;y=c[x>>2]|0;if(y>>>0>63){w=c[k>>2]|0;z=1;while(1){U=c[x+(z<<2)+4>>2]>>1;if((a[w+U>>0]|0)==0?(c[p+(U<<3)+4>>2]|0)>0:0)break;z=z+1|0;if((z|0)>=(y>>>5|0))break c}c[n+(v<<2)>>2]=u;v=v+1|0}}else{c[n+(v<<2)>>2]=u;v=v+1|0}while(0);o=o+1|0;p=c[m>>2]|0;if((o|0)>=(p|0)){n=p;break}}}else{n=u;o=1;v=1}else if((v|0)==2)if((u|0)>1){q=1;v=1;do{w=c[e>>2]|0;u=c[w+(q<<2)>>2]|0;if((c[(c[l>>2]|0)+(u>>1<<3)>>2]|0)!=-1){c[n>>2]=u;c[p+0>>2]=c[n+0>>2];if(!(Ub(b,p)|0)){u=c[e>>2]|0;w=u;u=c[u+(q<<2)>>2]|0;o=62}}else o=62;if((o|0)==62){o=0;c[w+(v<<2)>>2]=u;v=v+1|0}q=q+1|0;u=c[m>>2]|0}while((q|0)<(u|0));n=u;o=q}else{n=u;o=1;v=1}else{n=u;o=u;v=u}U=b+240|0;S=U;S=ne(c[S>>2]|0,c[S+4>>2]|0,n|0,((n|0)<0)<<31>>31|0)|0;c[U>>2]=S;c[U+4>>2]=F;o=o-v|0;if((o|0)>0){n=n-o|0;c[m>>2]=n}U=b+248|0;S=U;S=ne(c[S>>2]|0,c[S+4>>2]|0,n|0,((n|0)<0)<<31>>31|0)|0;c[U>>2]=S;c[U+4>>2]=F;if((n|0)==1)e=0;else{e=c[e>>2]|0;if((n|0)>2){b=c[l>>2]|0;m=2;o=1;do{o=(c[b+(c[e+(m<<2)>>2]>>1<<3)+4>>2]|0)>(c[b+(c[e+(o<<2)>>2]>>1<<3)+4>>2]|0)?m:o;m=m+1|0}while((m|0)<(n|0))}else o=1;S=e+(o<<2)|0;U=c[S>>2]|0;e=e+4|0;c[S>>2]=c[e>>2];c[e>>2]=U;e=c[(c[l>>2]|0)+(U>>1<<3)+4>>2]|0}c[f>>2]=e;if((c[s>>2]|0)>0)f=0;else{i=j;return}do{a[(c[k>>2]|0)+(c[(c[t>>2]|0)+(f<<2)>>2]>>1)>>0]=0;f=f+1|0}while((f|0)<(c[s>>2]|0));i=j;return}function Ub(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;e=i;n=c[d>>2]|0;l=b+396|0;q=c[l>>2]|0;k=b+544|0;s=(c[k>>2]|0)+(c[q+(n>>1<<3)>>2]<<2)|0;h=b+604|0;f=b+608|0;if(c[h>>2]|0)c[f>>2]=0;g=b+588|0;j=b+612|0;b=b+616|0;o=1;while(1){if(o>>>0<(c[s>>2]|0)>>>5>>>0){r=c[s+(o<<2)+4>>2]|0;p=r>>1;if((c[q+(p<<3)+4>>2]|0)!=0?(m=a[(c[g>>2]|0)+p>>0]|0,(m+ -1<<24>>24&255)>=2):0){s=c[f>>2]|0;t=(s|0)==(c[j>>2]|0);if(m<<24>>24==3?1:(c[q+(p<<3)>>2]|0)==-1){k=8;break}if(t){q=(s>>1)+2&-2;q=(q|0)<2?2:q;if((q|0)>(2147483647-s|0)){k=24;break}u=c[h>>2]|0;t=q+s|0;c[j>>2]=t;t=Ud(u,t<<3)|0;c[h>>2]=t;if((t|0)==0?(c[(Oa()|0)>>2]|0)==12:0){k=24;break}s=c[f>>2]|0}c[f>>2]=s+1;q=(c[h>>2]|0)+(s<<3)|0;if(q){u=q;c[u>>2]=o;c[u+4>>2]=n}c[d>>2]=r;s=c[l>>2]|0;n=r;q=s;s=(c[k>>2]|0)+(c[s+(p<<3)>>2]<<2)|0;o=0}}else{n=(c[g>>2]|0)+(n>>1)|0;if(!(a[n>>0]|0)){a[n>>0]=2;mc(b,d)}n=c[f>>2]|0;if(!n){f=1;k=34;break}u=n+ -1|0;n=c[h>>2]|0;o=c[n+(u<<3)>>2]|0;n=c[n+(u<<3)+4>>2]|0;c[d>>2]=n;q=c[l>>2]|0;s=(c[k>>2]|0)+(c[q+(n>>1<<3)>>2]<<2)|0;c[f>>2]=u}o=o+1|0}if((k|0)==8){if(t){k=(s>>1)+2&-2;k=(k|0)<2?2:k;if((k|0)>(2147483647-s|0)){u=va(1)|0;Ta(u|0,48,0)}t=c[h>>2]|0;u=k+s|0;c[j>>2]=u;u=Ud(t,u<<3)|0;c[h>>2]=u;if((u|0)==0?(c[(Oa()|0)>>2]|0)==12:0){u=va(1)|0;Ta(u|0,48,0)}s=c[f>>2]|0}j=s+1|0;c[f>>2]=j;k=(c[h>>2]|0)+(s<<3)|0;if(k){j=k;c[j>>2]=0;c[j+4>>2]=n;j=c[f>>2]|0}if((j|0)>0)k=0;else{u=0;i=e;return u|0}do{l=(c[g>>2]|0)+(c[(c[h>>2]|0)+(k<<3)+4>>2]>>1)|0;if(!(a[l>>0]|0)){a[l>>0]=3;mc(b,(c[h>>2]|0)+(k<<3)+4|0);j=c[f>>2]|0}k=k+1|0}while((k|0)<(j|0));f=0;i=e;return f|0}else if((k|0)==24)Ta(va(1)|0,48,0);else if((k|0)==34){i=e;return f|0}return 0}function Vb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;j=i;i=i+32|0;h=j+16|0;g=j+12|0;k=j+8|0;f=j;n=e+20|0;l=e+16|0;if((c[n>>2]|0)>0){m=0;do{a[(c[e>>2]|0)+(c[(c[l>>2]|0)+(m<<2)>>2]|0)>>0]=0;m=m+1|0}while((m|0)<(c[n>>2]|0))}if(c[l>>2]|0)c[n>>2]=0;m=c[d>>2]|0;c[k>>2]=m;c[g>>2]=m;c[h+0>>2]=c[g+0>>2];sc(e,h,0);l=(c[e>>2]|0)+m|0;if(!(a[l>>0]|0)){a[l>>0]=1;mc(e+16|0,k)}if(!(c[b+296>>2]|0)){i=j;return}d=m>>1;o=b+588|0;a[(c[o>>2]|0)+d>>0]=1;p=c[b+284>>2]|0;n=b+292|0;s=c[c[n>>2]>>2]|0;if((p|0)>(s|0)){k=b+280|0;l=b+396|0;m=e+16|0;b=b+544|0;do{p=p+ -1|0;r=c[(c[k>>2]|0)+(p<<2)>>2]|0;q=r>>1;if(a[(c[o>>2]|0)+q>>0]|0){s=c[l>>2]|0;t=c[s+(q<<3)>>2]|0;a:do if((t|0)==-1){r=r^1;c[f>>2]=r;c[g>>2]=r;c[h+0>>2]=c[g+0>>2];sc(e,h,0);r=(c[e>>2]|0)+r|0;if(!(a[r>>0]|0)){a[r>>0]=1;mc(m,f)}}else{r=(c[b>>2]|0)+(t<<2)|0;t=c[r>>2]|0;if(t>>>0>63){u=1;while(1){v=c[r+(u<<2)+4>>2]>>1;if((c[s+(v<<3)+4>>2]|0)>0){a[(c[o>>2]|0)+v>>0]=1;t=c[r>>2]|0}u=u+1|0;if((u|0)>=(t>>>5|0))break a;s=c[l>>2]|0}}}while(0);a[(c[o>>2]|0)+q>>0]=0;s=c[c[n>>2]>>2]|0}}while((p|0)>(s|0))}a[(c[o>>2]|0)+d>>0]=0;i=j;return}function Wb(b){b=b|0;var e=0,f=0,j=0,k=0,l=0,m=0,n=0.0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0;f=i;i=i+16|0;p=f+4|0;u=f;e=b+272|0;w=c[e>>2]|0;n=+h[b+496>>3]/+(w|0);k=b+544|0;l=b+268|0;v=c[l>>2]|0;c[u>>2]=k;c[p+0>>2]=c[u+0>>2];tc(v,w,p);p=c[e>>2]|0;if((p|0)>0){m=b+332|0;o=b+396|0;q=0;v=0;do{t=c[l>>2]|0;u=c[t+(q<<2)>>2]|0;w=c[k>>2]|0;r=w+(u<<2)|0;s=c[r>>2]|0;do if(s>>>0>95){x=c[w+(u+1<<2)>>2]|0;w=x>>1;x=(d[(c[m>>2]|0)+w>>0]|0)^x&1;z=a[528]|0;y=z&255;if(((x&255)<<24>>24==z<<24>>24&(y>>>1^1)|y&2&x|0)!=0?(z=c[(c[o>>2]|0)+(w<<3)>>2]|0,(z|0)!=-1&(z|0)==(u|0)):0){j=9;break}if((q|0)>=((p|0)/2|0|0)?!(+g[r+(s>>>5<<2)+4>>2]<n):0){j=9;break}Pb(b,u)}else j=9;while(0);if((j|0)==9){j=0;c[t+(v<<2)>>2]=u;v=v+1|0}q=q+1|0;p=c[e>>2]|0}while((q|0)<(p|0))}else{q=0;v=0}j=q-v|0;if((j|0)>0)c[e>>2]=p-j;if(!(+((c[b+556>>2]|0)>>>0)>+h[b+96>>3]*+((c[b+548>>2]|0)>>>0))){i=f;return}gb[c[(c[b>>2]|0)+8>>2]&31](b);i=f;return}function Xb(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;f=i;g=e+4|0;m=c[g>>2]|0;if((m|0)>0){j=b+544|0;h=b+332|0;k=0;l=0;do{u=c[e>>2]|0;p=c[u+(k<<2)>>2]|0;m=(c[j>>2]|0)+(p<<2)|0;o=c[m>>2]|0;do if(o>>>0>31){v=c[h>>2]|0;r=a[528]|0;q=r&255;w=q&2;q=q>>>1^1;s=o>>>5;t=0;do{x=c[m+(t<<2)+4>>2]|0;x=(d[v+(x>>1)>>0]|0)^x&1;t=t+1|0;if((x&255)<<24>>24==r<<24>>24&q|w&x){n=7;break}}while((t|0)<(s|0));if((n|0)==7){n=0;Pb(b,p);break}if(o>>>0>95){n=a[536]|0;q=o>>>5;p=2;do{r=m+(p<<2)+4|0;x=c[r>>2]|0;x=(d[(c[h>>2]|0)+(x>>1)>>0]|0)^x&1;w=n&255;if((x&255)<<24>>24==n<<24>>24&(w>>>1^1)|w&2&x){c[r>>2]=c[m+(q+ -1<<2)+4>>2];o=c[m>>2]|0;if(o&8){o=o>>>5;c[m+(o+ -1<<2)+4>>2]=c[m+(o<<2)+4>>2];o=c[m>>2]|0}o=o+ -32|0;c[m>>2]=o;p=p+ -1|0}p=p+1|0;q=o>>>5}while((p|0)<(q|0));p=c[e>>2]|0;u=p;p=c[p+(k<<2)>>2]|0;n=16}else n=16}else n=16;while(0);if((n|0)==16){n=0;c[u+(l<<2)>>2]=p;l=l+1|0}k=k+1|0;m=c[g>>2]|0}while((k|0)<(m|0))}else{k=0;l=0}e=k-l|0;if((e|0)<=0){i=f;return}c[g>>2]=m-e;i=f;return}function Yb(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;g=i;i=i+16|0;e=g+4|0;h=g;c[e>>2]=0;d=e+4|0;c[d>>2]=0;f=e+8|0;c[f>>2]=0;c[h>>2]=0;j=b+540|0;n=c[j>>2]|0;if((n|0)>0){l=b+380|0;k=b+332|0;m=0;do{if((a[(c[l>>2]|0)+m>>0]|0)!=0?(p=a[(c[k>>2]|0)+m>>0]|0,q=a[544]|0,o=q&255,((o>>>1^1)&p<<24>>24==q<<24>>24|p&2&o|0)!=0):0){nc(e,h);n=c[j>>2]|0}m=m+1|0;c[h>>2]=m}while((m|0)<(n|0))}uc(b+460|0,e);b=c[e>>2]|0;if(!b){i=g;return}c[d>>2]=0;Td(b);c[e>>2]=0;c[f>>2]=0;i=g;return}function Zb(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;d=i;f=b+492|0;if((a[f>>0]|0)!=0?(Mb(b)|0)==-1:0){f=b+284|0;g=b+516|0;if((c[f>>2]|0)==(c[g>>2]|0)){s=1;i=d;return s|0}j=b+520|0;s=j;r=c[s+4>>2]|0;if((r|0)>0|(r|0)==0&(c[s>>2]|0)>>>0>0){s=1;i=d;return s|0}Xb(b,b+268|0);if(a[b+536>>0]|0){Xb(b,b+256|0);l=b+564|0;k=b+568|0;if((c[k>>2]|0)>0){n=b+588|0;m=0;do{a[(c[n>>2]|0)+(c[(c[l>>2]|0)+(m<<2)>>2]|0)>>0]=1;m=m+1|0}while((m|0)<(c[k>>2]|0))}p=c[f>>2]|0;if((p|0)>0){m=c[b+280>>2]|0;n=c[b+588>>2]|0;q=0;o=0;do{r=c[m+(q<<2)>>2]|0;if(!(a[n+(r>>1)>>0]|0)){c[m+(o<<2)>>2]=r;p=c[f>>2]|0;o=o+1|0}q=q+1|0}while((q|0)<(p|0))}else{q=0;o=0}m=q-o|0;if((m|0)>0){p=p-m|0;c[f>>2]=p}c[b+512>>2]=p;a:do if((c[k>>2]|0)>0){o=b+588|0;m=0;do{a[(c[o>>2]|0)+(c[(c[l>>2]|0)+(m<<2)>>2]|0)>>0]=0;m=m+1|0;n=c[k>>2]|0}while((m|0)<(n|0));if((n|0)>0){n=b+580|0;o=b+584|0;m=b+576|0;p=0;while(1){r=c[n>>2]|0;if((r|0)==(c[o>>2]|0)){q=(r>>1)+2&-2;q=(q|0)<2?2:q;if((q|0)>(2147483647-r|0)){e=28;break}s=c[m>>2]|0;q=q+r|0;c[o>>2]=q;q=Ud(s,q<<2)|0;c[m>>2]=q;if((q|0)==0?(c[(Oa()|0)>>2]|0)==12:0){e=28;break}r=c[n>>2]|0}else q=c[m>>2]|0;s=q+(r<<2)|0;if(s){c[s>>2]=0;r=c[n>>2]|0}c[n>>2]=r+1;s=c[l>>2]|0;c[q+(r<<2)>>2]=c[s+(p<<2)>>2];p=p+1|0;if((p|0)>=(c[k>>2]|0))break a}if((e|0)==28)Ta(va(1)|0,48,0)}else e=21}else e=21;while(0);if((e|0)==21)s=c[l>>2]|0;if(s)c[k>>2]=0}if(+((c[b+556>>2]|0)>>>0)>+h[b+96>>3]*+((c[b+548>>2]|0)>>>0))gb[c[(c[b>>2]|0)+8>>2]&31](b);Yb(b);c[g>>2]=c[f>>2];r=b+224|0;s=b+232|0;r=ne(c[s>>2]|0,c[s+4>>2]|0,c[r>>2]|0,c[r+4>>2]|0)|0;s=j;c[s>>2]=r;c[s+4>>2]=F;s=1;i=d;return s|0}a[f>>0]=0;s=0;i=d;return s|0}function _b(b,e,f){b=b|0;e=e|0;f=f|0;var j=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0,_=0,$=0,aa=0,ba=0,ca=0,da=0,ea=0,fa=0.0,ga=0,ha=0,ia=0,ja=0.0,ka=0,la=0,ma=0,na=0,oa=0,pa=0,qa=0.0,ra=0,sa=0,ta=0.0;n=i;i=i+64|0;_=n;G=n+60|0;B=n+56|0;j=n+44|0;$=n+40|0;c[j>>2]=0;m=j+4|0;c[m>>2]=0;l=j+8|0;c[l>>2]=0;N=e+160|0;M=N;M=ne(c[M>>2]|0,c[M+4>>2]|0,1,0)|0;c[N>>2]=M;c[N+4>>2]=F;N=(f|0)<0;M=e+680|0;L=e+664|0;K=e+672|0;q=e+296|0;w=e+272|0;o=e+284|0;I=e+640|0;E=e+308|0;D=e+304|0;r=e+332|0;H=e+292|0;ba=e+168|0;t=e+396|0;v=e+280|0;J=e+184|0;C=e+192|0;u=e+48|0;U=e+504|0;Y=e+56|0;aa=e+496|0;ca=e+656|0;O=e+144|0;P=e+648|0;Q=e+128|0;R=e+44|0;T=e+200|0;V=e+208|0;W=e+224|0;X=e+216|0;s=e+232|0;Z=e+540|0;p=e+292|0;x=e+544|0;z=e+276|0;y=e+268|0;A=e+268|0;da=0;a:while(1){ea=N|(da|0)<(f|0);while(1){ga=Mb(e)|0;if((ga|0)!=-1)break;if(!ea){ga=41;break a}if(a[M>>0]|0){ga=41;break a}ga=L;ha=c[ga+4>>2]|0;if((ha|0)>=0?(sa=C,ra=c[sa+4>>2]|0,!(ra>>>0<ha>>>0|((ra|0)==(ha|0)?(c[sa>>2]|0)>>>0<(c[ga>>2]|0)>>>0:0))):0){ga=41;break a}ga=K;ha=c[ga+4>>2]|0;if((ha|0)>=0?(sa=J,ra=c[sa+4>>2]|0,!(ra>>>0<ha>>>0|((ra|0)==(ha|0)?(c[sa>>2]|0)>>>0<(c[ga>>2]|0)>>>0:0))):0){ga=41;break a}if((c[q>>2]|0)==0?!(Zb(e)|0):0){ga=50;break a}if(+((c[w>>2]|0)-(c[o>>2]|0)|0)>=+h[I>>3])Wb(e);while(1){ga=c[q>>2]|0;if((ga|0)>=(c[E>>2]|0)){ga=59;break}ka=c[(c[D>>2]|0)+(ga<<2)>>2]|0;ha=d[(c[r>>2]|0)+(ka>>1)>>0]|0;sa=ha^ka&1;ia=sa&255;pa=a[528]|0;ra=pa&255;if(!(ia<<24>>24==pa<<24>>24&(ra>>>1^1)|ra&2&sa)){ga=56;break}c[G>>2]=c[o>>2];nc(H,G)}if((ga|0)==56){ga=0;ra=a[536]|0;sa=ra&255;if((sa>>>1^1)&ia<<24>>24==ra<<24>>24|ha&2&sa){ga=57;break a}if((ka|0)==-2)ga=59}if((ga|0)==59){sa=ba;sa=ne(c[sa>>2]|0,c[sa+4>>2]|0,1,0)|0;ka=ba;c[ka>>2]=sa;c[ka+4>>2]=F;ka=Sb(e)|0;if((ka|0)==-2){ga=60;break a}}c[_>>2]=c[o>>2];nc(H,_);sa=ka>>1;a[(c[r>>2]|0)+sa>>0]=(ka&1^1)&255^1;ra=c[q>>2]|0;sa=(c[t>>2]|0)+(sa<<3)|0;c[sa>>2]=-1;c[sa+4>>2]=ra;sa=c[o>>2]|0;c[o>>2]=sa+1;c[(c[v>>2]|0)+(sa<<2)>>2]=ka}ra=C;ra=ne(c[ra>>2]|0,c[ra+4>>2]|0,1,0)|0;sa=C;c[sa>>2]=ra;c[sa+4>>2]=F;da=da+1|0;if(!(c[q>>2]|0)){ga=5;break}if(c[j>>2]|0)c[m>>2]=0;Tb(e,ga,j,B);Rb(e,c[B>>2]|0);if((c[m>>2]|0)==1){ra=c[c[j>>2]>>2]|0;sa=ra>>1;a[(c[r>>2]|0)+sa>>0]=(ra&1^1)&255^1;pa=c[q>>2]|0;sa=(c[t>>2]|0)+(sa<<3)|0;c[sa>>2]=-1;c[sa+4>>2]=pa;sa=c[o>>2]|0;c[o>>2]=sa+1;c[(c[v>>2]|0)+(sa<<2)>>2]=ra}else{ea=pc(x,j,1)|0;ga=c[w>>2]|0;if((ga|0)==(c[z>>2]|0)){ha=(ga>>1)+2&-2;ha=(ha|0)<2?2:ha;if((ha|0)>(2147483647-ga|0)){ga=14;break}ra=c[y>>2]|0;sa=ha+ga|0;c[z>>2]=sa;sa=Ud(ra,sa<<2)|0;c[y>>2]=sa;if((sa|0)==0?(c[(Oa()|0)>>2]|0)==12:0){ga=14;break}ga=c[w>>2]|0}c[w>>2]=ga+1;ga=(c[y>>2]|0)+(ga<<2)|0;if(ga)c[ga>>2]=ea;Nb(e,ea);ia=c[x>>2]|0;sa=ia+(ea<<2)|0;fa=+h[aa>>3];sa=sa+((c[sa>>2]|0)>>>5<<2)+4|0;ta=fa+ +g[sa>>2];g[sa>>2]=ta;if(ta>1.0e20){ha=c[w>>2]|0;if((ha|0)>0){ga=c[A>>2]|0;ka=0;do{sa=ia+(c[ga+(ka<<2)>>2]<<2)|0;sa=sa+((c[sa>>2]|0)>>>5<<2)+4|0;g[sa>>2]=+g[sa>>2]*1.0e-20;ka=ka+1|0}while((ka|0)!=(ha|0))}h[aa>>3]=fa*1.0e-20}ra=c[c[j>>2]>>2]|0;sa=ra>>1;a[(c[r>>2]|0)+sa>>0]=(ra&1^1)&255^1;pa=c[q>>2]|0;sa=(c[t>>2]|0)+(sa<<3)|0;c[sa>>2]=ea;c[sa+4>>2]=pa;sa=c[o>>2]|0;c[o>>2]=sa+1;c[(c[v>>2]|0)+(sa<<2)>>2]=ra}h[U>>3]=1.0/+h[u>>3]*+h[U>>3];h[aa>>3]=1.0/+h[Y>>3]*+h[aa>>3];sa=(c[ca>>2]|0)+ -1|0;c[ca>>2]=sa;if(sa)continue;fa=+h[O>>3]*+h[P>>3];h[P>>3]=fa;c[ca>>2]=~~fa;fa=+h[Q>>3]*+h[I>>3];h[I>>3]=fa;if((c[R>>2]|0)<=0)continue;ga=c[C>>2]|0;ea=c[T>>2]|0;oa=c[q>>2]|0;if(!oa)ha=o;else ha=c[p>>2]|0;ha=c[ha>>2]|0;na=c[V>>2]|0;ma=c[W>>2]|0;la=c[X>>2]|0;ka=s;ia=c[ka>>2]|0;ka=c[ka+4>>2]|0;ja=+(c[Z>>2]|0);qa=1.0/ja;if((oa|0)<0)ta=0.0;else{pa=0;ta=0.0;while(1){if(!pa)ra=0;else ra=c[(c[p>>2]|0)+(pa+ -1<<2)>>2]|0;if((pa|0)==(oa|0))sa=o;else sa=(c[p>>2]|0)+(pa<<2)|0;ta=ta+ +S(+qa,+(+(pa|0)))*+((c[sa>>2]|0)-ra|0);if((pa|0)==(oa|0))break;else pa=pa+1|0}}c[_>>2]=ga;c[_+4>>2]=ea-ha;c[_+8>>2]=na;c[_+12>>2]=ma;c[_+16>>2]=~~fa;c[_+20>>2]=la;sa=_+24|0;h[k>>3]=(+(ia>>>0)+4294967296.0*+(ka>>>0))/+(la|0);c[sa>>2]=c[k>>2];c[sa+4>>2]=c[k+4>>2];sa=_+32|0;h[k>>3]=ta/ja*100.0;c[sa>>2]=c[k>>2];c[sa+4>>2]=c[k+4>>2];La(1832,_|0)|0}if((ga|0)==5)a[b>>0]=a[536]|0;else if((ga|0)==14)Ta(va(1)|0,48,0);else if((ga|0)==41){fa=+(c[Z>>2]|0);ja=1.0/fa;r=c[q>>2]|0;if((r|0)<0)qa=0.0;else{q=0;qa=0.0;while(1){if(!q)s=0;else s=c[(c[p>>2]|0)+(q+ -1<<2)>>2]|0;if((q|0)==(r|0))t=o;else t=(c[p>>2]|0)+(q<<2)|0;qa=qa+ +S(+ja,+(+(q|0)))*+((c[t>>2]|0)-s|0);if((q|0)==(r|0))break;else q=q+1|0}}h[e+528>>3]=qa/fa;Rb(e,0);a[b>>0]=a[544]|0}else if((ga|0)==50)a[b>>0]=a[536]|0;else if((ga|0)==57){c[$>>2]=ka^1;sa=e+16|0;c[_+0>>2]=c[$+0>>2];Vb(e,_,sa);a[b>>0]=a[536]|0}else if((ga|0)==60)a[b>>0]=a[528]|0;b=c[j>>2]|0;if(!b){i=n;return}c[m>>2]=0;Td(b);c[j>>2]=0;c[l>>2]=0;i=n;return}function $b(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0.0,w=0,x=0,y=0,z=0.0,A=0,B=0;f=i;i=i+16|0;j=f;e=d+4|0;if(c[e>>2]|0)c[d+8>>2]=0;g=d+36|0;k=d+32|0;if((c[g>>2]|0)>0){l=d+16|0;m=0;do{a[(c[l>>2]|0)+(c[(c[k>>2]|0)+(m<<2)>>2]|0)>>0]=0;m=m+1|0}while((m|0)<(c[g>>2]|0))}if(c[k>>2]|0)c[g>>2]=0;k=d+492|0;if(!(a[k>>0]|0)){a[b>>0]=a[536]|0;i=f;return}l=d+152|0;y=l;y=ne(c[y>>2]|0,c[y+4>>2]|0,1,0)|0;c[l>>2]=y;c[l+4>>2]=F;z=+h[d+120>>3]*+(c[d+208>>2]|0);l=d+640|0;h[l>>3]=z;v=+(c[d+104>>2]|0);if(z<v)h[l>>3]=v;w=c[d+136>>2]|0;h[d+648>>3]=+(w|0);c[d+656>>2]=w;w=a[544]|0;l=d+44|0;if((c[l>>2]|0)>0){Ka(2288)|0;Ka(2368)|0;Ka(2448)|0;Ka(2528)|0;o=a[544]|0}else o=w;n=d+192|0;m=d+184|0;y=o&255;a:do if((y>>>1^1)&w<<24>>24==o<<24>>24|w&2&y){q=d+80|0;t=d+112|0;p=d+108|0;o=d+680|0;r=d+664|0;s=d+672|0;u=0;while(1){v=+h[t>>3];if(!(a[q>>0]|0))v=+S(+v,+(+(u|0)));else{y=u+1|0;if((u|0)>0){x=0;w=1;do{x=x+1|0;w=w<<1|1}while((w|0)<(y|0));y=w+ -1|0}else{x=0;y=0}if((y|0)!=(u|0)){w=u;do{A=y>>1;x=x+ -1|0;w=(w|0)%(A|0)|0;y=A+ -1|0}while((y|0)!=(w|0))}v=+S(+v,+(+(x|0)))}_b(j,d,~~(v*+(c[p>>2]|0)));w=a[j>>0]|0;if(a[o>>0]|0)break a;y=r;x=c[y+4>>2]|0;if((x|0)>=0?(A=n,B=c[A+4>>2]|0,!(B>>>0<x>>>0|((B|0)==(x|0)?(c[A>>2]|0)>>>0<(c[y>>2]|0)>>>0:0))):0)break a;y=s;x=c[y+4>>2]|0;if((x|0)>=0?(B=m,A=c[B+4>>2]|0,!(A>>>0<x>>>0|((A|0)==(x|0)?(c[B>>2]|0)>>>0<(c[y>>2]|0)>>>0:0))):0)break a;A=a[544]|0;B=A&255;if(!((B>>>1^1)&w<<24>>24==A<<24>>24|w&2&B))break;else u=u+1|0}}while(0);if((c[l>>2]|0)>0)Ka(2528)|0;A=a[528]|0;B=A&255;j=w&2;if(!((B>>>1^1)&w<<24>>24==A<<24>>24|j&B)){A=a[536]|0;B=A&255;if(((B>>>1^1)&w<<24>>24==A<<24>>24|j&B|0)!=0?(c[g>>2]|0)==0:0)a[k>>0]=0}else{g=d+540|0;jc(e,c[g>>2]|0);if((c[g>>2]|0)>0){j=d+332|0;k=0;do{a[(c[e>>2]|0)+k>>0]=a[(c[j>>2]|0)+k>>0]|0;k=k+1|0}while((k|0)<(c[g>>2]|0))}}Rb(d,0);a[b>>0]=w;i=f;return}function ac(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;f=i;h=b+412|0;vc(h);k=b+540|0;if((c[k>>2]|0)>0){j=b+544|0;g=0;do{l=g<<1;n=c[h>>2]|0;m=n+(l*12|0)+4|0;if((c[m>>2]|0)>0){p=n+(l*12|0)|0;o=0;do{s=(c[p>>2]|0)+(o<<3)|0;n=c[s>>2]|0;q=c[j>>2]|0;r=q+(n<<2)|0;if(!(c[r>>2]&16)){t=wc(e,r)|0;c[s>>2]=t;c[r>>2]=c[r>>2]|16;c[q+(n+1<<2)>>2]=t}else c[s>>2]=c[q+(n+1<<2)>>2];o=o+1|0}while((o|0)<(c[m>>2]|0));m=c[h>>2]|0}else m=n;n=l|1;l=m+(n*12|0)+4|0;if((c[l>>2]|0)>0){r=m+(n*12|0)|0;q=0;do{m=(c[r>>2]|0)+(q<<3)|0;p=c[m>>2]|0;o=c[j>>2]|0;n=o+(p<<2)|0;if(!(c[n>>2]&16)){t=wc(e,n)|0;c[m>>2]=t;c[n>>2]=c[n>>2]|16;c[o+(p+1<<2)>>2]=t}else c[m>>2]=c[o+(p+1<<2)>>2];q=q+1|0}while((q|0)<(c[l>>2]|0))}g=g+1|0}while((g|0)<(c[k>>2]|0))}g=b+284|0;if((c[g>>2]|0)>0){l=b+280|0;k=b+396|0;j=b+544|0;h=b+332|0;m=0;do{r=c[k>>2]|0;p=r+(c[(c[l>>2]|0)+(m<<2)>>2]>>1<<3)|0;q=c[p>>2]|0;do if((q|0)!=-1){t=c[j>>2]|0;s=t+(q<<2)|0;o=(c[s>>2]&16|0)==0;if(o){u=c[t+(q+1<<2)>>2]|0;n=u>>1;u=(d[(c[h>>2]|0)+n>>0]|0)^u&1;w=a[528]|0;v=w&255;if(!((u&255)<<24>>24==w<<24>>24&(v>>>1^1)|v&2&u))break;w=c[r+(n<<3)>>2]|0;if(!((w|0)!=-1&(w|0)==(q|0)))break;if(o){w=wc(e,s)|0;c[p>>2]=w;c[s>>2]=c[s>>2]|16;c[t+(q+1<<2)>>2]=w;break}}c[p>>2]=c[t+(q+1<<2)>>2]}while(0);m=m+1|0}while((m|0)<(c[g>>2]|0))}g=b+272|0;n=c[g>>2]|0;if((n|0)>0){j=b+268|0;h=b+544|0;m=c[j>>2]|0;k=0;l=0;do{p=m+(k<<2)|0;o=c[p>>2]|0;r=c[h>>2]|0;q=r+(o<<2)|0;s=c[q>>2]|0;if((s&3|0)!=1){if(!(s&16)){n=wc(e,q)|0;c[p>>2]=n;c[q>>2]=c[q>>2]|16;c[r+(o+1<<2)>>2]=n;n=c[j>>2]|0;m=n;n=c[n+(k<<2)>>2]|0}else{n=c[r+(o+1<<2)>>2]|0;c[p>>2]=n}c[m+(l<<2)>>2]=n;n=c[g>>2]|0;l=l+1|0}k=k+1|0}while((k|0)<(n|0))}else{k=0;l=0}h=k-l|0;if((h|0)>0)c[g>>2]=n-h;g=b+260|0;m=c[g>>2]|0;if((m|0)>0){h=b+256|0;b=b+544|0;l=c[h>>2]|0;j=0;k=0;do{n=l+(j<<2)|0;p=c[n>>2]|0;o=c[b>>2]|0;r=o+(p<<2)|0;q=c[r>>2]|0;if((q&3|0)!=1){if(!(q&16)){m=wc(e,r)|0;c[n>>2]=m;c[r>>2]=c[r>>2]|16;c[o+(p+1<<2)>>2]=m;m=c[h>>2]|0;l=m;m=c[m+(j<<2)>>2]|0}else{m=c[o+(p+1<<2)>>2]|0;c[n>>2]=m}c[l+(k<<2)>>2]=m;m=c[g>>2]|0;k=k+1|0}j=j+1|0}while((j|0)<(m|0))}else{j=0;k=0}e=j-k|0;if((e|0)<=0){i=f;return}c[g>>2]=m-e;i=f;return}function bc(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;g=i;i=i+32|0;j=g;d=g+8|0;e=b+548|0;f=b+556|0;h=(c[e>>2]|0)-(c[f>>2]|0)|0;c[d+0>>2]=0;c[d+4>>2]=0;c[d+8>>2]=0;c[d+12>>2]=0;gc(d,h);h=d+16|0;a[h>>0]=0;ac(b,d);if((c[b+44>>2]|0)>1){k=c[d+4>>2]<<2;c[j>>2]=c[e>>2]<<2;c[j+4>>2]=k;La(1888,j|0)|0}a[b+560>>0]=a[h>>0]|0;h=b+544|0;j=c[h>>2]|0;if(j)Td(j);c[h>>2]=c[d>>2];c[e>>2]=c[d+4>>2];c[b+552>>2]=c[d+8>>2];c[f>>2]=c[d+12>>2];i=g;return}function cc(){var d=0,e=0,f=0;d=i;i=i+16|0;e=d;a[528]=0;a[536]=1;a[544]=2;xb(552,608,624,2136,2144);c[138]=2168;h[72]=0.0;h[73]=1.0;a[592]=0;a[593]=0;b[297]=b[e+0>>1]|0;b[298]=b[e+2>>1]|0;b[299]=b[e+4>>1]|0;h[75]=.95;xb(664,720,736,2136,2144);c[166]=2168;h[86]=0.0;h[87]=1.0;a[704]=0;a[705]=0;b[353]=b[e+0>>1]|0;b[354]=b[e+2>>1]|0;b[355]=b[e+4>>1]|0;h[89]=.999;xb(776,832,848,2136,2144);c[194]=2168;h[100]=0.0;h[101]=1.0;a[816]=1;a[817]=1;b[409]=b[e+0>>1]|0;b[410]=b[e+2>>1]|0;b[411]=b[e+4>>1]|0;h[103]=0.0;xb(936,992,1008,2136,2144);c[234]=2168;h[120]=0.0;h[121]=v;a[976]=0;a[977]=0;b[489]=b[e+0>>1]|0;b[490]=b[e+2>>1]|0;b[491]=b[e+4>>1]|0;h[123]=91648253.0;xb(1048,1080,1096,2136,2016);c[262]=280;f=1068|0;c[f>>2]=0;c[f+4>>2]=2;c[269]=2;xb(1160,1192,1208,2136,2016);c[290]=280;f=1180|0;c[f>>2]=0;c[f+4>>2]=2;c[297]=2;xb(1272,1296,1312,2136,1992);c[318]=160;a[1292]=0;xb(1344,1368,1376,2136,1992);c[336]=160;a[1364]=1;xb(1408,1440,1448,2136,2016);c[352]=280;f=1428|0;c[f>>2]=1;c[f+4>>2]=2147483647;c[359]=100;xb(1480,1536,1544,2136,2144);c[370]=2168;h[188]=1.0;h[189]=v;a[1520]=0;a[1521]=0;b[761]=b[e+0>>1]|0;b[762]=b[e+2>>1]|0;b[763]=b[e+4>>1]|0;h[191]=2.0;xb(1584,1640,1648,2136,2144);c[396]=2168;h[201]=0.0;h[202]=v;a[1624]=0;a[1625]=0;b[813]=b[e+0>>1]|0;b[814]=b[e+2>>1]|0;b[815]=b[e+4>>1]|0;h[204]=.2;xb(1728,1760,1776,2136,2016);c[432]=280;e=1748|0;c[e>>2]=0;c[e+4>>2]=2147483647;c[439]=0;i=d;return}function dc(a){a=a|0;var b=0;b=i;pd(a);i=b;return}function ec(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,q=0.0,r=0.0;e=i;i=i+16|0;j=e;g=e+8|0;if((a[d>>0]|0)!=45){o=0;i=e;return o|0}m=d+1|0;f=b+4|0;k=c[f>>2]|0;l=a[k>>0]|0;a:do if(l<<24>>24){n=0;while(1){o=n;n=n+1|0;if((a[m>>0]|0)!=l<<24>>24){b=0;break}l=a[k+n>>0]|0;m=d+(o+2)|0;if(!(l<<24>>24))break a}i=e;return b|0}while(0);if((a[m>>0]|0)!=61){o=0;i=e;return o|0}k=m+1|0;q=+ce(k,g);if(!(c[g>>2]|0)){o=0;i=e;return o|0}r=+h[b+32>>3];if(q>=r?(a[b+41>>0]|0)==0|q!=r:0){o=c[p>>2]|0;n=c[f>>2]|0;c[j>>2]=k;c[j+4>>2]=n;Za(o|0,2024,j|0)|0;ab(1)}r=+h[b+24>>3];if(q<=r?(a[b+40>>0]|0)==0|q!=r:0){o=c[p>>2]|0;n=c[f>>2]|0;c[j>>2]=k;c[j+4>>2]=n;Za(o|0,2080,j|0)|0;ab(1)}h[b+48>>3]=q;o=1;i=e;return o|0}function fc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,l=0.0,m=0,n=0.0,o=0.0,q=0;e=i;i=i+48|0;f=e;g=c[p>>2]|0;q=c[b+16>>2]|0;m=(a[b+40>>0]|0)!=0?91:40;o=+h[b+24>>3];n=+h[b+32>>3];j=(a[b+41>>0]|0)!=0?93:41;l=+h[b+48>>3];c[f>>2]=c[b+4>>2];c[f+4>>2]=q;c[f+8>>2]=m;m=f+12|0;h[k>>3]=o;c[m>>2]=c[k>>2];c[m+4>>2]=c[k+4>>2];m=f+20|0;h[k>>3]=n;c[m>>2]=c[k>>2];c[m+4>>2]=c[k+4>>2];c[f+28>>2]=j;j=f+32|0;h[k>>3]=l;c[j>>2]=c[k>>2];c[j+4>>2]=c[k+4>>2];Za(g|0,2232,f|0)|0;if(!d){i=e;return}c[f>>2]=c[b+8>>2];Za(g|0,2e3,f|0)|0;Sa(10,g|0)|0;i=e;return}function gc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0;d=i;e=a+8|0;f=c[e>>2]|0;if(f>>>0<b>>>0)h=f;else{i=d;return}while(1){if(h>>>0>=b>>>0)break;h=((h>>>3)+2+(h>>>1)&-2)+h|0;c[e>>2]=h;if(h>>>0<=f>>>0){g=4;break}}if((g|0)==4)Ta(va(1)|0,48,0);e=Ud(c[a>>2]|0,h<<2)|0;if((e|0)==0?(c[(Oa()|0)>>2]|0)==12:0)Ta(va(1)|0,48,0);c[a>>2]=e;i=d;return}function hc(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0;b=i;e=a+32|0;d=c[e>>2]|0;if(d){c[a+36>>2]=0;Td(d);c[e>>2]=0;c[a+40>>2]=0}e=a+16|0;d=c[e>>2]|0;if(d){c[a+20>>2]=0;Td(d);c[e>>2]=0;c[a+24>>2]=0}e=c[a>>2]|0;if(!e){i=b;return}d=a+4|0;g=c[d>>2]|0;if((g|0)>0){f=0;do{j=e+(f*12|0)|0;h=c[j>>2]|0;if(h){c[e+(f*12|0)+4>>2]=0;Td(h);c[j>>2]=0;c[e+(f*12|0)+8>>2]=0;e=c[a>>2]|0;g=c[d>>2]|0}f=f+1|0}while((f|0)<(g|0))}c[d>>2]=0;Td(e);c[a>>2]=0;c[a+8>>2]=0;i=b;return}function ic(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;f=i;i=i+16|0;e=f+4|0;d=f;l=c[b>>2]|0;h=l+1|0;g=a+4|0;if((c[g>>2]|0)<(h|0)){k=a+8|0;j=c[k>>2]|0;if((j|0)<(h|0)){m=l+2-j&-2;l=(j>>1)+2&-2;l=(m|0)>(l|0)?m:l;if((l|0)>(2147483647-j|0)){m=va(1)|0;Ta(m|0,48,0)}n=c[a>>2]|0;m=l+j|0;c[k>>2]=m;m=Ud(n,m*12|0)|0;c[a>>2]=m;if((m|0)==0?(c[(Oa()|0)>>2]|0)==12:0){n=va(1)|0;Ta(n|0,48,0)}}k=c[g>>2]|0;if((k|0)<(h|0)){j=c[a>>2]|0;do{l=j+(k*12|0)|0;if(l){c[l>>2]=0;c[j+(k*12|0)+4>>2]=0;c[j+(k*12|0)+8>>2]=0}k=k+1|0}while((k|0)!=(h|0))}c[g>>2]=h;l=c[b>>2]|0}g=c[a>>2]|0;if(!(c[g+(l*12|0)>>2]|0)){m=l;n=a+16|0;c[d>>2]=m;c[e+0>>2]=c[d+0>>2];sc(n,e,0);i=f;return}c[g+(l*12|0)+4>>2]=0;m=c[b>>2]|0;n=a+16|0;c[d>>2]=m;c[e+0>>2]=c[d+0>>2];sc(n,e,0);i=f;return}function jc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;f=i;e=b+4|0;if((c[e>>2]|0)>=(d|0)){i=f;return}h=b+8|0;g=c[h>>2]|0;if((g|0)<(d|0)){k=d+1-g&-2;j=(g>>1)+2&-2;j=(k|0)>(j|0)?k:j;if((j|0)>(2147483647-g|0)){k=va(1)|0;Ta(k|0,48,0)}l=c[b>>2]|0;k=j+g|0;c[h>>2]=k;k=Ud(l,k)|0;c[b>>2]=k;if((k|0)==0?(c[(Oa()|0)>>2]|0)==12:0){l=va(1)|0;Ta(l|0,48,0)}}g=c[e>>2]|0;if((g|0)<(d|0)){b=c[b>>2]|0;do{h=b+g|0;if(h)a[h>>0]=0;g=g+1|0}while((g|0)!=(d|0))}c[e>>2]=d;i=f;return}function kc(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;h=i;g=d+1|0;f=b+4|0;if((c[f>>2]|0)>=(g|0)){l=c[b>>2]|0;l=l+d|0;a[l>>0]=e;i=h;return}k=b+8|0;j=c[k>>2]|0;if((j|0)<(g|0)){m=d+2-j&-2;l=(j>>1)+2&-2;l=(m|0)>(l|0)?m:l;if((l|0)>(2147483647-j|0)){m=va(1)|0;Ta(m|0,48,0)}n=c[b>>2]|0;m=l+j|0;c[k>>2]=m;m=Ud(n,m)|0;c[b>>2]=m;if((m|0)==0?(c[(Oa()|0)>>2]|0)==12:0){n=va(1)|0;Ta(n|0,48,0)}}j=c[f>>2]|0;if((j|0)<(g|0))do{k=(c[b>>2]|0)+j|0;if(k)a[k>>0]=0;j=j+1|0}while((j|0)!=(g|0));c[f>>2]=g;n=c[b>>2]|0;n=n+d|0;a[n>>0]=e;i=h;return}function lc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0;d=i;i=i+16|0;g=d;c[g>>2]=b;j=a+12|0;f=b+1|0;e=a+16|0;if((c[e>>2]|0)<(f|0)){l=a+20|0;k=c[l>>2]|0;if((k|0)<(f|0)){n=b+2-k&-2;m=(k>>1)+2&-2;m=(n|0)>(m|0)?n:m;if((m|0)>(2147483647-k|0)){n=va(1)|0;Ta(n|0,48,0)}o=c[j>>2]|0;n=m+k|0;c[l>>2]=n;n=Ud(o,n<<2)|0;c[j>>2]=n;if((n|0)==0?(c[(Oa()|0)>>2]|0)==12:0){o=va(1)|0;Ta(o|0,48,0)}}k=c[e>>2]|0;if((f|0)>(k|0))ke((c[j>>2]|0)+(k<<2)|0,-1,f-k<<2|0)|0;c[e>>2]=f}c[(c[j>>2]|0)+(b<<2)>>2]=c[a+4>>2];nc(a,g);e=c[j>>2]|0;g=c[e+(b<<2)>>2]|0;b=c[a>>2]|0;f=c[b+(g<<2)>>2]|0;if(!g){n=0;o=b+(n<<2)|0;c[o>>2]=f;o=e+(f<<2)|0;c[o>>2]=n;i=d;return}a=a+28|0;while(1){j=g;g=g+ -1>>1;k=b+(g<<2)|0;l=c[k>>2]|0;o=c[c[a>>2]>>2]|0;if(!(+h[o+(f<<3)>>3]>+h[o+(l<<3)>>3])){a=14;break}c[b+(j<<2)>>2]=l;c[e+(c[k>>2]<<2)>>2]=j;if(!g){j=0;a=14;break}}if((a|0)==14){o=b+(j<<2)|0;c[o>>2]=f;o=e+(f<<2)|0;c[o>>2]=j;i=d;return}}function mc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;d=i;e=a+4|0;f=c[e>>2]|0;g=a+8|0;h=c[g>>2]|0;if((f|0)==(h|0)&(h|0)<(f+1|0)){h=(f>>1)+2&-2;h=(h|0)<2?2:h;if((h|0)>(2147483647-f|0)){h=va(1)|0;Ta(h|0,48,0)}j=c[a>>2]|0;f=h+f|0;c[g>>2]=f;f=Ud(j,f<<2)|0;c[a>>2]=f;if((f|0)==0?(c[(Oa()|0)>>2]|0)==12:0){j=va(1)|0;Ta(j|0,48,0)}}else f=c[a>>2]|0;j=c[e>>2]|0;c[e>>2]=j+1;e=f+(j<<2)|0;if(!e){i=d;return}c[e>>2]=c[b>>2];i=d;return}function nc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;d=i;e=a+4|0;f=c[e>>2]|0;g=a+8|0;h=c[g>>2]|0;if((f|0)==(h|0)&(h|0)<(f+1|0)){h=(f>>1)+2&-2;h=(h|0)<2?2:h;if((h|0)>(2147483647-f|0)){h=va(1)|0;Ta(h|0,48,0)}j=c[a>>2]|0;f=h+f|0;c[g>>2]=f;f=Ud(j,f<<2)|0;c[a>>2]=f;if((f|0)==0?(c[(Oa()|0)>>2]|0)==12:0){j=va(1)|0;Ta(j|0,48,0)}}else f=c[a>>2]|0;j=c[e>>2]|0;c[e>>2]=j+1;e=f+(j<<2)|0;if(!e){i=d;return}c[e>>2]=c[b>>2];i=d;return}function oc(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;e=i;i=i+16|0;f=e+2|0;h=e+1|0;g=e;if((d|0)<16){g=d+ -1|0;if((g|0)>0)h=0;else{i=e;return}do{f=h;h=h+1|0;if((h|0)<(d|0)){k=f;j=h;do{k=(c[b+(j<<2)>>2]|0)<(c[b+(k<<2)>>2]|0)?j:k;j=j+1|0}while((j|0)!=(d|0))}else k=f;n=b+(f<<2)|0;o=c[n>>2]|0;p=b+(k<<2)|0;c[n>>2]=c[p>>2];c[p>>2]=o}while((h|0)!=(g|0));i=e;return}j=c[b+(((d|0)/2|0)<<2)>>2]|0;m=-1;n=d;while(1){do{m=m+1|0;l=b+(m<<2)|0;k=c[l>>2]|0}while((k|0)<(j|0));do{n=n+ -1|0;o=b+(n<<2)|0;p=c[o>>2]|0}while((j|0)<(p|0));if((m|0)>=(n|0))break;c[l>>2]=p;c[o>>2]=k}a[f+0>>0]=a[h+0>>0]|0;oc(b,m,f);p=d-m|0;a[f+0>>0]=a[g+0>>0]|0;oc(l,p,f);i=e;return}function pc(a,b,e){a=a|0;b=b|0;e=e|0;var f=0,h=0,j=0,k=0,l=0,m=0;f=i;k=e&1;j=d[a+16>>0]|0|k;h=b+4|0;l=((j+(c[h>>2]|0)<<2)+4|0)>>>2;m=a+4|0;gc(a,l+(c[m>>2]|0)|0);e=c[m>>2]|0;l=l+e|0;c[m>>2]=l;if(l>>>0<e>>>0)Ta(va(1)|0,48,0);a=(c[a>>2]|0)+(e<<2)|0;if(!a){i=f;return e|0}j=j<<3|k<<2;c[a>>2]=c[a>>2]&-32|j;j=c[h>>2]<<5|j;c[a>>2]=j;if((c[h>>2]|0)>0){j=c[b>>2]|0;b=0;do{c[a+(b<<2)+4>>2]=c[j+(b<<2)>>2];b=b+1|0}while((b|0)<(c[h>>2]|0));j=c[a>>2]|0}if(!(j&8)){i=f;return e|0}h=j>>>5;if(j&4){g[a+(h<<2)+4>>2]=0.0;i=f;return e|0}if(!h){h=0;j=0}else{j=0;b=0;do{j=1<<((c[a+(b<<2)+4>>2]|0)>>>1&31)|j;b=b+1|0}while((b|0)<(h|0))}c[a+(h<<2)+4>>2]=j;i=f;return e|0}function qc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;d=i;e=a+4|0;f=c[e>>2]|0;g=a+8|0;h=c[g>>2]|0;if((f|0)==(h|0)&(h|0)<(f+1|0)){h=(f>>1)+2&-2;h=(h|0)<2?2:h;if((h|0)>(2147483647-f|0)){h=va(1)|0;Ta(h|0,48,0)}j=c[a>>2]|0;f=h+f|0;c[g>>2]=f;f=Ud(j,f<<3)|0;c[a>>2]=f;if((f|0)==0?(c[(Oa()|0)>>2]|0)==12:0){j=va(1)|0;Ta(j|0,48,0)}}else f=c[a>>2]|0;j=c[e>>2]|0;c[e>>2]=j+1;e=f+(j<<3)|0;if(!e){i=d;return}g=b;h=c[g+4>>2]|0;j=e;c[j>>2]=c[g>>2];c[j+4>>2]=h;i=d;return}function rc(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0.0,s=0;b=i;d=c[a>>2]|0;f=c[d>>2]|0;k=a+4|0;o=c[d+((c[k>>2]|0)+ -1<<2)>>2]|0;c[d>>2]=o;e=c[a+12>>2]|0;c[e+(o<<2)>>2]=0;c[e+(f<<2)>>2]=-1;o=(c[k>>2]|0)+ -1|0;c[k>>2]=o;if((o|0)<=1){i=b;return f|0}g=c[d>>2]|0;l=a+28|0;a=0;m=1;while(1){n=(a<<1)+2|0;if((n|0)<(o|0)){p=c[d+(n<<2)>>2]|0;s=c[d+(m<<2)>>2]|0;o=c[c[l>>2]>>2]|0;q=+h[o+(p<<3)>>3];r=+h[o+(s<<3)>>3];if(!(q>r)){p=s;q=r;j=6}}else{o=c[c[l>>2]>>2]|0;j=c[d+(m<<2)>>2]|0;p=j;q=+h[o+(j<<3)>>3];j=6}if((j|0)==6){j=0;n=m}if(!(q>+h[o+(g<<3)>>3]))break;c[d+(a<<2)>>2]=p;c[e+(p<<2)>>2]=a;m=n<<1|1;o=c[k>>2]|0;if((m|0)>=(o|0)){a=n;break}else a=n}c[d+(a<<2)>>2]=g;c[e+(g<<2)>>2]=a;i=b;return f|0}function sc(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0;f=i;k=c[d>>2]|0;d=k+1|0;g=b+4|0;if((c[g>>2]|0)>=(d|0)){i=f;return}j=b+8|0;h=c[j>>2]|0;if((h|0)<(d|0)){l=k+2-h&-2;k=(h>>1)+2&-2;k=(l|0)>(k|0)?l:k;if((k|0)>(2147483647-h|0)){l=va(1)|0;Ta(l|0,48,0)}m=c[b>>2]|0;l=k+h|0;c[j>>2]=l;l=Ud(m,l)|0;c[b>>2]=l;if((l|0)==0?(c[(Oa()|0)>>2]|0)==12:0){m=va(1)|0;Ta(m|0,48,0)}}h=c[g>>2]|0;if((h|0)<(d|0))do{a[(c[b>>2]|0)+h>>0]=e;h=h+1|0}while((h|0)!=(d|0));c[g>>2]=d;i=f;return}function tc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;e=i;i=i+16|0;h=e+8|0;f=e+4|0;j=e;if((b|0)<16){f=b+ -1|0;if((f|0)<=0){i=e;return}h=c[d>>2]|0;d=0;do{j=d;d=d+1|0;if((d|0)<(b|0)){k=c[h>>2]|0;m=j;l=d;do{n=k+(c[a+(l<<2)>>2]<<2)|0;u=c[n>>2]|0;q=u>>>5;if(u>>>0>95){o=k+(c[a+(m<<2)>>2]<<2)|0;p=(c[o>>2]|0)>>>5;if((p|0)==2)m=l;else m=+g[n+(q<<2)+4>>2]<+g[o+(p<<2)+4>>2]?l:m}l=l+1|0}while((l|0)!=(b|0))}else m=j;s=a+(j<<2)|0;t=c[s>>2]|0;u=a+(m<<2)|0;c[s>>2]=c[u>>2];c[u>>2]=t}while((d|0)!=(f|0));i=e;return}k=c[a+(((b|0)/2|0)<<2)>>2]|0;q=-1;o=b;while(1){t=q+1|0;p=a+(t<<2)|0;u=c[p>>2]|0;l=c[d>>2]|0;m=c[l>>2]|0;s=m+(u<<2)|0;r=c[s>>2]|0;q=m+(k<<2)|0;n=c[q>>2]|0;a:do if(r>>>0>95)while(1){v=n>>>5;if((v|0)!=2?!(+g[s+(r>>>5<<2)+4>>2]<+g[q+(v<<2)+4>>2]):0){q=t;break a}t=t+1|0;p=a+(t<<2)|0;u=c[p>>2]|0;s=m+(u<<2)|0;r=c[s>>2]|0;if(r>>>0<=95){q=t;break}}else q=t;while(0);o=o+ -1|0;s=a+(o<<2)|0;r=m+(k<<2)|0;b:do if(n>>>0>95)while(1){t=m+(c[s>>2]<<2)|0;v=(c[t>>2]|0)>>>5;if((v|0)!=2?!(+g[r+(n>>>5<<2)+4>>2]<+g[t+(v<<2)+4>>2]):0)break b;v=o+ -1|0;s=a+(v<<2)|0;o=v}while(0);if((q|0)>=(o|0))break;c[p>>2]=c[s>>2];c[s>>2]=u}c[f>>2]=l;c[h+0>>2]=c[f+0>>2];tc(a,q,h);v=b-q|0;c[j>>2]=l;c[h+0>>2]=c[j+0>>2];tc(p,v,h);i=e;return}function uc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0.0,s=0;e=i;f=a+4|0;j=c[f>>2]|0;g=c[a>>2]|0;if((j|0)>0){l=c[a+12>>2]|0;k=0;do{c[l+(c[g+(k<<2)>>2]<<2)>>2]=-1;k=k+1|0;j=c[f>>2]|0}while((k|0)<(j|0))}if(g){c[f>>2]=0;j=0}g=b+4|0;if((c[g>>2]|0)>0){k=a+12|0;j=0;do{s=(c[b>>2]|0)+(j<<2)|0;c[(c[k>>2]|0)+(c[s>>2]<<2)>>2]=j;nc(a,s);j=j+1|0}while((j|0)<(c[g>>2]|0));j=c[f>>2]|0}if((j|0)<=1){i=e;return}g=c[a>>2]|0;b=a+28|0;a=a+12|0;o=j;k=(j|0)/2|0;while(1){k=k+ -1|0;j=c[g+(k<<2)>>2]|0;m=k<<1|1;a:do if((m|0)<(o|0)){l=k;while(1){n=(l<<1)+2|0;if((n|0)<(o|0)){p=c[g+(n<<2)>>2]|0;s=c[g+(m<<2)>>2]|0;o=c[c[b>>2]>>2]|0;q=+h[o+(p<<3)>>3];r=+h[o+(s<<3)>>3];if(!(q>r)){p=s;q=r;d=16}}else{o=c[c[b>>2]>>2]|0;d=c[g+(m<<2)>>2]|0;p=d;q=+h[o+(d<<3)>>3];d=16}if((d|0)==16){d=0;n=m}if(!(q>+h[o+(j<<3)>>3]))break a;c[g+(l<<2)>>2]=p;c[(c[a>>2]|0)+(p<<2)>>2]=l;m=n<<1|1;o=c[f>>2]|0;if((m|0)>=(o|0)){l=n;break}else l=n}}else l=k;while(0);c[g+(l<<2)>>2]=j;c[(c[a>>2]|0)+(j<<2)>>2]=l;if((k|0)<=0)break;o=c[f>>2]|0}i=e;return}function vc(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;e=i;d=b+36|0;l=c[d>>2]|0;f=b+32|0;n=c[f>>2]|0;if((l|0)>0){h=b+16|0;g=b+44|0;j=0;do{k=n+(j<<2)|0;m=c[k>>2]|0;if(a[(c[h>>2]|0)+m>>0]|0){n=c[b>>2]|0;l=n+(m*12|0)+4|0;p=c[l>>2]|0;if((p|0)>0){m=n+(m*12|0)|0;n=0;o=0;do{q=c[m>>2]|0;r=q+(n<<3)|0;if((c[(c[c[g>>2]>>2]|0)+(c[r>>2]<<2)>>2]&3|0)!=1){s=r;r=c[s+4>>2]|0;p=q+(o<<3)|0;c[p>>2]=c[s>>2];c[p+4>>2]=r;p=c[l>>2]|0;o=o+1|0}n=n+1|0}while((n|0)<(p|0))}else{n=0;o=0}m=n-o|0;if((m|0)>0)c[l>>2]=p-m;a[(c[h>>2]|0)+(c[k>>2]|0)>>0]=0;l=c[d>>2]|0;n=c[f>>2]|0}j=j+1|0}while((j|0)<(l|0))}if(!n){i=e;return}c[d>>2]=0;i=e;return}function wc(a,b){a=a|0;b=b|0;var e=0,f=0,h=0,j=0,k=0;f=i;j=c[b>>2]|0;h=j>>>2&1|(d[a+16>>0]|0);j=((h+(j>>>5)<<2)+4|0)>>>2;k=a+4|0;gc(a,j+(c[k>>2]|0)|0);e=c[k>>2]|0;j=j+e|0;c[k>>2]=j;if(j>>>0<e>>>0)Ta(va(1)|0,48,0);a=(c[a>>2]|0)+(e<<2)|0;if(!a){i=f;return e|0}h=c[b>>2]&-9|h<<3;c[a>>2]=h;if((c[b>>2]|0)>>>0>31){h=0;do{c[a+(h<<2)+4>>2]=c[b+(h<<2)+4>>2];h=h+1|0}while((h|0)<((c[b>>2]|0)>>>5|0));h=c[a>>2]|0}if(!(h&8)){i=f;return e|0}j=h>>>5;b=b+(j<<2)+4|0;if(!(h&4)){c[a+(j<<2)+4>>2]=c[b>>2];i=f;return e|0}else{g[a+(j<<2)+4>>2]=+g[b>>2];i=f;return e|0}return 0}function xc(b){b=b|0;var d=0,e=0,f=0,g=0,j=0,k=0;d=i;i=i+16|0;g=d;Gb(b);c[b>>2]=3424;c[b+684>>2]=c[719];c[b+688>>2]=c[747];c[b+692>>2]=c[785];h[b+696>>3]=+h[411];a[b+704>>0]=a[2652]|0;a[b+705>>0]=a[2724]|0;a[b+706>>0]=a[2804]|0;a[b+707>>0]=1;c[b+708>>2]=0;c[b+712>>2]=0;c[b+716>>2]=0;c[b+720>>2]=1;a[b+724>>0]=1;e=b+732|0;k=b+544|0;c[b+760>>2]=0;c[b+764>>2]=0;c[b+768>>2]=0;c[b+776>>2]=0;c[b+780>>2]=0;c[b+784>>2]=0;c[b+792>>2]=0;c[b+796>>2]=0;c[b+800>>2]=0;j=b+804|0;c[e+0>>2]=0;c[e+4>>2]=0;c[e+8>>2]=0;c[e+12>>2]=0;c[e+16>>2]=0;c[e+20>>2]=0;c[j>>2]=k;j=b+808|0;c[j>>2]=0;c[b+812>>2]=0;c[b+816>>2]=0;e=b+824|0;c[e+0>>2]=0;c[e+4>>2]=0;c[e+8>>2]=0;c[e+12>>2]=0;c[e+16>>2]=0;c[e+20>>2]=0;c[b+852>>2]=j;Qc(b+856|0,1);j=b+868|0;e=b+892|0;c[b+920>>2]=0;c[b+924>>2]=0;c[j+0>>2]=0;c[j+4>>2]=0;c[j+8>>2]=0;c[j+12>>2]=0;c[j+16>>2]=0;c[e+0>>2]=0;c[e+4>>2]=0;c[e+8>>2]=0;c[e+12>>2]=0;c[e+16>>2]=0;c[e+20>>2]=0;e=g+4|0;c[e>>2]=0;j=g+8|0;c[j>>2]=2;f=Ud(0,8)|0;c[g>>2]=f;if((f|0)==0?(c[(Oa()|0)>>2]|0)==12:0)Ta(va(1)|0,48,0);c[f>>2]=-2;c[e>>2]=1;a[b+560>>0]=1;c[b+928>>2]=pc(k,g,0)|0;a[b+536>>0]=0;if(!f){i=d;return}c[e>>2]=0;Td(f);c[g>>2]=0;c[j>>2]=0;i=d;return}function yc(a){a=a|0;var b=0;b=i;zc(a);pd(a);i=b;return}function zc(a){a=a|0;var b=0,d=0,e=0;b=i;c[a>>2]=3424;d=a+904|0;e=c[d>>2]|0;if(e){c[a+908>>2]=0;Td(e);c[d>>2]=0;c[a+912>>2]=0}d=a+892|0;e=c[d>>2]|0;if(e){c[a+896>>2]=0;Td(e);c[d>>2]=0;c[a+900>>2]=0}d=a+876|0;e=c[d>>2]|0;if(e){c[a+880>>2]=0;Td(e);c[d>>2]=0;c[a+884>>2]=0}d=a+856|0;e=c[d>>2]|0;if(e){c[a+860>>2]=0;Td(e);c[d>>2]=0;c[a+864>>2]=0}e=a+836|0;d=c[e>>2]|0;if(d){c[a+840>>2]=0;Td(d);c[e>>2]=0;c[a+844>>2]=0}d=a+824|0;e=c[d>>2]|0;if(e){c[a+828>>2]=0;Td(e);c[d>>2]=0;c[a+832>>2]=0}d=a+808|0;e=c[d>>2]|0;if(e){c[a+812>>2]=0;Td(e);c[d>>2]=0;c[a+816>>2]=0}Rc(a+760|0);d=a+744|0;e=c[d>>2]|0;if(e){c[a+748>>2]=0;Td(e);c[d>>2]=0;c[a+752>>2]=0}d=a+732|0;e=c[d>>2]|0;if(!e){Ib(a);i=b;return}c[a+736>>2]=0;Td(e);c[d>>2]=0;c[a+740>>2]=0;Ib(a);i=b;return}function Ac(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;i=i+32|0;h=f+12|0;k=f+8|0;l=f+16|0;g=f+4|0;j=f;a[l>>0]=a[d>>0]|0;a[h+0>>0]=a[l+0>>0]|0;e=Jb(b,h,e)|0;c[k>>2]=e;kc(b+876|0,e,0);kc(b+904|0,e,0);if(!(a[b+724>>0]|0)){i=f;return e|0}l=b+808|0;d=e<<1;c[g>>2]=d;c[h+0>>2]=c[g+0>>2];Sc(l,h,0);c[j>>2]=d|1;c[h+0>>2]=c[j+0>>2];Sc(l,h,0);Tc(b+760|0,k);kc(b+744|0,e,0);Uc(b+824|0,e);i=f;return e|0}function Bc(b,e,f,g){b=b|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0;k=i;i=i+32|0;h=k+4|0;r=k;p=k+16|0;c[h>>2]=0;j=h+4|0;c[j>>2]=0;l=h+8|0;c[l>>2]=0;s=a[2608]|0;a[b>>0]=s;m=e+724|0;f=(d[m>>0]&(f&1)|0)!=0;if(f){u=e+308|0;x=c[u>>2]|0;if((x|0)>0){t=e+304|0;s=e+876|0;v=0;do{w=c[(c[t>>2]|0)+(v<<2)>>2]>>1;c[r>>2]=w;w=(c[s>>2]|0)+w|0;if(!(a[w>>0]|0)){a[w>>0]=1;nc(h,r);x=c[u>>2]|0}v=v+1|0}while((v|0)<(x|0))}r=(Cc(e,g)|0)&1^1;a[b>>0]=r;g=a[2608]|0}else{g=s;r=s}x=g&255;if(!((x>>>1^1)&r<<24>>24==g<<24>>24|x&2&(r&255))){if((c[e+44>>2]|0)>0)Ka(3760)|0}else{$b(p,e);r=a[p>>0]|0;a[b>>0]=r}w=a[2608]|0;x=w&255;if((((x>>>1^1)&r<<24>>24==w<<24>>24|x&2&(r&255)|0)!=0?(a[e+707>>0]|0)!=0:0)?(q=(c[e+736>>2]|0)+ -1|0,(q|0)>0):0){b=e+732|0;p=e+4|0;do{g=c[b>>2]|0;u=c[g+(q<<2)>>2]|0;v=q+ -1|0;w=c[g+(v<<2)>>2]|0;q=c[p>>2]|0;a:do if((u|0)>1){s=a[2616]|0;r=s&255;t=r&2;r=r>>>1^1;x=v;while(1){w=d[q+(w>>1)>>0]^w&1;v=u+ -1|0;if(!((w&255)<<24>>24==s<<24>>24&r|t&w))break a;u=x+ -1|0;w=c[g+(u<<2)>>2]|0;if((v|0)>1){x=u;u=v}else{x=u;u=v;o=20;break}}}else{x=v;o=20}while(0);if((o|0)==20){o=0;a[q+(w>>1)>>0]=(w&1^1)&255^1}q=x-u|0}while((q|0)>0)}if(f?(n=c[j>>2]|0,(n|0)>0):0){o=c[h>>2]|0;f=e+876|0;p=0;do{b=c[o+(p<<2)>>2]|0;a[(c[f>>2]|0)+b>>0]=0;if(a[m>>0]|0)Vc(e,b);p=p+1|0}while((p|0)<(n|0))}e=c[h>>2]|0;if(!e){i=k;return}c[j>>2]=0;Td(e);c[h>>2]=0;c[l>>2]=0;i=k;return}function Cc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,j=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0;m=i;i=i+16|0;j=m;if(!(Zb(b)|0)){H=0;i=m;return H|0}l=b+724|0;if(!(a[l>>0]|0)){H=1;i=m;return H|0}x=b+924|0;v=b+872|0;w=b+868|0;u=b+860|0;r=b+680|0;y=b+824|0;g=b+828|0;o=b+836|0;z=b+904|0;A=b+332|0;e=b+44|0;B=b+704|0;D=b+706|0;E=b+696|0;p=b+556|0;q=b+548|0;C=b+876|0;s=b+920|0;t=b+284|0;a:while(1){if(((c[x>>2]|0)<=0?(c[s>>2]|0)>=(c[t>>2]|0):0)?(c[g>>2]|0)<=0:0)break;Ic(b);G=c[v>>2]|0;H=c[w>>2]|0;F=G-H|0;if((G|0)<(H|0))F=(c[u>>2]|0)+F|0;if(!((F|0)<=0?(c[s>>2]|0)>=(c[t>>2]|0):0))n=11;if((n|0)==11?(n=0,!(Jc(b,1)|0)):0){n=12;break}H=c[g>>2]|0;if(a[r>>0]|0){n=15;break}if(!H)continue;else F=0;while(1){J=c[y>>2]|0;G=c[J>>2]|0;I=c[J+(H+ -1<<2)>>2]|0;c[J>>2]=I;H=c[o>>2]|0;c[H+(I<<2)>>2]=0;c[H+(G<<2)>>2]=-1;H=(c[g>>2]|0)+ -1|0;c[g>>2]=H;if((H|0)>1)Wc(y,0);if(a[r>>0]|0)continue a;if((a[(c[z>>2]|0)+G>>0]|0)==0?(I=a[(c[A>>2]|0)+G>>0]|0,H=a[2624]|0,J=H&255,((J>>>1^1)&I<<24>>24==H<<24>>24|I&2&J|0)!=0):0){if((c[e>>2]|0)>1&((F|0)%100|0|0)==0){c[j>>2]=c[g>>2];La(3504,j|0)|0}if(a[B>>0]|0){J=(c[C>>2]|0)+G|0;H=a[J>>0]|0;a[J>>0]=1;if(!(Lc(b,G)|0)){n=29;break a}a[(c[C>>2]|0)+G>>0]=H<<24>>24!=0&1}if((((a[D>>0]|0)!=0?(I=a[(c[A>>2]|0)+G>>0]|0,H=a[2624]|0,J=H&255,((J>>>1^1)&I<<24>>24==H<<24>>24|I&2&J|0)!=0):0)?(a[(c[C>>2]|0)+G>>0]|0)==0:0)?!(Mc(b,G)|0):0){n=35;break a}if(+((c[p>>2]|0)>>>0)>+h[E>>3]*+((c[q>>2]|0)>>>0))gb[c[(c[b>>2]|0)+8>>2]&31](b)}H=c[g>>2]|0;if(!H)continue a;else F=F+1|0}}do if((n|0)==12)a[b+492>>0]=0;else if((n|0)==15){r=c[b+824>>2]|0;if((H|0)<=0){if(!r)break}else{t=c[o>>2]|0;s=0;do{c[t+(c[r+(s<<2)>>2]<<2)>>2]=-1;s=s+1|0}while((s|0)<(c[g>>2]|0))}c[g>>2]=0}else if((n|0)==29)a[b+492>>0]=0;else if((n|0)==35)a[b+492>>0]=0;while(0);if(!d){if(+((c[p>>2]|0)>>>0)>+h[b+96>>3]*+((c[q>>2]|0)>>>0))gb[c[(c[b>>2]|0)+8>>2]&31](b)}else{d=b+744|0;p=c[d>>2]|0;if(p){c[b+748>>2]=0;Td(p);c[d>>2]=0;c[b+752>>2]=0}Xc(b+760|0,1);d=b+808|0;p=c[d>>2]|0;if(p){c[b+812>>2]=0;Td(p);c[d>>2]=0;c[b+816>>2]=0}p=b+824|0;d=c[p>>2]|0;if((c[g>>2]|0)<=0){if(d)n=48}else{n=c[o>>2]|0;o=0;do{c[n+(c[d+(o<<2)>>2]<<2)>>2]=-1;o=o+1|0}while((o|0)<(c[g>>2]|0));n=48}if((n|0)==48){c[g>>2]=0;Td(d);c[p>>2]=0;c[b+832>>2]=0}Yc(b+856|0,1);a[l>>0]=0;a[b+536>>0]=1;a[b+560>>0]=0;c[b+728>>2]=c[b+540>>2];Yb(b);gb[c[(c[b>>2]|0)+8>>2]&31](b)}if((c[e>>2]|0)>0?(f=c[b+736>>2]|0,(f|0)>0):0){h[k>>3]=+(f<<2>>>0)*9.5367431640625e-7;c[j>>2]=c[k>>2];c[j+4>>2]=c[k+4>>2];La(3528,j|0)|0}J=(a[b+492>>0]|0)!=0;i=m;return J|0}function Dc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;e=i;i=i+16|0;g=e;j=b+256|0;k=b+260|0;h=c[k>>2]|0;if((a[b+705>>0]|0)!=0?Ec(b,d)|0:0){p=1;i=e;return p|0}if(!(Kb(b,d)|0)){p=0;i=e;return p|0}if(!(a[b+724>>0]|0)){p=1;i=e;return p|0}d=c[k>>2]|0;if((d|0)!=(h+1|0)){p=1;i=e;return p|0}p=c[(c[j>>2]|0)+(d+ -1<<2)>>2]|0;c[g>>2]=p;m=(c[b+544>>2]|0)+(p<<2)|0;Zc(b+856|0,p);if((c[m>>2]|0)>>>0<=31){p=1;i=e;return p|0}l=b+760|0;k=b+808|0;j=b+744|0;h=b+924|0;d=b+824|0;n=b+840|0;b=b+836|0;o=0;do{p=m+(o<<2)+4|0;_c((c[l>>2]|0)+((c[p>>2]>>1)*12|0)|0,g);q=(c[k>>2]|0)+(c[p>>2]<<2)|0;c[q>>2]=(c[q>>2]|0)+1;a[(c[j>>2]|0)+(c[p>>2]>>1)>>0]=1;c[h>>2]=(c[h>>2]|0)+1;p=c[p>>2]>>1;if((c[n>>2]|0)>(p|0)?(f=c[(c[b>>2]|0)+(p<<2)>>2]|0,(f|0)>-1):0)Wc(d,f);o=o+1|0}while((o|0)<((c[m>>2]|0)>>>5|0));f=1;i=e;return f|0}function Ec(b,e){b=b|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;k=i;i=i+16|0;h=k+8|0;j=k+4|0;g=k;c[j>>2]=c[b+284>>2];nc(b+292|0,j);j=e+4|0;m=c[j>>2]|0;a:do if((m|0)>0){f=b+332|0;l=0;while(1){n=c[(c[e>>2]|0)+(l<<2)>>2]|0;p=d[(c[f>>2]|0)+(n>>1)>>0]|0;q=p^n&1;o=q&255;s=a[2608]|0;r=s&255;if(o<<24>>24==s<<24>>24&(r>>>1^1)|r&2&q)break;r=a[2616]|0;s=r&255;if(!((s>>>1^1)&o<<24>>24==r<<24>>24|p&2&s)){c[g>>2]=n^1;c[h+0>>2]=c[g+0>>2];Lb(b,h,-1);m=c[j>>2]|0}l=l+1|0;if((l|0)>=(m|0))break a}Rb(b,0);s=1;i=k;return s|0}while(0);s=(Mb(b)|0)!=-1;Rb(b,0);i=k;return s|0}function Fc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0;e=i;i=i+16|0;g=e;f=(c[b+544>>2]|0)+(d<<2)|0;if(!(a[b+724>>0]|0)){Pb(b,d);i=e;return}if((c[f>>2]|0)>>>0<=31){Pb(b,d);i=e;return}j=b+808|0;k=b+776|0;h=b+792|0;l=0;do{m=f+(l<<2)+4|0;n=(c[j>>2]|0)+(c[m>>2]<<2)|0;c[n>>2]=(c[n>>2]|0)+ -1;Vc(b,c[m>>2]>>1);m=c[m>>2]>>1;c[g>>2]=m;m=(c[k>>2]|0)+m|0;if(!(a[m>>0]|0)){a[m>>0]=1;nc(h,g)}l=l+1|0}while((l|0)<((c[f>>2]|0)>>>5|0));Pb(b,d);i=e;return}function Gc(b,e,f){b=b|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0;g=i;i=i+16|0;j=g+4|0;h=g;l=c[b+544>>2]|0;k=l+(e<<2)|0;Zc(b+856|0,e);if((c[k>>2]&-32|0)==64){Fc(b,e);p=c[f>>2]|0;f=c[k>>2]|0;a:do if(f>>>0>31){m=f>>>5;n=0;while(1){o=n+1|0;if((c[k+(n<<2)+4>>2]|0)==(p|0)){o=n;break a}if((o|0)<(m|0))n=o;else break}}else{m=0;o=0}while(0);n=m+ -1|0;if((o|0)<(n|0))do{f=o;o=o+1|0;c[k+(f<<2)+4>>2]=c[k+(o<<2)+4>>2];f=c[k>>2]|0;m=f>>>5;n=m+ -1|0}while((o|0)<(n|0));if(f&8){c[k+(n<<2)+4>>2]=c[k+(m<<2)+4>>2];f=c[k>>2]|0}m=f+ -32|0;c[k>>2]=m;m=m>>>5;if(!m){m=0;f=0}else{f=0;n=0;do{f=1<<((c[k+(n<<2)+4>>2]|0)>>>1&31)|f;n=n+1|0}while((n|0)<(m|0))}c[k+(m<<2)+4>>2]=f}else{Ob(b,e,1);f=c[f>>2]|0;n=c[k>>2]|0;b:do if(n>>>0>31){m=n>>>5;o=0;while(1){p=o+1|0;if((c[k+(o<<2)+4>>2]|0)==(f|0)){p=o;break b}if((p|0)<(m|0))o=p;else break}}else{m=0;p=0}while(0);o=m+ -1|0;if((p|0)<(o|0))do{n=p;p=p+1|0;c[k+(n<<2)+4>>2]=c[k+(p<<2)+4>>2];n=c[k>>2]|0;m=n>>>5;o=m+ -1|0}while((p|0)<(o|0));if(n&8){c[k+(o<<2)+4>>2]=c[k+(m<<2)+4>>2];n=c[k>>2]|0}o=n+ -32|0;c[k>>2]=o;o=o>>>5;if(!o){o=0;m=0}else{m=0;n=0;do{m=1<<((c[k+(n<<2)+4>>2]|0)>>>1&31)|m;n=n+1|0}while((n|0)<(o|0))}c[k+(o<<2)+4>>2]=m;Nb(b,e);m=f>>1;n=c[b+760>>2]|0;o=n+(m*12|0)|0;n=n+(m*12|0)+4|0;p=c[n>>2]|0;c:do if((p|0)>0){s=c[o>>2]|0;q=0;while(1){r=q+1|0;if((c[s+(q<<2)>>2]|0)==(e|0))break c;if((r|0)<(p|0))q=r;else{q=r;break}}}else q=0;while(0);p=p+ -1|0;if((q|0)<(p|0)){o=c[o>>2]|0;do{p=q;q=q+1|0;c[o+(p<<2)>>2]=c[o+(q<<2)>>2];p=(c[n>>2]|0)+ -1|0}while((q|0)<(p|0))}c[n>>2]=p;s=(c[b+808>>2]|0)+(f<<2)|0;c[s>>2]=(c[s>>2]|0)+ -1;Vc(b,m)}if((c[k>>2]&-32|0)!=32){s=1;i=g;return s|0}l=c[l+(e+1<<2)>>2]|0;k=d[(c[b+332>>2]|0)+(l>>1)>>0]|0;s=k^l&1;e=s&255;q=a[2624]|0;r=q&255;if(!(e<<24>>24==q<<24>>24&(r>>>1^1)|r&2&s)){r=a[2616]|0;s=r&255;if((s>>>1^1)&e<<24>>24==r<<24>>24|k&2&s){s=0;i=g;return s|0}}else{c[h>>2]=l;c[j+0>>2]=c[h+0>>2];Lb(b,j,-1)}s=(Mb(b)|0)==-1;i=g;return s|0}function Hc(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;g=i;i=i+16|0;j=g+4|0;h=g;o=a+708|0;c[o>>2]=(c[o>>2]|0)+1;if(c[f>>2]|0)c[f+4>>2]=0;k=(c[b>>2]|0)>>>5>>>0<(c[d>>2]|0)>>>5>>>0;a=k?d:b;b=k?b:d;k=c[b>>2]|0;a:do if(k>>>0>31){d=0;b:while(1){l=c[b+(d<<2)+4>>2]|0;c:do if((l>>1|0)!=(e|0)){m=c[a>>2]|0;d:do if(m>>>0>31){n=0;while(1){o=c[a+(n<<2)+4>>2]|0;n=n+1|0;if((l^o)>>>0<2)break;if((n|0)>=(m>>>5|0))break d}if((o|0)==(l^1|0)){f=0;break b}else break c}while(0);c[j>>2]=l;mc(f,j);k=c[b>>2]|0}while(0);d=d+1|0;if((d|0)>=(k>>>5|0))break a}i=g;return f|0}while(0);d=c[a>>2]|0;if(d>>>0<=31){o=1;i=g;return o|0}j=0;do{b=c[a+(j<<2)+4>>2]|0;if((b>>1|0)!=(e|0)){c[h>>2]=b;mc(f,h);d=c[a>>2]|0}j=j+1|0}while((j|0)<(d>>>5|0));f=1;i=g;return f|0}function Ic(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;d=i;k=b+924|0;if(!(c[k>>2]|0)){i=d;return}h=b+856|0;e=b+872|0;f=b+868|0;j=b+860|0;g=b+544|0;l=0;while(1){w=c[e>>2]|0;m=c[f>>2]|0;n=w-m|0;if((w|0)<(m|0))n=(c[j>>2]|0)+n|0;if((l|0)>=(n|0))break;n=(c[g>>2]|0)+(c[(c[h>>2]|0)+(((m+l|0)%(c[j>>2]|0)|0)<<2)>>2]<<2)|0;m=c[n>>2]|0;if(!(m&3))c[n>>2]=m&-4|2;l=l+1|0}l=b+540|0;q=c[l>>2]|0;if((q|0)>0){n=b+744|0;o=b+776|0;m=b+760|0;b=b+804|0;p=0;do{if(a[(c[n>>2]|0)+p>>0]|0){r=(c[o>>2]|0)+p|0;if(a[r>>0]|0){s=c[m>>2]|0;q=s+(p*12|0)+4|0;u=c[q>>2]|0;if((u|0)>0){s=c[s+(p*12|0)>>2]|0;v=0;t=0;do{w=c[s+(v<<2)>>2]|0;if((c[(c[c[b>>2]>>2]|0)+(w<<2)>>2]&3|0)!=1){c[s+(t<<2)>>2]=w;u=c[q>>2]|0;t=t+1|0}v=v+1|0}while((v|0)<(u|0))}else{v=0;t=0}s=v-t|0;if((s|0)>0)c[q>>2]=u-s;a[r>>0]=0}r=c[m>>2]|0;q=r+(p*12|0)+4|0;t=c[q>>2]|0;if((t|0)>0){r=r+(p*12|0)|0;s=0;do{u=c[(c[r>>2]|0)+(s<<2)>>2]|0;if(!(c[(c[g>>2]|0)+(u<<2)>>2]&3)){Zc(h,u);t=(c[g>>2]|0)+(c[(c[r>>2]|0)+(s<<2)>>2]<<2)|0;c[t>>2]=c[t>>2]&-4|2;t=c[q>>2]|0}s=s+1|0}while((s|0)<(t|0))}a[(c[n>>2]|0)+p>>0]=0;q=c[l>>2]|0}p=p+1|0}while((p|0)<(q|0));l=0}else l=0;while(1){w=c[e>>2]|0;m=c[f>>2]|0;n=w-m|0;if((w|0)<(m|0))n=(c[j>>2]|0)+n|0;if((l|0)>=(n|0))break;m=(c[g>>2]|0)+(c[(c[h>>2]|0)+(((m+l|0)%(c[j>>2]|0)|0)<<2)>>2]<<2)|0;n=c[m>>2]|0;if((n&3|0)==2)c[m>>2]=n&-4;l=l+1|0}c[k>>2]=0;i=d;return}function Jc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0;e=i;i=i+16|0;m=e;x=e+12|0;g=b+856|0;l=b+872|0;q=b+868|0;j=b+860|0;u=b+680|0;f=b+920|0;h=b+284|0;t=b+280|0;r=b+544|0;s=b+928|0;o=b+44|0;n=b+776|0;v=b+692|0;p=b+804|0;k=b+760|0;C=0;F=0;D=0;a:while(1){E=c[q>>2]|0;do{A=c[l>>2]|0;B=(A|0)<(E|0);A=A-E|0;if(B)G=(c[j>>2]|0)+A|0;else G=A;if((G|0)<=0?(c[f>>2]|0)>=(c[h>>2]|0):0){f=1;j=53;break a}if(a[u>>0]|0){j=8;break a}if(B)A=(c[j>>2]|0)+A|0;if((A|0)==0?(z=c[f>>2]|0,(z|0)<(c[h>>2]|0)):0){c[f>>2]=z+1;c[(c[r>>2]|0)+((c[s>>2]|0)+1<<2)>>2]=c[(c[t>>2]|0)+(z<<2)>>2];A=(c[r>>2]|0)+(c[s>>2]<<2)|0;B=(c[A>>2]|0)>>>5;if(!B){B=0;G=0}else{G=0;E=0;do{G=1<<((c[A+(E<<2)+4>>2]|0)>>>1&31)|G;E=E+1|0}while((E|0)<(B|0))}c[A+(B<<2)+4>>2]=G;Zc(g,c[s>>2]|0);E=c[q>>2]|0}A=c[(c[g>>2]|0)+(E<<2)>>2]|0;E=E+1|0;J=c[j>>2]|0;E=(E|0)==(J|0)?0:E;c[q>>2]=E;G=c[r>>2]|0;B=G+(A<<2)|0;I=c[B>>2]|0}while((I&3|0)!=0);if(d?(c[o>>2]|0)>1:0){H=C+1|0;if(!((C|0)%1e3|0)){I=c[l>>2]|0;c[m>>2]=I-E+((I|0)<(E|0)?J:0);c[m+4>>2]=D;c[m+8>>2]=F;La(3440,m|0)|0;I=c[B>>2]|0;C=H}else C=H}E=G+(A+1<<2)|0;G=c[E>>2]>>1;if(I>>>0>63){H=c[k>>2]|0;I=I>>>5;J=1;do{P=c[B+(J<<2)+4>>2]>>1;G=(c[H+(P*12|0)+4>>2]|0)<(c[H+(G*12|0)+4>>2]|0)?P:G;J=J+1|0}while((J|0)<(I|0))}I=(c[n>>2]|0)+G|0;if(a[I>>0]|0){J=c[k>>2]|0;H=J+(G*12|0)+4|0;M=c[H>>2]|0;if((M|0)>0){J=c[J+(G*12|0)>>2]|0;L=0;K=0;do{N=c[J+(L<<2)>>2]|0;if((c[(c[c[p>>2]>>2]|0)+(N<<2)>>2]&3|0)!=1){c[J+(K<<2)>>2]=N;M=c[H>>2]|0;K=K+1|0}L=L+1|0}while((L|0)<(M|0))}else{L=0;K=0}J=L-K|0;if((J|0)>0)c[H>>2]=M-J;a[I>>0]=0}I=c[k>>2]|0;H=c[I+(G*12|0)>>2]|0;I=I+(G*12|0)+4|0;if((c[I>>2]|0)>0)J=0;else continue;while(1){N=c[B>>2]|0;if(N&3)continue a;K=c[H+(J<<2)>>2]|0;L=c[r>>2]|0;O=L+(K<<2)|0;M=c[O>>2]|0;b:do if(((!((M&3|0)!=0|(K|0)==(A|0))?(P=c[v>>2]|0,y=M>>>5,(P|0)==-1|(y|0)<(P|0)):0)?(w=N>>>5,y>>>0>=w>>>0):0)?(c[B+(w<<2)+4>>2]&~c[O+(y<<2)+4>>2]|0)==0:0){L=L+(K+1<<2)|0;do if(N>>>0>31){if(M>>>0>31){O=-2;M=0}else break b;while(1){N=c[E+(M<<2)>>2]|0;c:do if((O|0)==-2){P=0;while(1){O=c[L+(P<<2)>>2]|0;if((N|0)==(O|0)){N=-2;break c}P=P+1|0;if((N|0)==(O^1|0))break c;if(P>>>0>=y>>>0)break b}}else{P=0;while(1){if((N|0)==(c[L+(P<<2)>>2]|0)){N=O;break c}P=P+1|0;if(P>>>0>=y>>>0)break b}}while(0);M=M+1|0;if(M>>>0>=w>>>0)break;else O=N}if((N|0)==-2)break;else if((N|0)==-1)break b;c[x>>2]=N^1;c[m+0>>2]=c[x+0>>2];if(!(Gc(b,K,m)|0)){f=0;j=53;break a}F=F+1|0;J=(((N>>1|0)==(G|0))<<31>>31)+J|0;break b}while(0);Fc(b,K);D=D+1|0}while(0);J=J+1|0;if((J|0)>=(c[I>>2]|0))continue a}}if((j|0)==8){Yc(g,0);c[f>>2]=c[h>>2];P=1;i=e;return P|0}else if((j|0)==53){i=e;return f|0}return 0}function Kc(b,e,f){b=b|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;h=i;i=i+16|0;g=h+12|0;m=h+8|0;k=h+4|0;j=h;l=(c[b+544>>2]|0)+(f<<2)|0;if(c[l>>2]&3){r=1;i=h;return r|0}if(Qb(b,l)|0){r=1;i=h;return r|0}c[m>>2]=c[b+284>>2];nc(b+292|0,m);p=c[l>>2]|0;if(p>>>0>31){m=b+332|0;n=0;o=-2;do{q=c[l+(n<<2)+4>>2]|0;r=q>>1;if((r|0)!=(e|0)?(r=(d[(c[m>>2]|0)+r>>0]|0)^q&1,t=a[2616]|0,s=t&255,((r&255)<<24>>24==t<<24>>24&(s>>>1^1)|s&2&r|0)==0):0){c[k>>2]=q^1;c[g+0>>2]=c[k+0>>2];Lb(b,g,-1);p=c[l>>2]|0}else o=q;n=n+1|0}while((n|0)<(p>>>5|0))}else o=-2;t=(Mb(b)|0)==-1;Rb(b,0);if(!t){t=b+712|0;c[t>>2]=(c[t>>2]|0)+1;c[j>>2]=o;c[g+0>>2]=c[j+0>>2];if(!(Gc(b,f,g)|0)){t=0;i=h;return t|0}}t=1;i=h;return t|0}function Lc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0;e=i;h=(c[b+776>>2]|0)+d|0;f=b+760|0;if(a[h>>0]|0){k=c[f>>2]|0;g=k+(d*12|0)+4|0;n=c[g>>2]|0;if((n|0)>0){j=b+804|0;k=c[k+(d*12|0)>>2]|0;m=0;l=0;do{o=c[k+(m<<2)>>2]|0;if((c[(c[c[j>>2]>>2]|0)+(o<<2)>>2]&3|0)!=1){c[k+(l<<2)>>2]=o;n=c[g>>2]|0;l=l+1|0}m=m+1|0}while((m|0)<(n|0))}else{m=0;l=0}j=m-l|0;if((j|0)>0)c[g>>2]=n-j;a[h>>0]=0}g=c[f>>2]|0;n=a[(c[b+332>>2]|0)+d>>0]|0;m=a[2624]|0;o=m&255;if(!((o>>>1^1)&n<<24>>24==m<<24>>24|n&2&o)){o=1;i=e;return o|0}f=g+(d*12|0)+4|0;h=c[f>>2]|0;if(!h){o=1;i=e;return o|0}a:do if((h|0)>0){g=g+(d*12|0)|0;h=0;while(1){if(!(Kc(b,d,c[(c[g>>2]|0)+(h<<2)>>2]|0)|0)){b=0;break}h=h+1|0;if((h|0)>=(c[f>>2]|0))break a}i=e;return b|0}while(0);o=Jc(b,0)|0;i=e;return o|0}function Mc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0,O=0,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=0;e=i;i=i+48|0;s=e+36|0;r=e+32|0;t=e+28|0;u=e+24|0;f=e+12|0;g=e;n=(c[b+776>>2]|0)+d|0;m=b+760|0;if(a[n>>0]|0){q=c[m>>2]|0;o=q+(d*12|0)+4|0;y=c[o>>2]|0;if((y|0)>0){p=b+804|0;q=c[q+(d*12|0)>>2]|0;w=0;v=0;do{z=c[q+(w<<2)>>2]|0;if((c[(c[c[p>>2]>>2]|0)+(z<<2)>>2]&3|0)!=1){c[q+(v<<2)>>2]=z;y=c[o>>2]|0;v=v+1|0}w=w+1|0}while((w|0)<(y|0))}else{w=0;v=0}p=w-v|0;if((p|0)>0)c[o>>2]=y-p;a[n>>0]=0}v=c[m>>2]|0;w=v+(d*12|0)|0;c[f>>2]=0;n=f+4|0;c[n>>2]=0;o=f+8|0;c[o>>2]=0;c[g>>2]=0;q=g+4|0;c[q>>2]=0;p=g+8|0;c[p>>2]=0;v=v+(d*12|0)+4|0;a:do if((c[v>>2]|0)>0){y=b+544|0;B=d<<1;A=0;do{C=(c[w>>2]|0)+(A<<2)|0;E=(c[y>>2]|0)+(c[C>>2]<<2)|0;Z=c[E>>2]|0;z=Z>>>5;b:do if(Z>>>0>31){G=0;while(1){D=G+1|0;if((c[E+(G<<2)+4>>2]|0)==(B|0)){D=G;break b}if((D|0)<(z|0))G=D;else break}}else D=0;while(0);_c((D|0)<(z|0)?f:g,C);A=A+1|0;z=c[v>>2]|0}while((A|0)<(z|0));y=c[n>>2]|0;B=(y|0)>0;if(B){C=c[q>>2]|0;K=(C|0)>0;J=b+544|0;D=c[f>>2]|0;A=c[g>>2]|0;E=b+708|0;I=b+684|0;H=b+688|0;P=0;G=0;while(1){if(K){M=D+(G<<2)|0;L=c[J>>2]|0;N=c[E>>2]|0;O=0;do{S=L+(c[M>>2]<<2)|0;U=L+(c[A+(O<<2)>>2]<<2)|0;N=N+1|0;c[E>>2]=N;Q=(c[S>>2]|0)>>>5>>>0<(c[U>>2]|0)>>>5>>>0;R=Q?U:S;U=Q?S:U;S=R+4|0;Q=U+4|0;R=c[R>>2]|0;T=R>>>5;W=T+ -1|0;U=c[U>>2]|0;c:do if(U>>>0>31){V=0;while(1){Z=c[Q+(V<<2)>>2]|0;d:do if((Z>>1|0)!=(d|0)){e:do if(R>>>0>31){Y=0;while(1){X=c[S+(Y<<2)>>2]|0;Y=Y+1|0;if((X^Z)>>>0<2)break;if((Y|0)>=(T|0))break e}if((X|0)==(Z^1|0))break c;else break d}while(0);W=W+1|0}while(0);V=V+1|0;if((V|0)>=(U>>>5|0)){x=28;break}}}else x=28;while(0);if((x|0)==28){x=0;if((P|0)>=((c[I>>2]|0)+z|0)){b=1;break a}Z=c[H>>2]|0;if((Z|0)!=-1&(W|0)>(Z|0)){b=1;break a}else P=P+1|0}O=O+1|0}while((O|0)<(C|0))}G=G+1|0;if((G|0)>=(y|0)){x=32;break}}}else{B=0;x=32}}else{y=0;B=0;x=32}while(0);f:do if((x|0)==32){a[(c[b+904>>2]|0)+d>>0]=1;z=b+380|0;A=(c[z>>2]|0)+d|0;if(a[A>>0]|0){Z=b+200|0;Y=Z;Y=ne(c[Y>>2]|0,c[Y+4>>2]|0,-1,-1)|0;c[Z>>2]=Y;c[Z+4>>2]=F}a[A>>0]=0;A=b+460|0;if(!((c[b+476>>2]|0)>(d|0)?(c[(c[b+472>>2]|0)+(d<<2)>>2]|0)>-1:0))x=36;if((x|0)==36?(a[(c[z>>2]|0)+d>>0]|0)!=0:0)lc(A,d);x=b+716|0;c[x>>2]=(c[x>>2]|0)+1;x=c[q>>2]|0;if((y|0)>(x|0)){A=b+732|0;if((x|0)>0){u=b+544|0;t=c[g>>2]|0;E=b+736|0;D=0;do{C=(c[u>>2]|0)+(c[t+(D<<2)>>2]<<2)|0;z=c[E>>2]|0;if((c[C>>2]|0)>>>0>31){G=0;H=-1;do{Z=C+(G<<2)+4|0;c[s>>2]=c[Z>>2];_c(A,s);H=(c[Z>>2]>>1|0)==(d|0)?G+z|0:H;G=G+1|0}while((G|0)<((c[C>>2]|0)>>>5|0))}else H=-1;Z=c[A>>2]|0;X=Z+(H<<2)|0;Y=c[X>>2]|0;Z=Z+(z<<2)|0;c[X>>2]=c[Z>>2];c[Z>>2]=Y;c[r>>2]=(c[C>>2]|0)>>>5;_c(A,r);D=D+1|0}while((D|0)<(x|0))}c[s>>2]=d<<1;_c(A,s);c[r>>2]=1;_c(A,r)}else{D=b+732|0;if(B){G=b+544|0;E=c[f>>2]|0;z=b+736|0;H=0;do{C=(c[G>>2]|0)+(c[E+(H<<2)>>2]<<2)|0;A=c[z>>2]|0;if((c[C>>2]|0)>>>0>31){I=0;J=-1;do{Z=C+(I<<2)+4|0;c[s>>2]=c[Z>>2];_c(D,s);J=(c[Z>>2]>>1|0)==(d|0)?I+A|0:J;I=I+1|0}while((I|0)<((c[C>>2]|0)>>>5|0))}else J=-1;Z=c[D>>2]|0;X=Z+(J<<2)|0;Y=c[X>>2]|0;Z=Z+(A<<2)|0;c[X>>2]=c[Z>>2];c[Z>>2]=Y;c[r>>2]=(c[C>>2]|0)>>>5;_c(D,r);H=H+1|0}while((H|0)<(y|0))}c[t>>2]=d<<1|1;_c(D,t);c[u>>2]=1;_c(D,u)}if((c[v>>2]|0)>0){r=0;do{Fc(b,c[(c[w>>2]|0)+(r<<2)>>2]|0);r=r+1|0}while((r|0)<(c[v>>2]|0))}r=b+628|0;g:do if(B){s=b+544|0;w=c[f>>2]|0;A=c[g>>2]|0;if((x|0)>0)v=0;else{r=0;while(1){r=r+1|0;if((r|0)>=(y|0))break g}}do{u=w+(v<<2)|0;t=0;do{Z=c[s>>2]|0;if(Hc(b,Z+(c[u>>2]<<2)|0,Z+(c[A+(t<<2)>>2]<<2)|0,d,r)|0?!(Dc(b,r)|0):0){b=0;break f}t=t+1|0}while((t|0)<(x|0));v=v+1|0}while((v|0)<(y|0))}while(0);r=c[m>>2]|0;m=r+(d*12|0)|0;s=c[m>>2]|0;if(s){c[r+(d*12|0)+4>>2]=0;Td(s);c[m>>2]=0;c[r+(d*12|0)+8>>2]=0}m=b+412|0;d=d<<1;s=c[m>>2]|0;r=s+(d*12|0)+4|0;if((c[r>>2]|0)==0?(l=s+(d*12|0)|0,k=c[l>>2]|0,(k|0)!=0):0){c[r>>2]=0;Td(k);c[l>>2]=0;c[s+(d*12|0)+8>>2]=0;s=c[m>>2]|0}k=d|1;l=s+(k*12|0)+4|0;if((c[l>>2]|0)==0?(j=s+(k*12|0)|0,h=c[j>>2]|0,(h|0)!=0):0){c[l>>2]=0;Td(h);c[j>>2]=0;c[s+(k*12|0)+8>>2]=0}b=Jc(b,0)|0;A=c[g>>2]|0}while(0);if(A){c[q>>2]=0;Td(A);c[g>>2]=0;c[p>>2]=0}g=c[f>>2]|0;if(!g){i=e;return b|0}c[n>>2]=0;Td(g);c[f>>2]=0;c[o>>2]=0;i=e;return b|0}function Nc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;e=i;if(!(a[b+724>>0]|0)){i=e;return}l=b+540|0;if((c[l>>2]|0)>0){j=b+760|0;f=b+804|0;g=b+776|0;k=b+544|0;h=0;do{n=c[j>>2]|0;m=n+(h*12|0)+4|0;p=c[m>>2]|0;if((p|0)>0){n=c[n+(h*12|0)>>2]|0;q=0;o=0;do{r=c[n+(q<<2)>>2]|0;if((c[(c[c[f>>2]>>2]|0)+(r<<2)>>2]&3|0)!=1){c[n+(o<<2)>>2]=r;p=c[m>>2]|0;o=o+1|0}q=q+1|0}while((q|0)<(p|0))}else{q=0;o=0}n=q-o|0;if((n|0)>0)c[m>>2]=p-n;a[(c[g>>2]|0)+h>>0]=0;n=c[j>>2]|0;m=n+(h*12|0)+4|0;if((c[m>>2]|0)>0){r=n+(h*12|0)|0;p=0;do{n=(c[r>>2]|0)+(p<<2)|0;o=c[n>>2]|0;q=c[k>>2]|0;s=q+(o<<2)|0;if(!(c[s>>2]&16)){t=wc(d,s)|0;c[n>>2]=t;c[s>>2]=c[s>>2]|16;c[q+(o+1<<2)>>2]=t}else c[n>>2]=c[q+(o+1<<2)>>2];p=p+1|0}while((p|0)<(c[m>>2]|0))}h=h+1|0}while((h|0)<(c[l>>2]|0))}f=b+856|0;t=c[b+872>>2]|0;g=b+868|0;m=c[g>>2]|0;k=t-m|0;if((t|0)<(m|0))k=(c[b+860>>2]|0)+k|0;a:do if((k|0)>0){h=b+860|0;j=b+544|0;while(1){l=c[(c[f>>2]|0)+(m<<2)>>2]|0;n=m+1|0;c[g>>2]=(n|0)==(c[h>>2]|0)?0:n;n=c[j>>2]|0;o=n+(l<<2)|0;m=c[o>>2]|0;if(!(m&3)){if(!(m&16)){t=wc(d,o)|0;c[o>>2]=c[o>>2]|16;c[n+(l+1<<2)>>2]=t;l=t}else l=c[n+(l+1<<2)>>2]|0;Zc(f,l)}k=k+ -1|0;if((k|0)<=0)break a;m=c[g>>2]|0}}else j=b+544|0;while(0);b=b+928|0;f=c[b>>2]|0;h=c[j>>2]|0;g=h+(f<<2)|0;if(!(c[g>>2]&16)){t=wc(d,g)|0;c[b>>2]=t;c[g>>2]=c[g>>2]|16;c[h+(f+1<<2)>>2]=t;i=e;return}else{c[b>>2]=c[h+(f+1<<2)>>2];i=e;return}}function Oc(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;h=i;i=i+32|0;l=h;d=h+8|0;e=b+544|0;f=b+548|0;g=b+556|0;j=(c[f>>2]|0)-(c[g>>2]|0)|0;c[d+0>>2]=0;c[d+4>>2]=0;c[d+8>>2]=0;c[d+12>>2]=0;gc(d,j);j=d+16|0;k=b+560|0;a[j>>0]=a[k>>0]|0;Nc(b,d);ac(b,d);if((c[b+44>>2]|0)>1){m=c[d+4>>2]<<2;c[l>>2]=c[f>>2]<<2;c[l+4>>2]=m;La(3608,l|0)|0}a[k>>0]=a[j>>0]|0;j=c[e>>2]|0;if(j)Td(j);c[e>>2]=c[d>>2];c[f>>2]=c[d+4>>2];c[b+552>>2]=c[d+8>>2];c[g>>2]=c[d+12>>2];i=h;return}function Pc(){var d=0,e=0,f=0;d=i;i=i+16|0;e=d;a[2608]=0;a[2616]=1;a[2624]=2;xb(2632,2656,2664,3744,3752);c[658]=160;a[2652]=0;xb(2704,2728,2736,3744,3752);c[676]=160;a[2724]=0;xb(2784,2808,2816,3744,3752);c[696]=160;a[2804]=1;xb(2848,2880,2888,3744,3736);c[712]=280;f=2868|0;c[f>>2]=-2147483648;c[f+4>>2]=2147483647;c[719]=0;xb(2960,2992,3e3,3744,3736);c[740]=280;f=2980|0;c[f>>2]=-1;c[f+4>>2]=2147483647;c[747]=20;xb(3112,3144,3152,3744,3736);c[778]=280;f=3132|0;c[f>>2]=-1;c[f+4>>2]=2147483647;c[785]=1e3;xb(3240,3296,3312,3744,3720);c[810]=2168;h[408]=0.0;h[409]=v;a[3280]=0;a[3281]=0;b[1641]=b[e+0>>1]|0;b[1642]=b[e+2>>1]|0;b[1643]=b[e+4>>1]|0;h[411]=.5;i=d;return}function Qc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;d=i;c[a>>2]=0;e=a+4|0;c[e>>2]=0;f=a+8|0;c[f>>2]=0;if((b|0)<=0){i=d;return}g=b+1&-2;g=(g|0)>2?g:2;c[f>>2]=g;f=Ud(0,g<<2)|0;c[a>>2]=f;if((f|0)==0?(c[(Oa()|0)>>2]|0)==12:0)Ta(va(1)|0,48,0);a=c[e>>2]|0;if((a|0)<(b|0))do{g=f+(a<<2)|0;if(g)c[g>>2]=0;a=a+1|0}while((a|0)!=(b|0));c[e>>2]=b;i=d;return}function Rc(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0;b=i;e=a+32|0;d=c[e>>2]|0;if(d){c[a+36>>2]=0;Td(d);c[e>>2]=0;c[a+40>>2]=0}e=a+16|0;d=c[e>>2]|0;if(d){c[a+20>>2]=0;Td(d);c[e>>2]=0;c[a+24>>2]=0}e=c[a>>2]|0;if(!e){i=b;return}d=a+4|0;g=c[d>>2]|0;if((g|0)>0){f=0;do{j=e+(f*12|0)|0;h=c[j>>2]|0;if(h){c[e+(f*12|0)+4>>2]=0;Td(h);c[j>>2]=0;c[e+(f*12|0)+8>>2]=0;e=c[a>>2]|0;g=c[d>>2]|0}f=f+1|0}while((f|0)<(g|0))}c[d>>2]=0;Td(e);c[a>>2]=0;c[a+8>>2]=0;i=b;return}function Sc(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;e=i;b=c[b>>2]|0;g=b+1|0;f=a+4|0;if((c[f>>2]|0)>=(g|0)){k=c[a>>2]|0;k=k+(b<<2)|0;c[k>>2]=d;i=e;return}h=a+8|0;k=c[h>>2]|0;if((k|0)<(g|0)){l=b+2-k&-2;j=(k>>1)+2&-2;j=(l|0)>(j|0)?l:j;if((j|0)>(2147483647-k|0)){l=va(1)|0;Ta(l|0,48,0)}m=c[a>>2]|0;l=j+k|0;c[h>>2]=l;l=Ud(m,l<<2)|0;c[a>>2]=l;if((l|0)==0?(c[(Oa()|0)>>2]|0)==12:0){m=va(1)|0;Ta(m|0,48,0)}}k=c[f>>2]|0;if((k|0)<(g|0)){h=c[a>>2]|0;do{j=h+(k<<2)|0;if(j)c[j>>2]=0;k=k+1|0}while((k|0)!=(g|0))}c[f>>2]=g;m=c[a>>2]|0;m=m+(b<<2)|0;c[m>>2]=d;i=e;return}function Tc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;e=i;k=c[d>>2]|0;g=k+1|0;f=b+4|0;if((c[f>>2]|0)<(g|0)){j=b+8|0;h=c[j>>2]|0;if((h|0)<(g|0)){l=k+2-h&-2;k=(h>>1)+2&-2;k=(l|0)>(k|0)?l:k;if((k|0)>(2147483647-h|0)){l=va(1)|0;Ta(l|0,48,0)}m=c[b>>2]|0;l=k+h|0;c[j>>2]=l;l=Ud(m,l*12|0)|0;c[b>>2]=l;if((l|0)==0?(c[(Oa()|0)>>2]|0)==12:0){m=va(1)|0;Ta(m|0,48,0)}}j=c[f>>2]|0;if((j|0)<(g|0)){h=c[b>>2]|0;do{k=h+(j*12|0)|0;if(k){c[k>>2]=0;c[h+(j*12|0)+4>>2]=0;c[h+(j*12|0)+8>>2]=0}j=j+1|0}while((j|0)!=(g|0))}c[f>>2]=g;h=c[d>>2]|0}else h=k;f=c[b>>2]|0;if(c[f+(h*12|0)>>2]|0){c[f+(h*12|0)+4>>2]=0;h=c[d>>2]|0}d=b+16|0;f=h+1|0;g=b+20|0;if((c[g>>2]|0)>=(f|0)){i=e;return}j=b+24|0;b=c[j>>2]|0;if((b|0)<(f|0)){m=h+2-b&-2;h=(b>>1)+2&-2;h=(m|0)>(h|0)?m:h;if((h|0)>(2147483647-b|0)){m=va(1)|0;Ta(m|0,48,0)}l=c[d>>2]|0;m=h+b|0;c[j>>2]=m;m=Ud(l,m)|0;c[d>>2]=m;if((m|0)==0?(c[(Oa()|0)>>2]|0)==12:0){m=va(1)|0;Ta(m|0,48,0)}}b=c[g>>2]|0;if((b|0)<(f|0))do{a[(c[d>>2]|0)+b>>0]=0;b=b+1|0}while((b|0)!=(f|0));c[g>>2]=f;i=e;return}function Uc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;d=i;i=i+16|0;g=d;c[g>>2]=b;f=a+12|0;e=b+1|0;h=a+16|0;if((c[h>>2]|0)<(e|0)){k=a+20|0;j=c[k>>2]|0;if((j|0)<(e|0)){m=b+2-j&-2;l=(j>>1)+2&-2;l=(m|0)>(l|0)?m:l;if((l|0)>(2147483647-j|0)){m=va(1)|0;Ta(m|0,48,0)}n=c[f>>2]|0;m=l+j|0;c[k>>2]=m;m=Ud(n,m<<2)|0;c[f>>2]=m;if((m|0)==0?(c[(Oa()|0)>>2]|0)==12:0){n=va(1)|0;Ta(n|0,48,0)}}j=c[h>>2]|0;if((e|0)>(j|0))ke((c[f>>2]|0)+(j<<2)|0,-1,e-j<<2|0)|0;c[h>>2]=e}c[(c[f>>2]|0)+(b<<2)>>2]=c[a+4>>2];nc(a,g);e=c[f>>2]|0;j=c[e+(b<<2)>>2]|0;b=c[a>>2]|0;f=c[b+(j<<2)>>2]|0;if(!j){m=0;n=b+(m<<2)|0;c[n>>2]=f;n=e+(f<<2)|0;c[n>>2]=m;i=d;return}a=a+28|0;g=f<<1;h=g|1;while(1){m=j;j=j+ -1>>1;l=b+(j<<2)|0;k=c[l>>2]|0;r=c[c[a>>2]>>2]|0;o=c[r+(g<<2)>>2]|0;q=c[r+(h<<2)>>2]|0;o=we(q|0,((q|0)<0)<<31>>31|0,o|0,((o|0)<0)<<31>>31|0)|0;q=F;p=k<<1;n=c[r+(p<<2)>>2]|0;p=c[r+((p|1)<<2)>>2]|0;n=we(p|0,((p|0)<0)<<31>>31|0,n|0,((n|0)<0)<<31>>31|0)|0;p=F;if(!(q>>>0<p>>>0|(q|0)==(p|0)&o>>>0<n>>>0)){a=14;break}c[b+(m<<2)>>2]=k;c[e+(c[l>>2]<<2)>>2]=m;if(!j){m=0;a=14;break}}if((a|0)==14){r=b+(m<<2)|0;c[r>>2]=f;r=e+(f<<2)|0;c[r>>2]=m;i=d;return}}function Vc(b,d){b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;e=i;h=b+824|0;l=(c[b+840>>2]|0)>(d|0);if(l?(c[(c[b+836>>2]|0)+(d<<2)>>2]|0)>-1:0)j=7;else j=3;do if((j|0)==3){if(a[(c[b+876>>2]|0)+d>>0]|0){i=e;return}if(a[(c[b+904>>2]|0)+d>>0]|0){i=e;return}o=a[(c[b+332>>2]|0)+d>>0]|0;n=a[2624]|0;p=n&255;if((p>>>1^1)&o<<24>>24==n<<24>>24|o&2&p)if(l){j=7;break}else break;else{i=e;return}}while(0);if((j|0)==7?(f=c[b+836>>2]|0,g=f+(d<<2)|0,k=c[g>>2]|0,(k|0)>-1):0){d=c[h>>2]|0;j=c[d+(k<<2)>>2]|0;a:do if(!k)o=0;else{l=b+852|0;m=j<<1;b=m|1;while(1){o=k;k=k+ -1>>1;p=d+(k<<2)|0;n=c[p>>2]|0;u=c[c[l>>2]>>2]|0;r=c[u+(m<<2)>>2]|0;t=c[u+(b<<2)>>2]|0;r=we(t|0,((t|0)<0)<<31>>31|0,r|0,((r|0)<0)<<31>>31|0)|0;t=F;s=n<<1;q=c[u+(s<<2)>>2]|0;s=c[u+((s|1)<<2)>>2]|0;q=we(s|0,((s|0)<0)<<31>>31|0,q|0,((q|0)<0)<<31>>31|0)|0;s=F;if(!(t>>>0<s>>>0|(t|0)==(s|0)&r>>>0<q>>>0))break a;c[d+(o<<2)>>2]=n;c[f+(c[p>>2]<<2)>>2]=o;if(!k){o=0;break}}}while(0);c[d+(o<<2)>>2]=j;c[f+(j<<2)>>2]=o;Wc(h,c[g>>2]|0);i=e;return}Uc(h,d);i=e;return}function Wc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;d=i;e=c[a>>2]|0;f=c[e+(b<<2)>>2]|0;m=b<<1|1;l=a+4|0;o=c[l>>2]|0;if((m|0)>=(o|0)){p=b;q=a+12|0;o=e+(p<<2)|0;c[o>>2]=f;q=c[q>>2]|0;q=q+(f<<2)|0;c[q>>2]=p;i=d;return}h=a+28|0;k=f<<1;j=k|1;a=a+12|0;while(1){n=(b<<1)+2|0;if((n|0)<(o|0)){p=c[e+(n<<2)>>2]|0;q=c[e+(m<<2)>>2]|0;u=p<<1;o=c[c[h>>2]>>2]|0;s=c[o+(u<<2)>>2]|0;u=c[o+((u|1)<<2)>>2]|0;s=we(u|0,((u|0)<0)<<31>>31|0,s|0,((s|0)<0)<<31>>31|0)|0;u=F;t=q<<1;r=c[o+(t<<2)>>2]|0;t=c[o+((t|1)<<2)>>2]|0;r=we(t|0,((t|0)<0)<<31>>31|0,r|0,((r|0)<0)<<31>>31|0)|0;t=F;if(!(u>>>0<t>>>0|(u|0)==(t|0)&s>>>0<r>>>0)){p=q;g=7}}else{p=c[e+(m<<2)>>2]|0;o=c[c[h>>2]>>2]|0;g=7}if((g|0)==7){g=0;n=m}r=p<<1;t=c[o+(r<<2)>>2]|0;r=c[o+((r|1)<<2)>>2]|0;t=we(r|0,((r|0)<0)<<31>>31|0,t|0,((t|0)<0)<<31>>31|0)|0;r=F;u=c[o+(k<<2)>>2]|0;s=c[o+(j<<2)>>2]|0;u=we(s|0,((s|0)<0)<<31>>31|0,u|0,((u|0)<0)<<31>>31|0)|0;s=F;if(!(r>>>0<s>>>0|(r|0)==(s|0)&t>>>0<u>>>0)){g=10;break}c[e+(b<<2)>>2]=p;c[(c[a>>2]|0)+(p<<2)>>2]=b;m=n<<1|1;o=c[l>>2]|0;if((m|0)>=(o|0)){b=n;g=10;break}else b=n}if((g|0)==10){u=e+(b<<2)|0;c[u>>2]=f;u=c[a>>2]|0;u=u+(f<<2)|0;c[u>>2]=b;i=d;return}}function Xc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0;d=i;h=c[a>>2]|0;if(h){e=a+4|0;f=c[e>>2]|0;a:do if((f|0)>0){g=0;while(1){j=h+(g*12|0)|0;k=c[j>>2]|0;if(k){c[h+(g*12|0)+4>>2]=0;Td(k);c[j>>2]=0;c[h+(g*12|0)+8>>2]=0;f=c[e>>2]|0}g=g+1|0;if((g|0)>=(f|0))break a;h=c[a>>2]|0}}while(0);c[e>>2]=0;if(b){Td(c[a>>2]|0);c[a>>2]=0;c[a+8>>2]=0}}e=a+16|0;f=c[e>>2]|0;if((f|0)!=0?(c[a+20>>2]=0,b):0){Td(f);c[e>>2]=0;c[a+24>>2]=0}f=a+32|0;e=c[f>>2]|0;if(!e){i=d;return}c[a+36>>2]=0;if(!b){i=d;return}Td(e);c[f>>2]=0;c[a+40>>2]=0;i=d;return}function Yc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;e=i;f=c[a>>2]|0;d=a+4|0;if(f){c[d>>2]=0;if(b){Td(f);c[a>>2]=0;c[a+8>>2]=0;f=0}}else f=0;if((c[d>>2]|0)>=1){h=a+16|0;c[h>>2]=0;h=a+12|0;c[h>>2]=0;i=e;return}h=a+8|0;g=c[h>>2]|0;if((g|0)<1){j=2-g&-2;b=(g>>1)+2&-2;b=(j|0)>(b|0)?j:b;if((b|0)>(2147483647-g|0)){j=va(1)|0;Ta(j|0,48,0)}j=b+g|0;c[h>>2]=j;f=Ud(f,j<<2)|0;c[a>>2]=f;if((f|0)==0?(c[(Oa()|0)>>2]|0)==12:0){j=va(1)|0;Ta(j|0,48,0)}}b=c[d>>2]|0;if((b|0)<1)while(1){g=f+(b<<2)|0;if(g)c[g>>2]=0;if(!b)break;else b=b+1|0}c[d>>2]=1;j=a+16|0;c[j>>2]=0;j=a+12|0;c[j>>2]=0;i=e;return}function Zc(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;e=i;i=i+16|0;d=e;f=a+16|0;j=c[f>>2]|0;c[f>>2]=j+1;c[(c[a>>2]|0)+(j<<2)>>2]=b;j=c[f>>2]|0;b=a+4|0;h=c[b>>2]|0;if((j|0)==(h|0)){c[f>>2]=0;j=0}g=a+12|0;if((c[g>>2]|0)!=(j|0)){i=e;return}Qc(d,(h*3|0)+1>>1);l=c[g>>2]|0;m=c[b>>2]|0;if((l|0)<(m|0)){j=c[a>>2]|0;k=c[d>>2]|0;m=0;while(1){h=m+1|0;c[k+(m<<2)>>2]=c[j+(l<<2)>>2];l=l+1|0;m=c[b>>2]|0;if((l|0)>=(m|0)){k=h;break}else m=h}}else k=0;h=c[a>>2]|0;if((c[f>>2]|0)>0){j=c[d>>2]|0;l=0;while(1){c[j+(k<<2)>>2]=c[h+(l<<2)>>2];l=l+1|0;if((l|0)>=(c[f>>2]|0))break;else k=k+1|0}m=c[b>>2]|0}c[g>>2]=0;c[f>>2]=m;if(!h)f=a+8|0;else{c[b>>2]=0;Td(h);c[a>>2]=0;f=a+8|0;c[f>>2]=0}c[a>>2]=c[d>>2];l=d+4|0;c[b>>2]=c[l>>2];m=d+8|0;c[f>>2]=c[m>>2];c[d>>2]=0;c[l>>2]=0;c[m>>2]=0;i=e;return}function _c(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;d=i;e=a+4|0;f=c[e>>2]|0;g=a+8|0;h=c[g>>2]|0;if((f|0)==(h|0)&(h|0)<(f+1|0)){h=(f>>1)+2&-2;h=(h|0)<2?2:h;if((h|0)>(2147483647-f|0)){h=va(1)|0;Ta(h|0,48,0)}j=c[a>>2]|0;f=h+f|0;c[g>>2]=f;f=Ud(j,f<<2)|0;c[a>>2]=f;if((f|0)==0?(c[(Oa()|0)>>2]|0)==12:0){j=va(1)|0;Ta(j|0,48,0)}}else f=c[a>>2]|0;j=c[e>>2]|0;c[e>>2]=j+1;e=f+(j<<2)|0;if(!e){i=d;return}c[e>>2]=c[b>>2];i=d;return}function $c(){var a=0,b=0;b=i;Ka(3864)|0;a=od(936)|0;xc(a);i=b;return a|0}function ad(a){a=a|0;var b=0;b=i;if(!a){i=b;return}gb[c[(c[a>>2]|0)+4>>2]&31](a);i=b;return}function bd(){var b=0,d=0,e=0;b=i;i=i+16|0;d=b;e=od(936)|0;xc(e);c[964]=e;Cc(e,1)|0;e=c[964]|0;a[d+0>>0]=a[3840]|0;Ac(e,d,1)|0;i=b;return}function cd(b){b=b|0;var d=0,e=0,f=0;d=i;i=i+16|0;e=d;if((c[962]|0)>=(b|0)){i=d;return}do{f=c[964]|0;a[e+0>>0]=a[3840]|0;Ac(f,e,1)|0;f=(c[962]|0)+1|0;c[962]=f}while((f|0)<(b|0));i=d;return}function dd(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;g=i;i=i+32|0;h=g+16|0;e=g+4|0;j=g;c[e>>2]=0;f=e+4|0;c[f>>2]=0;d=e+8|0;c[d>>2]=0;k=c[b>>2]|0;if(k)do{l=(k|0)<0?0-k|0:k;if((c[962]|0)<(l|0))do{m=c[964]|0;a[h+0>>0]=a[3840]|0;Ac(m,h,1)|0;m=(c[962]|0)+1|0;c[962]=m}while((m|0)<(l|0));c[j>>2]=l<<1|k>>>31;mc(e,j);b=b+4|0;k=c[b>>2]|0}while((k|0)!=0);j=c[964]|0;h=j+628|0;ld(e,h);h=Dc(j,h)|0;j=c[e>>2]|0;if(!j){i=g;return h|0}c[f>>2]=0;Td(j);c[e>>2]=0;c[d>>2]=0;i=g;return h|0}function ed(){var b=0,d=0,e=0,f=0;d=i;i=i+16|0;b=d;e=c[964]|0;f=e+664|0;c[f+0>>2]=-1;c[f+4>>2]=-1;c[f+8>>2]=-1;c[f+12>>2]=-1;if(c[e+304>>2]|0)c[e+308>>2]=0;Bc(b,e,1,0);i=d;return(a[b>>0]|0)==0|0}function fd(){return(c[(c[964]|0)+4>>2]|0)+1|0}function gd(){return c[962]|0}function hd(b){b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0;d=i;i=i+32|0;h=d+16|0;f=d+4|0;j=d;c[f>>2]=0;e=f+4|0;c[e>>2]=0;g=f+8|0;c[g>>2]=0;c[j>>2]=b<<1;mc(f,j);b=c[964]|0;j=b+664|0;c[j+0>>2]=-1;c[j+4>>2]=-1;c[j+8>>2]=-1;c[j+12>>2]=-1;ld(f,b+304|0);Bc(h,b,1,0);b=(a[h>>0]|0)==0;h=c[f>>2]|0;if(!h){i=d;return b|0}c[e>>2]=0;Td(h);c[f>>2]=0;c[g>>2]=0;i=d;return b|0}function id(a){a=a|0;var b=0,d=0,e=0;b=i;i=i+16|0;e=b;d=c[964]|0;c[e>>2]=a<<1|1;a=d+628|0;if(c[a>>2]|0)c[d+632>>2]=0;mc(a,e);Dc(d,a)|0;i=b;return}function jd(){return c[(c[964]|0)+36>>2]|0}function kd(){return c[(c[964]|0)+32>>2]|0}function ld(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0;d=i;h=c[b>>2]|0;e=b+4|0;if(!h)j=c[e>>2]|0;else{c[e>>2]=0;j=0}e=a+4|0;f=c[e>>2]|0;g=b+4|0;if((j|0)<(f|0)){k=b+8|0;j=c[k>>2]|0;if((j|0)<(f|0)){m=f+1-j&-2;l=(j>>1)+2&-2;l=(m|0)>(l|0)?m:l;if((l|0)>(2147483647-j|0)){m=va(1)|0;Ta(m|0,48,0)}m=l+j|0;c[k>>2]=m;h=Ud(h,m<<2)|0;c[b>>2]=h;if((h|0)==0?(c[(Oa()|0)>>2]|0)==12:0){m=va(1)|0;Ta(m|0,48,0)}}j=c[g>>2]|0;a:do if((j|0)<(f|0))while(1){h=h+(j<<2)|0;if(h)c[h>>2]=0;j=j+1|0;if((j|0)==(f|0))break a;h=c[b>>2]|0}while(0);c[g>>2]=f;f=c[e>>2]|0}if((f|0)<=0){i=d;return}b=c[b>>2]|0;a=c[a>>2]|0;f=0;do{c[b+(f<<2)>>2]=c[a+(f<<2)>>2];f=f+1|0}while((f|0)<(c[e>>2]|0));i=d;return}function md(a,b){a=a|0;b=b|0;var d=0;d=i;i=i+16|0;c[d>>2]=b;b=c[p>>2]|0;ua(b|0,a|0,d|0)|0;Sa(10,b|0)|0;Wa()}function nd(){var a=0,b=0;a=i;i=i+16|0;if(!(Ja(4064,3)|0)){b=Ha(c[1014]|0)|0;i=a;return b|0}else md(4072,a);return 0}function od(a){a=a|0;var b=0,d=0;b=i;a=(a|0)==0?1:a;d=Sd(a)|0;if(d){i=b;return d|0}while(1){d=vd()|0;if(!d){a=4;break}jb[d&3]();d=Sd(a)|0;if(d){a=5;break}}if((a|0)==4){d=va(4)|0;c[d>>2]=4248;Ta(d|0,4296,12)}else if((a|0)==5){i=b;return d|0}return 0}function pd(a){a=a|0;var b=0;b=i;Td(a);i=b;return}function qd(a){a=a|0;var b=0;b=i;pd(a);i=b;return}function rd(a){a=a|0;return}function sd(a){a=a|0;return 4264}function td(a){a=a|0;var b=0;b=i;i=i+16|0;jb[a&3]();md(4312,b)}function ud(){var a=0,b=0;b=nd()|0;if(((b|0)!=0?(a=c[b>>2]|0,(a|0)!=0):0)?(b=a+48|0,(c[b>>2]&-256|0)==1126902528?(c[b+4>>2]|0)==1129074247:0):0)td(c[a+12>>2]|0);b=c[968]|0;c[968]=b+0;td(b)}function vd(){var a=0;a=c[1102]|0;c[1102]=a+0;return a|0}function wd(a){a=a|0;return}function xd(a){a=a|0;return}function yd(a){a=a|0;return}function zd(a){a=a|0;return}function Ad(a){a=a|0;return}function Bd(a){a=a|0;var b=0;b=i;pd(a);i=b;return}function Cd(a){a=a|0;var b=0;b=i;pd(a);i=b;return}function Dd(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0;e=i;i=i+64|0;f=e;if((a|0)==(b|0)){h=1;i=e;return h|0}if(!b){h=0;i=e;return h|0}b=Hd(b,4504,4560,0)|0;if(!b){h=0;i=e;return h|0}h=f+0|0;g=h+56|0;do{c[h>>2]=0;h=h+4|0}while((h|0)<(g|0));c[f>>2]=b;c[f+8>>2]=a;c[f+12>>2]=-1;c[f+48>>2]=1;mb[c[(c[b>>2]|0)+28>>2]&3](b,f,c[d>>2]|0,1);if((c[f+24>>2]|0)!=1){h=0;i=e;return h|0}c[d>>2]=c[f+16>>2];h=1;i=e;return h|0}function Ed(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0;b=i;g=d+16|0;h=c[g>>2]|0;if(!h){c[g>>2]=e;c[d+24>>2]=f;c[d+36>>2]=1;i=b;return}if((h|0)!=(e|0)){h=d+36|0;c[h>>2]=(c[h>>2]|0)+1;c[d+24>>2]=2;a[d+54>>0]=1;i=b;return}e=d+24|0;if((c[e>>2]|0)!=2){i=b;return}c[e>>2]=f;i=b;return}function Fd(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0;f=i;if((c[b+8>>2]|0)!=(a|0)){i=f;return}Ed(0,b,d,e);i=f;return}function Gd(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0;f=i;if((a|0)==(c[b+8>>2]|0)){Ed(0,b,d,e);i=f;return}else{a=c[a+8>>2]|0;mb[c[(c[a>>2]|0)+28>>2]&3](a,b,d,e);i=f;return}}function Hd(d,e,f,g){d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;h=i;i=i+64|0;j=h;k=c[d>>2]|0;l=d+(c[k+ -8>>2]|0)|0;k=c[k+ -4>>2]|0;c[j>>2]=f;c[j+4>>2]=d;c[j+8>>2]=e;c[j+12>>2]=g;n=j+16|0;o=j+20|0;e=j+24|0;m=j+28|0;g=j+32|0;d=j+40|0;p=(k|0)==(f|0);q=n+0|0;f=q+36|0;do{c[q>>2]=0;q=q+4|0}while((q|0)<(f|0));b[n+36>>1]=0;a[n+38>>0]=0;if(p){c[j+48>>2]=1;kb[c[(c[k>>2]|0)+20>>2]&3](k,j,l,l,1,0);q=(c[e>>2]|0)==1?l:0;i=h;return q|0}fb[c[(c[k>>2]|0)+24>>2]&3](k,j,l,1,0);j=c[j+36>>2]|0;if(!j){q=(c[d>>2]|0)==1&(c[m>>2]|0)==1&(c[g>>2]|0)==1?c[o>>2]|0:0;i=h;return q|0}else if((j|0)==1){if((c[e>>2]|0)!=1?!((c[d>>2]|0)==0&(c[m>>2]|0)==1&(c[g>>2]|0)==1):0){q=0;i=h;return q|0}q=c[n>>2]|0;i=h;return q|0}else{q=0;i=h;return q|0}return 0}function Id(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0;b=i;a[d+53>>0]=1;if((c[d+4>>2]|0)!=(f|0)){i=b;return}a[d+52>>0]=1;f=d+16|0;h=c[f>>2]|0;if(!h){c[f>>2]=e;c[d+24>>2]=g;c[d+36>>2]=1;if(!((g|0)==1?(c[d+48>>2]|0)==1:0)){i=b;return}a[d+54>>0]=1;i=b;return}if((h|0)!=(e|0)){h=d+36|0;c[h>>2]=(c[h>>2]|0)+1;a[d+54>>0]=1;i=b;return}e=d+24|0;f=c[e>>2]|0;if((f|0)==2)c[e>>2]=g;else g=f;if(!((g|0)==1?(c[d+48>>2]|0)==1:0)){i=b;return}a[d+54>>0]=1;i=b;return}function Jd(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0,j=0,k=0,l=0,m=0;h=i;if((b|0)==(c[d+8>>2]|0)){if((c[d+4>>2]|0)!=(e|0)){i=h;return}j=d+28|0;if((c[j>>2]|0)==1){i=h;return}c[j>>2]=f;i=h;return}if((b|0)!=(c[d>>2]|0)){l=c[b+8>>2]|0;fb[c[(c[l>>2]|0)+24>>2]&3](l,d,e,f,g);i=h;return}if((c[d+16>>2]|0)!=(e|0)?(k=d+20|0,(c[k>>2]|0)!=(e|0)):0){c[d+32>>2]=f;f=d+44|0;if((c[f>>2]|0)==4){i=h;return}l=d+52|0;a[l>>0]=0;m=d+53|0;a[m>>0]=0;b=c[b+8>>2]|0;kb[c[(c[b>>2]|0)+20>>2]&3](b,d,e,e,1,g);if(a[m>>0]|0){if(!(a[l>>0]|0)){b=1;j=13}}else{b=0;j=13}do if((j|0)==13){c[k>>2]=e;m=d+40|0;c[m>>2]=(c[m>>2]|0)+1;if((c[d+36>>2]|0)==1?(c[d+24>>2]|0)==2:0){a[d+54>>0]=1;if(b)break}else j=16;if((j|0)==16?b:0)break;c[f>>2]=4;i=h;return}while(0);c[f>>2]=3;i=h;return}if((f|0)!=1){i=h;return}c[d+32>>2]=1;i=h;return}function Kd(b,d,e,f,g){b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0;g=i;if((c[d+8>>2]|0)==(b|0)){if((c[d+4>>2]|0)!=(e|0)){i=g;return}d=d+28|0;if((c[d>>2]|0)==1){i=g;return}c[d>>2]=f;i=g;return}if((c[d>>2]|0)!=(b|0)){i=g;return}if((c[d+16>>2]|0)!=(e|0)?(h=d+20|0,(c[h>>2]|0)!=(e|0)):0){c[d+32>>2]=f;c[h>>2]=e;b=d+40|0;c[b>>2]=(c[b>>2]|0)+1;if((c[d+36>>2]|0)==1?(c[d+24>>2]|0)==2:0)a[d+54>>0]=1;c[d+44>>2]=4;i=g;return}if((f|0)!=1){i=g;return}c[d+32>>2]=1;i=g;return}function Ld(a,b,d,e,f,g){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;var h=0;h=i;if((a|0)==(c[b+8>>2]|0)){Id(0,b,d,e,f);i=h;return}else{a=c[a+8>>2]|0;kb[c[(c[a>>2]|0)+20>>2]&3](a,b,d,e,f,g);i=h;return}}function Md(a,b,d,e,f,g){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;g=g|0;g=i;if((c[b+8>>2]|0)!=(a|0)){i=g;return}Id(0,b,d,e,f);i=g;return}function Nd(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0;e=i;i=i+16|0;f=e;c[f>>2]=c[d>>2];a=eb[c[(c[a>>2]|0)+16>>2]&1](a,b,f)|0;b=a&1;if(!a){i=e;return b|0}c[d>>2]=c[f>>2];i=e;return b|0}function Od(a){a=a|0;var b=0;b=i;if(!a)a=0;else a=(Hd(a,4504,4672,0)|0)!=0;i=b;return a&1|0}function Pd(){var a=0,b=0,d=0,e=0,f=0;a=i;i=i+16|0;b=a;a=a+12|0;d=nd()|0;if(!d)md(4040,b);d=c[d>>2]|0;if(!d)md(4040,b);f=d+48|0;e=c[f>>2]|0;f=c[f+4>>2]|0;if(!((e&-256|0)==1126902528&(f|0)==1129074247)){c[b>>2]=c[970];md(4e3,b)}if((e|0)==1126902529&(f|0)==1129074247)e=c[d+44>>2]|0;else e=d+80|0;c[a>>2]=e;f=c[d>>2]|0;d=c[f+4>>2]|0;if(eb[c[(c[4432>>2]|0)+16>>2]&1](4432,f,a)|0){f=c[a>>2]|0;e=c[970]|0;f=ib[c[(c[f>>2]|0)+8>>2]&1](f)|0;c[b>>2]=e;c[b+4>>2]=d;c[b+8>>2]=f;md(3904,b)}else{c[b>>2]=c[970];c[b+4>>2]=d;md(3952,b)}}function Qd(){var a=0;a=i;i=i+16|0;if(!(Ma(4056,20)|0)){i=a;return}else md(4128,a)}function Rd(a){a=a|0;var b=0;b=i;i=i+16|0;Td(a);if(!(Pa(c[1014]|0,0)|0)){i=b;return}else md(4184,b)}function Sd(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0;b=i;do if(a>>>0<245){if(a>>>0<11)a=16;else a=a+11&-8;x=a>>>3;p=c[1206]|0;w=p>>>x;if(w&3){g=(w&1^1)+x|0;f=g<<1;d=4864+(f<<2)|0;f=4864+(f+2<<2)|0;h=c[f>>2]|0;j=h+8|0;e=c[j>>2]|0;do if((d|0)!=(e|0)){if(e>>>0<(c[1210]|0)>>>0)Wa();k=e+12|0;if((c[k>>2]|0)==(h|0)){c[k>>2]=d;c[f>>2]=e;break}else Wa()}else c[1206]=p&~(1<<g);while(0);H=g<<3;c[h+4>>2]=H|3;H=h+(H|4)|0;c[H>>2]=c[H>>2]|1;H=j;i=b;return H|0}v=c[1208]|0;if(a>>>0>v>>>0){if(w){h=2<<x;h=w<<x&(h|0-h);h=(h&0-h)+ -1|0;d=h>>>12&16;h=h>>>d;j=h>>>5&8;h=h>>>j;f=h>>>2&4;h=h>>>f;g=h>>>1&2;h=h>>>g;e=h>>>1&1;e=(j|d|f|g|e)+(h>>>e)|0;h=e<<1;g=4864+(h<<2)|0;h=4864+(h+2<<2)|0;f=c[h>>2]|0;d=f+8|0;j=c[d>>2]|0;do if((g|0)!=(j|0)){if(j>>>0<(c[1210]|0)>>>0)Wa();k=j+12|0;if((c[k>>2]|0)==(f|0)){c[k>>2]=g;c[h>>2]=j;E=c[1208]|0;break}else Wa()}else{c[1206]=p&~(1<<e);E=v}while(0);H=e<<3;e=H-a|0;c[f+4>>2]=a|3;g=f+a|0;c[f+(a|4)>>2]=e|1;c[f+H>>2]=e;if(E){f=c[1211]|0;l=E>>>3;j=l<<1;h=4864+(j<<2)|0;k=c[1206]|0;l=1<<l;if(k&l){j=4864+(j+2<<2)|0;k=c[j>>2]|0;if(k>>>0<(c[1210]|0)>>>0)Wa();else{D=j;C=k}}else{c[1206]=k|l;D=4864+(j+2<<2)|0;C=h}c[D>>2]=f;c[C+12>>2]=f;c[f+8>>2]=C;c[f+12>>2]=h}c[1208]=e;c[1211]=g;H=d;i=b;return H|0}p=c[1207]|0;if(p){d=(p&0-p)+ -1|0;G=d>>>12&16;d=d>>>G;F=d>>>5&8;d=d>>>F;H=d>>>2&4;d=d>>>H;f=d>>>1&2;d=d>>>f;e=d>>>1&1;e=c[5128+((F|G|H|f|e)+(d>>>e)<<2)>>2]|0;d=(c[e+4>>2]&-8)-a|0;f=e;while(1){g=c[f+16>>2]|0;if(!g){g=c[f+20>>2]|0;if(!g)break}f=(c[g+4>>2]&-8)-a|0;H=f>>>0<d>>>0;d=H?f:d;f=g;e=H?g:e}h=c[1210]|0;if(e>>>0<h>>>0)Wa();f=e+a|0;if(e>>>0>=f>>>0)Wa();g=c[e+24>>2]|0;k=c[e+12>>2]|0;do if((k|0)==(e|0)){k=e+20|0;j=c[k>>2]|0;if(!j){k=e+16|0;j=c[k>>2]|0;if(!j){B=0;break}}while(1){l=j+20|0;m=c[l>>2]|0;if(m){j=m;k=l;continue}l=j+16|0;m=c[l>>2]|0;if(!m)break;else{j=m;k=l}}if(k>>>0<h>>>0)Wa();else{c[k>>2]=0;B=j;break}}else{j=c[e+8>>2]|0;if(j>>>0<h>>>0)Wa();h=j+12|0;if((c[h>>2]|0)!=(e|0))Wa();l=k+8|0;if((c[l>>2]|0)==(e|0)){c[h>>2]=k;c[l>>2]=j;B=k;break}else Wa()}while(0);do if(g){j=c[e+28>>2]|0;h=5128+(j<<2)|0;if((e|0)==(c[h>>2]|0)){c[h>>2]=B;if(!B){c[1207]=c[1207]&~(1<<j);break}}else{if(g>>>0<(c[1210]|0)>>>0)Wa();h=g+16|0;if((c[h>>2]|0)==(e|0))c[h>>2]=B;else c[g+20>>2]=B;if(!B)break}h=c[1210]|0;if(B>>>0<h>>>0)Wa();c[B+24>>2]=g;g=c[e+16>>2]|0;do if(g)if(g>>>0<h>>>0)Wa();else{c[B+16>>2]=g;c[g+24>>2]=B;break}while(0);g=c[e+20>>2]|0;if(g)if(g>>>0<(c[1210]|0)>>>0)Wa();else{c[B+20>>2]=g;c[g+24>>2]=B;break}}while(0);if(d>>>0<16){H=d+a|0;c[e+4>>2]=H|3;H=e+(H+4)|0;c[H>>2]=c[H>>2]|1}else{c[e+4>>2]=a|3;c[e+(a|4)>>2]=d|1;c[e+(d+a)>>2]=d;h=c[1208]|0;if(h){g=c[1211]|0;k=h>>>3;l=k<<1;h=4864+(l<<2)|0;j=c[1206]|0;k=1<<k;if(j&k){j=4864+(l+2<<2)|0;k=c[j>>2]|0;if(k>>>0<(c[1210]|0)>>>0)Wa();else{A=j;z=k}}else{c[1206]=j|k;A=4864+(l+2<<2)|0;z=h}c[A>>2]=g;c[z+12>>2]=g;c[g+8>>2]=z;c[g+12>>2]=h}c[1208]=d;c[1211]=f}H=e+8|0;i=b;return H|0}}}else if(a>>>0<=4294967231){z=a+11|0;a=z&-8;B=c[1207]|0;if(B){A=0-a|0;z=z>>>8;if(z)if(a>>>0>16777215)C=31;else{G=(z+1048320|0)>>>16&8;H=z<<G;F=(H+520192|0)>>>16&4;H=H<<F;C=(H+245760|0)>>>16&2;C=14-(F|G|C)+(H<<C>>>15)|0;C=a>>>(C+7|0)&1|C<<1}else C=0;D=c[5128+(C<<2)>>2]|0;a:do if(!D){F=0;z=0}else{if((C|0)==31)z=0;else z=25-(C>>>1)|0;F=0;E=a<<z;z=0;while(1){G=c[D+4>>2]&-8;H=G-a|0;if(H>>>0<A>>>0)if((G|0)==(a|0)){A=H;F=D;z=D;break a}else{A=H;z=D}H=c[D+20>>2]|0;D=c[D+(E>>>31<<2)+16>>2]|0;F=(H|0)==0|(H|0)==(D|0)?F:H;if(!D)break;else E=E<<1}}while(0);if((F|0)==0&(z|0)==0){H=2<<C;B=B&(H|0-H);if(!B)break;H=(B&0-B)+ -1|0;D=H>>>12&16;H=H>>>D;C=H>>>5&8;H=H>>>C;E=H>>>2&4;H=H>>>E;G=H>>>1&2;H=H>>>G;F=H>>>1&1;F=c[5128+((C|D|E|G|F)+(H>>>F)<<2)>>2]|0}if(F)while(1){H=(c[F+4>>2]&-8)-a|0;B=H>>>0<A>>>0;A=B?H:A;z=B?F:z;B=c[F+16>>2]|0;if(B){F=B;continue}F=c[F+20>>2]|0;if(!F)break}if((z|0)!=0?A>>>0<((c[1208]|0)-a|0)>>>0:0){f=c[1210]|0;if(z>>>0<f>>>0)Wa();d=z+a|0;if(z>>>0>=d>>>0)Wa();e=c[z+24>>2]|0;g=c[z+12>>2]|0;do if((g|0)==(z|0)){h=z+20|0;g=c[h>>2]|0;if(!g){h=z+16|0;g=c[h>>2]|0;if(!g){x=0;break}}while(1){j=g+20|0;k=c[j>>2]|0;if(k){g=k;h=j;continue}j=g+16|0;k=c[j>>2]|0;if(!k)break;else{g=k;h=j}}if(h>>>0<f>>>0)Wa();else{c[h>>2]=0;x=g;break}}else{h=c[z+8>>2]|0;if(h>>>0<f>>>0)Wa();j=h+12|0;if((c[j>>2]|0)!=(z|0))Wa();f=g+8|0;if((c[f>>2]|0)==(z|0)){c[j>>2]=g;c[f>>2]=h;x=g;break}else Wa()}while(0);do if(e){f=c[z+28>>2]|0;g=5128+(f<<2)|0;if((z|0)==(c[g>>2]|0)){c[g>>2]=x;if(!x){c[1207]=c[1207]&~(1<<f);break}}else{if(e>>>0<(c[1210]|0)>>>0)Wa();f=e+16|0;if((c[f>>2]|0)==(z|0))c[f>>2]=x;else c[e+20>>2]=x;if(!x)break}f=c[1210]|0;if(x>>>0<f>>>0)Wa();c[x+24>>2]=e;e=c[z+16>>2]|0;do if(e)if(e>>>0<f>>>0)Wa();else{c[x+16>>2]=e;c[e+24>>2]=x;break}while(0);e=c[z+20>>2]|0;if(e)if(e>>>0<(c[1210]|0)>>>0)Wa();else{c[x+20>>2]=e;c[e+24>>2]=x;break}}while(0);b:do if(A>>>0>=16){c[z+4>>2]=a|3;c[z+(a|4)>>2]=A|1;c[z+(A+a)>>2]=A;f=A>>>3;if(A>>>0<256){h=f<<1;e=4864+(h<<2)|0;g=c[1206]|0;f=1<<f;do if(!(g&f)){c[1206]=g|f;w=4864+(h+2<<2)|0;v=e}else{f=4864+(h+2<<2)|0;g=c[f>>2]|0;if(g>>>0>=(c[1210]|0)>>>0){w=f;v=g;break}Wa()}while(0);c[w>>2]=d;c[v+12>>2]=d;c[z+(a+8)>>2]=v;c[z+(a+12)>>2]=e;break}e=A>>>8;if(e)if(A>>>0>16777215)e=31;else{G=(e+1048320|0)>>>16&8;H=e<<G;F=(H+520192|0)>>>16&4;H=H<<F;e=(H+245760|0)>>>16&2;e=14-(F|G|e)+(H<<e>>>15)|0;e=A>>>(e+7|0)&1|e<<1}else e=0;f=5128+(e<<2)|0;c[z+(a+28)>>2]=e;c[z+(a+20)>>2]=0;c[z+(a+16)>>2]=0;g=c[1207]|0;h=1<<e;if(!(g&h)){c[1207]=g|h;c[f>>2]=d;c[z+(a+24)>>2]=f;c[z+(a+12)>>2]=d;c[z+(a+8)>>2]=d;break}h=c[f>>2]|0;if((e|0)==31)e=0;else e=25-(e>>>1)|0;c:do if((c[h+4>>2]&-8|0)!=(A|0)){e=A<<e;while(1){g=h+(e>>>31<<2)+16|0;f=c[g>>2]|0;if(!f)break;if((c[f+4>>2]&-8|0)==(A|0)){p=f;break c}else{e=e<<1;h=f}}if(g>>>0<(c[1210]|0)>>>0)Wa();else{c[g>>2]=d;c[z+(a+24)>>2]=h;c[z+(a+12)>>2]=d;c[z+(a+8)>>2]=d;break b}}else p=h;while(0);f=p+8|0;e=c[f>>2]|0;H=c[1210]|0;if(p>>>0>=H>>>0&e>>>0>=H>>>0){c[e+12>>2]=d;c[f>>2]=d;c[z+(a+8)>>2]=e;c[z+(a+12)>>2]=p;c[z+(a+24)>>2]=0;break}else Wa()}else{H=A+a|0;c[z+4>>2]=H|3;H=z+(H+4)|0;c[H>>2]=c[H>>2]|1}while(0);H=z+8|0;i=b;return H|0}}}else a=-1;while(0);p=c[1208]|0;if(p>>>0>=a>>>0){e=p-a|0;d=c[1211]|0;if(e>>>0>15){c[1211]=d+a;c[1208]=e;c[d+(a+4)>>2]=e|1;c[d+p>>2]=e;c[d+4>>2]=a|3}else{c[1208]=0;c[1211]=0;c[d+4>>2]=p|3;H=d+(p+4)|0;c[H>>2]=c[H>>2]|1}H=d+8|0;i=b;return H|0}p=c[1209]|0;if(p>>>0>a>>>0){G=p-a|0;c[1209]=G;H=c[1212]|0;c[1212]=H+a;c[H+(a+4)>>2]=G|1;c[H+4>>2]=a|3;H=H+8|0;i=b;return H|0}do if(!(c[1324]|0)){p=Ga(30)|0;if(!(p+ -1&p)){c[1326]=p;c[1325]=p;c[1327]=-1;c[1328]=-1;c[1329]=0;c[1317]=0;c[1324]=(Ya(0)|0)&-16^1431655768;break}else Wa()}while(0);x=a+48|0;p=c[1326]|0;w=a+47|0;A=p+w|0;p=0-p|0;v=A&p;if(v>>>0<=a>>>0){H=0;i=b;return H|0}z=c[1316]|0;if((z|0)!=0?(G=c[1314]|0,H=G+v|0,H>>>0<=G>>>0|H>>>0>z>>>0):0){H=0;i=b;return H|0}d:do if(!(c[1317]&4)){B=c[1212]|0;e:do if(B){z=5272|0;while(1){C=c[z>>2]|0;if(C>>>0<=B>>>0?(y=z+4|0,(C+(c[y>>2]|0)|0)>>>0>B>>>0):0)break;z=c[z+8>>2]|0;if(!z){o=181;break e}}if(z){A=A-(c[1209]|0)&p;if(A>>>0<2147483647){p=Aa(A|0)|0;if((p|0)==((c[z>>2]|0)+(c[y>>2]|0)|0)){z=A;o=190}else{z=A;o=191}}else z=0}else o=181}else o=181;while(0);do if((o|0)==181){y=Aa(0)|0;if((y|0)!=(-1|0)){A=y;z=c[1325]|0;p=z+ -1|0;if(!(p&A))z=v;else z=v-A+(p+A&0-z)|0;p=c[1314]|0;A=p+z|0;if(z>>>0>a>>>0&z>>>0<2147483647){H=c[1316]|0;if((H|0)!=0?A>>>0<=p>>>0|A>>>0>H>>>0:0){z=0;break}p=Aa(z|0)|0;if((p|0)==(y|0)){p=y;o=190}else o=191}else z=0}else z=0}while(0);f:do if((o|0)==190){if((p|0)!=(-1|0)){q=z;o=201;break d}}else if((o|0)==191){o=0-z|0;do if((p|0)!=(-1|0)&z>>>0<2147483647&x>>>0>z>>>0?(u=c[1326]|0,u=w-z+u&0-u,u>>>0<2147483647):0)if((Aa(u|0)|0)==(-1|0)){Aa(o|0)|0;z=0;break f}else{z=u+z|0;break}while(0);if((p|0)==(-1|0))z=0;else{q=z;o=201;break d}}while(0);c[1317]=c[1317]|4;o=198}else{z=0;o=198}while(0);if((((o|0)==198?v>>>0<2147483647:0)?(t=Aa(v|0)|0,s=Aa(0)|0,(t|0)!=(-1|0)&(s|0)!=(-1|0)&t>>>0<s>>>0):0)?(r=s-t|0,q=r>>>0>(a+40|0)>>>0,q):0){p=t;q=q?r:z;o=201}if((o|0)==201){r=(c[1314]|0)+q|0;c[1314]=r;if(r>>>0>(c[1315]|0)>>>0)c[1315]=r;r=c[1212]|0;g:do if(r){t=5272|0;while(1){s=c[t>>2]|0;v=t+4|0;w=c[v>>2]|0;if((p|0)==(s+w|0)){o=213;break}u=c[t+8>>2]|0;if(!u)break;else t=u}if(((o|0)==213?(c[t+12>>2]&8|0)==0:0)?r>>>0>=s>>>0&r>>>0<p>>>0:0){c[v>>2]=w+q;d=(c[1209]|0)+q|0;e=r+8|0;if(!(e&7))e=0;else e=0-e&7;H=d-e|0;c[1212]=r+e;c[1209]=H;c[r+(e+4)>>2]=H|1;c[r+(d+4)>>2]=40;c[1213]=c[1328];break}s=c[1210]|0;if(p>>>0<s>>>0){c[1210]=p;s=p}v=p+q|0;t=5272|0;while(1){if((c[t>>2]|0)==(v|0)){o=223;break}u=c[t+8>>2]|0;if(!u)break;else t=u}if((o|0)==223?(c[t+12>>2]&8|0)==0:0){c[t>>2]=p;h=t+4|0;c[h>>2]=(c[h>>2]|0)+q;h=p+8|0;if(!(h&7))h=0;else h=0-h&7;j=p+(q+8)|0;if(!(j&7))n=0;else n=0-j&7;o=p+(n+q)|0;k=h+a|0;j=p+k|0;m=o-(p+h)-a|0;c[p+(h+4)>>2]=a|3;h:do if((o|0)!=(r|0)){if((o|0)==(c[1211]|0)){H=(c[1208]|0)+m|0;c[1208]=H;c[1211]=j;c[p+(k+4)>>2]=H|1;c[p+(H+k)>>2]=H;break}r=q+4|0;u=c[p+(r+n)>>2]|0;if((u&3|0)==1){a=u&-8;t=u>>>3;i:do if(u>>>0>=256){l=c[p+((n|24)+q)>>2]|0;t=c[p+(q+12+n)>>2]|0;do if((t|0)==(o|0)){v=n|16;u=p+(r+v)|0;t=c[u>>2]|0;if(!t){u=p+(v+q)|0;t=c[u>>2]|0;if(!t){g=0;break}}while(1){w=t+20|0;v=c[w>>2]|0;if(v){t=v;u=w;continue}w=t+16|0;v=c[w>>2]|0;if(!v)break;else{t=v;u=w}}if(u>>>0<s>>>0)Wa();else{c[u>>2]=0;g=t;break}}else{u=c[p+((n|8)+q)>>2]|0;if(u>>>0<s>>>0)Wa();v=u+12|0;if((c[v>>2]|0)!=(o|0))Wa();s=t+8|0;if((c[s>>2]|0)==(o|0)){c[v>>2]=t;c[s>>2]=u;g=t;break}else Wa()}while(0);if(!l)break;s=c[p+(q+28+n)>>2]|0;t=5128+(s<<2)|0;do if((o|0)!=(c[t>>2]|0)){if(l>>>0<(c[1210]|0)>>>0)Wa();s=l+16|0;if((c[s>>2]|0)==(o|0))c[s>>2]=g;else c[l+20>>2]=g;if(!g)break i}else{c[t>>2]=g;if(g)break;c[1207]=c[1207]&~(1<<s);break i}while(0);o=c[1210]|0;if(g>>>0<o>>>0)Wa();c[g+24>>2]=l;s=n|16;l=c[p+(s+q)>>2]|0;do if(l)if(l>>>0<o>>>0)Wa();else{c[g+16>>2]=l;c[l+24>>2]=g;break}while(0);l=c[p+(r+s)>>2]|0;if(!l)break;if(l>>>0<(c[1210]|0)>>>0)Wa();else{c[g+20>>2]=l;c[l+24>>2]=g;break}}else{g=c[p+((n|8)+q)>>2]|0;r=c[p+(q+12+n)>>2]|0;u=4864+(t<<1<<2)|0;do if((g|0)!=(u|0)){if(g>>>0<s>>>0)Wa();if((c[g+12>>2]|0)==(o|0))break;Wa()}while(0);if((r|0)==(g|0)){c[1206]=c[1206]&~(1<<t);break}do if((r|0)==(u|0))l=r+8|0;else{if(r>>>0<s>>>0)Wa();s=r+8|0;if((c[s>>2]|0)==(o|0)){l=s;break}Wa()}while(0);c[g+12>>2]=r;c[l>>2]=g}while(0);o=p+((a|n)+q)|0;m=a+m|0}g=o+4|0;c[g>>2]=c[g>>2]&-2;c[p+(k+4)>>2]=m|1;c[p+(m+k)>>2]=m;g=m>>>3;if(m>>>0<256){l=g<<1;d=4864+(l<<2)|0;m=c[1206]|0;g=1<<g;do if(!(m&g)){c[1206]=m|g;f=4864+(l+2<<2)|0;e=d}else{l=4864+(l+2<<2)|0;g=c[l>>2]|0;if(g>>>0>=(c[1210]|0)>>>0){f=l;e=g;break}Wa()}while(0);c[f>>2]=j;c[e+12>>2]=j;c[p+(k+8)>>2]=e;c[p+(k+12)>>2]=d;break}e=m>>>8;do if(!e)e=0;else{if(m>>>0>16777215){e=31;break}G=(e+1048320|0)>>>16&8;H=e<<G;F=(H+520192|0)>>>16&4;H=H<<F;e=(H+245760|0)>>>16&2;e=14-(F|G|e)+(H<<e>>>15)|0;e=m>>>(e+7|0)&1|e<<1}while(0);l=5128+(e<<2)|0;c[p+(k+28)>>2]=e;c[p+(k+20)>>2]=0;c[p+(k+16)>>2]=0;g=c[1207]|0;f=1<<e;if(!(g&f)){c[1207]=g|f;c[l>>2]=j;c[p+(k+24)>>2]=l;c[p+(k+12)>>2]=j;c[p+(k+8)>>2]=j;break}f=c[l>>2]|0;if((e|0)==31)e=0;else e=25-(e>>>1)|0;j:do if((c[f+4>>2]&-8|0)!=(m|0)){e=m<<e;while(1){g=f+(e>>>31<<2)+16|0;l=c[g>>2]|0;if(!l)break;if((c[l+4>>2]&-8|0)==(m|0)){d=l;break j}else{e=e<<1;f=l}}if(g>>>0<(c[1210]|0)>>>0)Wa();else{c[g>>2]=j;c[p+(k+24)>>2]=f;c[p+(k+12)>>2]=j;c[p+(k+8)>>2]=j;break h}}else d=f;while(0);e=d+8|0;f=c[e>>2]|0;H=c[1210]|0;if(d>>>0>=H>>>0&f>>>0>=H>>>0){c[f+12>>2]=j;c[e>>2]=j;c[p+(k+8)>>2]=f;c[p+(k+12)>>2]=d;c[p+(k+24)>>2]=0;break}else Wa()}else{H=(c[1209]|0)+m|0;c[1209]=H;c[1212]=j;c[p+(k+4)>>2]=H|1}while(0);H=p+(h|8)|0;i=b;return H|0}e=5272|0;while(1){d=c[e>>2]|0;if(d>>>0<=r>>>0?(n=c[e+4>>2]|0,m=d+n|0,m>>>0>r>>>0):0)break;e=c[e+8>>2]|0}e=d+(n+ -39)|0;if(!(e&7))e=0;else e=0-e&7;d=d+(n+ -47+e)|0;d=d>>>0<(r+16|0)>>>0?r:d;e=d+8|0;f=p+8|0;if(!(f&7))f=0;else f=0-f&7;H=q+ -40-f|0;c[1212]=p+f;c[1209]=H;c[p+(f+4)>>2]=H|1;c[p+(q+ -36)>>2]=40;c[1213]=c[1328];c[d+4>>2]=27;c[e+0>>2]=c[1318];c[e+4>>2]=c[1319];c[e+8>>2]=c[1320];c[e+12>>2]=c[1321];c[1318]=p;c[1319]=q;c[1321]=0;c[1320]=e;e=d+28|0;c[e>>2]=7;if((d+32|0)>>>0<m>>>0)do{H=e;e=e+4|0;c[e>>2]=7}while((H+8|0)>>>0<m>>>0);if((d|0)!=(r|0)){d=d-r|0;e=r+(d+4)|0;c[e>>2]=c[e>>2]&-2;c[r+4>>2]=d|1;c[r+d>>2]=d;e=d>>>3;if(d>>>0<256){f=e<<1;d=4864+(f<<2)|0;g=c[1206]|0;e=1<<e;do if(!(g&e)){c[1206]=g|e;k=4864+(f+2<<2)|0;j=d}else{f=4864+(f+2<<2)|0;e=c[f>>2]|0;if(e>>>0>=(c[1210]|0)>>>0){k=f;j=e;break}Wa()}while(0);c[k>>2]=r;c[j+12>>2]=r;c[r+8>>2]=j;c[r+12>>2]=d;break}e=d>>>8;if(e)if(d>>>0>16777215)e=31;else{G=(e+1048320|0)>>>16&8;H=e<<G;F=(H+520192|0)>>>16&4;H=H<<F;e=(H+245760|0)>>>16&2;e=14-(F|G|e)+(H<<e>>>15)|0;e=d>>>(e+7|0)&1|e<<1}else e=0;j=5128+(e<<2)|0;c[r+28>>2]=e;c[r+20>>2]=0;c[r+16>>2]=0;f=c[1207]|0;g=1<<e;if(!(f&g)){c[1207]=f|g;c[j>>2]=r;c[r+24>>2]=j;c[r+12>>2]=r;c[r+8>>2]=r;break}f=c[j>>2]|0;if((e|0)==31)e=0;else e=25-(e>>>1)|0;k:do if((c[f+4>>2]&-8|0)!=(d|0)){e=d<<e;j=f;while(1){f=j+(e>>>31<<2)+16|0;g=c[f>>2]|0;if(!g)break;if((c[g+4>>2]&-8|0)==(d|0)){h=g;break k}else{e=e<<1;j=g}}if(f>>>0<(c[1210]|0)>>>0)Wa();else{c[f>>2]=r;c[r+24>>2]=j;c[r+12>>2]=r;c[r+8>>2]=r;break g}}else h=f;while(0);e=h+8|0;d=c[e>>2]|0;H=c[1210]|0;if(h>>>0>=H>>>0&d>>>0>=H>>>0){c[d+12>>2]=r;c[e>>2]=r;c[r+8>>2]=d;c[r+12>>2]=h;c[r+24>>2]=0;break}else Wa()}}else{H=c[1210]|0;if((H|0)==0|p>>>0<H>>>0)c[1210]=p;c[1318]=p;c[1319]=q;c[1321]=0;c[1215]=c[1324];c[1214]=-1;d=0;do{H=d<<1;G=4864+(H<<2)|0;c[4864+(H+3<<2)>>2]=G;c[4864+(H+2<<2)>>2]=G;d=d+1|0}while((d|0)!=32);d=p+8|0;if(!(d&7))d=0;else d=0-d&7;H=q+ -40-d|0;c[1212]=p+d;c[1209]=H;c[p+(d+4)>>2]=H|1;c[p+(q+ -36)>>2]=40;c[1213]=c[1328]}while(0);d=c[1209]|0;if(d>>>0>a>>>0){G=d-a|0;c[1209]=G;H=c[1212]|0;c[1212]=H+a;c[H+(a+4)>>2]=G|1;c[H+4>>2]=a|3;H=H+8|0;i=b;return H|0}}c[(Oa()|0)>>2]=12;H=0;i=b;return H|0}function Td(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0;b=i;if(!a){i=b;return}q=a+ -8|0;r=c[1210]|0;if(q>>>0<r>>>0)Wa();n=c[a+ -4>>2]|0;m=n&3;if((m|0)==1)Wa();j=n&-8;h=a+(j+ -8)|0;do if(!(n&1)){u=c[q>>2]|0;if(!m){i=b;return}q=-8-u|0;n=a+q|0;m=u+j|0;if(n>>>0<r>>>0)Wa();if((n|0)==(c[1211]|0)){e=a+(j+ -4)|0;o=c[e>>2]|0;if((o&3|0)!=3){e=n;o=m;break}c[1208]=m;c[e>>2]=o&-2;c[a+(q+4)>>2]=m|1;c[h>>2]=m;i=b;return}t=u>>>3;if(u>>>0<256){e=c[a+(q+8)>>2]|0;o=c[a+(q+12)>>2]|0;p=4864+(t<<1<<2)|0;if((e|0)!=(p|0)){if(e>>>0<r>>>0)Wa();if((c[e+12>>2]|0)!=(n|0))Wa()}if((o|0)==(e|0)){c[1206]=c[1206]&~(1<<t);e=n;o=m;break}if((o|0)!=(p|0)){if(o>>>0<r>>>0)Wa();p=o+8|0;if((c[p>>2]|0)==(n|0))s=p;else Wa()}else s=o+8|0;c[e+12>>2]=o;c[s>>2]=e;e=n;o=m;break}s=c[a+(q+24)>>2]|0;t=c[a+(q+12)>>2]|0;do if((t|0)==(n|0)){u=a+(q+20)|0;t=c[u>>2]|0;if(!t){u=a+(q+16)|0;t=c[u>>2]|0;if(!t){p=0;break}}while(1){v=t+20|0;w=c[v>>2]|0;if(w){t=w;u=v;continue}v=t+16|0;w=c[v>>2]|0;if(!w)break;else{t=w;u=v}}if(u>>>0<r>>>0)Wa();else{c[u>>2]=0;p=t;break}}else{u=c[a+(q+8)>>2]|0;if(u>>>0<r>>>0)Wa();r=u+12|0;if((c[r>>2]|0)!=(n|0))Wa();v=t+8|0;if((c[v>>2]|0)==(n|0)){c[r>>2]=t;c[v>>2]=u;p=t;break}else Wa()}while(0);if(s){r=c[a+(q+28)>>2]|0;t=5128+(r<<2)|0;if((n|0)==(c[t>>2]|0)){c[t>>2]=p;if(!p){c[1207]=c[1207]&~(1<<r);e=n;o=m;break}}else{if(s>>>0<(c[1210]|0)>>>0)Wa();r=s+16|0;if((c[r>>2]|0)==(n|0))c[r>>2]=p;else c[s+20>>2]=p;if(!p){e=n;o=m;break}}r=c[1210]|0;if(p>>>0<r>>>0)Wa();c[p+24>>2]=s;s=c[a+(q+16)>>2]|0;do if(s)if(s>>>0<r>>>0)Wa();else{c[p+16>>2]=s;c[s+24>>2]=p;break}while(0);q=c[a+(q+20)>>2]|0;if(q)if(q>>>0<(c[1210]|0)>>>0)Wa();else{c[p+20>>2]=q;c[q+24>>2]=p;e=n;o=m;break}else{e=n;o=m}}else{e=n;o=m}}else{e=q;o=j}while(0);if(e>>>0>=h>>>0)Wa();m=a+(j+ -4)|0;n=c[m>>2]|0;if(!(n&1))Wa();if(!(n&2)){if((h|0)==(c[1212]|0)){w=(c[1209]|0)+o|0;c[1209]=w;c[1212]=e;c[e+4>>2]=w|1;if((e|0)!=(c[1211]|0)){i=b;return}c[1211]=0;c[1208]=0;i=b;return}if((h|0)==(c[1211]|0)){w=(c[1208]|0)+o|0;c[1208]=w;c[1211]=e;c[e+4>>2]=w|1;c[e+w>>2]=w;i=b;return}o=(n&-8)+o|0;m=n>>>3;do if(n>>>0>=256){l=c[a+(j+16)>>2]|0;m=c[a+(j|4)>>2]|0;do if((m|0)==(h|0)){n=a+(j+12)|0;m=c[n>>2]|0;if(!m){n=a+(j+8)|0;m=c[n>>2]|0;if(!m){k=0;break}}while(1){q=m+20|0;p=c[q>>2]|0;if(p){m=p;n=q;continue}p=m+16|0;q=c[p>>2]|0;if(!q)break;else{m=q;n=p}}if(n>>>0<(c[1210]|0)>>>0)Wa();else{c[n>>2]=0;k=m;break}}else{n=c[a+j>>2]|0;if(n>>>0<(c[1210]|0)>>>0)Wa();p=n+12|0;if((c[p>>2]|0)!=(h|0))Wa();q=m+8|0;if((c[q>>2]|0)==(h|0)){c[p>>2]=m;c[q>>2]=n;k=m;break}else Wa()}while(0);if(l){m=c[a+(j+20)>>2]|0;n=5128+(m<<2)|0;if((h|0)==(c[n>>2]|0)){c[n>>2]=k;if(!k){c[1207]=c[1207]&~(1<<m);break}}else{if(l>>>0<(c[1210]|0)>>>0)Wa();m=l+16|0;if((c[m>>2]|0)==(h|0))c[m>>2]=k;else c[l+20>>2]=k;if(!k)break}h=c[1210]|0;if(k>>>0<h>>>0)Wa();c[k+24>>2]=l;l=c[a+(j+8)>>2]|0;do if(l)if(l>>>0<h>>>0)Wa();else{c[k+16>>2]=l;c[l+24>>2]=k;break}while(0);h=c[a+(j+12)>>2]|0;if(h)if(h>>>0<(c[1210]|0)>>>0)Wa();else{c[k+20>>2]=h;c[h+24>>2]=k;break}}}else{k=c[a+j>>2]|0;j=c[a+(j|4)>>2]|0;a=4864+(m<<1<<2)|0;if((k|0)!=(a|0)){if(k>>>0<(c[1210]|0)>>>0)Wa();if((c[k+12>>2]|0)!=(h|0))Wa()}if((j|0)==(k|0)){c[1206]=c[1206]&~(1<<m);break}if((j|0)!=(a|0)){if(j>>>0<(c[1210]|0)>>>0)Wa();a=j+8|0;if((c[a>>2]|0)==(h|0))l=a;else Wa()}else l=j+8|0;c[k+12>>2]=j;c[l>>2]=k}while(0);c[e+4>>2]=o|1;c[e+o>>2]=o;if((e|0)==(c[1211]|0)){c[1208]=o;i=b;return}}else{c[m>>2]=n&-2;c[e+4>>2]=o|1;c[e+o>>2]=o}h=o>>>3;if(o>>>0<256){j=h<<1;d=4864+(j<<2)|0;k=c[1206]|0;h=1<<h;if(k&h){j=4864+(j+2<<2)|0;h=c[j>>2]|0;if(h>>>0<(c[1210]|0)>>>0)Wa();else{f=j;g=h}}else{c[1206]=k|h;f=4864+(j+2<<2)|0;g=d}c[f>>2]=e;c[g+12>>2]=e;c[e+8>>2]=g;c[e+12>>2]=d;i=b;return}f=o>>>8;if(f)if(o>>>0>16777215)f=31;else{v=(f+1048320|0)>>>16&8;w=f<<v;u=(w+520192|0)>>>16&4;w=w<<u;f=(w+245760|0)>>>16&2;f=14-(u|v|f)+(w<<f>>>15)|0;f=o>>>(f+7|0)&1|f<<1}else f=0;g=5128+(f<<2)|0;c[e+28>>2]=f;c[e+20>>2]=0;c[e+16>>2]=0;j=c[1207]|0;h=1<<f;a:do if(j&h){g=c[g>>2]|0;if((f|0)==31)f=0;else f=25-(f>>>1)|0;b:do if((c[g+4>>2]&-8|0)!=(o|0)){f=o<<f;while(1){j=g+(f>>>31<<2)+16|0;h=c[j>>2]|0;if(!h)break;if((c[h+4>>2]&-8|0)==(o|0)){d=h;break b}else{f=f<<1;g=h}}if(j>>>0<(c[1210]|0)>>>0)Wa();else{c[j>>2]=e;c[e+24>>2]=g;c[e+12>>2]=e;c[e+8>>2]=e;break a}}else d=g;while(0);g=d+8|0;f=c[g>>2]|0;w=c[1210]|0;if(d>>>0>=w>>>0&f>>>0>=w>>>0){c[f+12>>2]=e;c[g>>2]=e;c[e+8>>2]=f;c[e+12>>2]=d;c[e+24>>2]=0;break}else Wa()}else{c[1207]=j|h;c[g>>2]=e;c[e+24>>2]=g;c[e+12>>2]=e;c[e+8>>2]=e}while(0);w=(c[1214]|0)+ -1|0;c[1214]=w;if(!w)d=5280|0;else{i=b;return}while(1){d=c[d>>2]|0;if(!d)break;else d=d+8|0}c[1214]=-1;i=b;return}function Ud(a,b){a=a|0;b=b|0;var d=0,e=0,f=0;d=i;do if(a){if(b>>>0>4294967231){c[(Oa()|0)>>2]=12;e=0;break}if(b>>>0<11)e=16;else e=b+11&-8;e=fe(a+ -8|0,e)|0;if(e){e=e+8|0;break}e=Sd(b)|0;if(!e)e=0;else{f=c[a+ -4>>2]|0;f=(f&-8)-((f&3|0)==0?8:4)|0;pe(e|0,a|0,(f>>>0<b>>>0?f:b)|0)|0;Td(a)}}else e=Sd(b)|0;while(0);i=d;return e|0}function Vd(a){a=a|0;if((a|0)==32)a=1;else a=(a+ -9|0)>>>0<5;return a&1|0}function Wd(b,e,f,g,h){b=b|0;e=e|0;f=f|0;g=g|0;h=h|0;var j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0;j=i;if(e>>>0>36){c[(Oa()|0)>>2]=22;s=0;t=0;F=s;i=j;return t|0}k=b+4|0;l=b+100|0;do{m=c[k>>2]|0;if(m>>>0<(c[l>>2]|0)>>>0){c[k>>2]=m+1;o=d[m>>0]|0}else o=Zd(b)|0}while((Vd(o)|0)!=0);do if((o|0)==43|(o|0)==45){m=((o|0)==45)<<31>>31;n=c[k>>2]|0;if(n>>>0<(c[l>>2]|0)>>>0){c[k>>2]=n+1;o=d[n>>0]|0;break}else{o=Zd(b)|0;break}}else m=0;while(0);n=(e|0)==0;do if((e&-17|0)==0&(o|0)==48){o=c[k>>2]|0;if(o>>>0<(c[l>>2]|0)>>>0){c[k>>2]=o+1;o=d[o>>0]|0}else o=Zd(b)|0;if((o|32|0)!=120)if(n){e=8;f=46;break}else{f=32;break}e=c[k>>2]|0;if(e>>>0<(c[l>>2]|0)>>>0){c[k>>2]=e+1;o=d[e>>0]|0}else o=Zd(b)|0;if((d[o+5321>>0]|0)>15){g=(c[l>>2]|0)==0;if(!g)c[k>>2]=(c[k>>2]|0)+ -1;if(!f){Yd(b,0);s=0;t=0;F=s;i=j;return t|0}if(g){s=0;t=0;F=s;i=j;return t|0}c[k>>2]=(c[k>>2]|0)+ -1;s=0;t=0;F=s;i=j;return t|0}else{e=16;f=46}}else{e=n?10:e;if((d[o+5321>>0]|0)>>>0<e>>>0)f=32;else{if(c[l>>2]|0)c[k>>2]=(c[k>>2]|0)+ -1;Yd(b,0);c[(Oa()|0)>>2]=22;s=0;t=0;F=s;i=j;return t|0}}while(0);if((f|0)==32)if((e|0)==10){e=o+ -48|0;if(e>>>0<10){n=0;do{n=(n*10|0)+e|0;e=c[k>>2]|0;if(e>>>0<(c[l>>2]|0)>>>0){c[k>>2]=e+1;o=d[e>>0]|0}else o=Zd(b)|0;e=o+ -48|0}while(e>>>0<10&n>>>0<429496729);p=0}else{n=0;p=0}e=o+ -48|0;if(e>>>0<10){do{q=we(n|0,p|0,10,0)|0;r=F;s=((e|0)<0)<<31>>31;t=~s;if(r>>>0>t>>>0|(r|0)==(t|0)&q>>>0>~e>>>0)break;n=ne(q|0,r|0,e|0,s|0)|0;p=F;e=c[k>>2]|0;if(e>>>0<(c[l>>2]|0)>>>0){c[k>>2]=e+1;o=d[e>>0]|0}else o=Zd(b)|0;e=o+ -48|0}while(e>>>0<10&(p>>>0<429496729|(p|0)==429496729&n>>>0<2576980378));if(e>>>0<=9){e=10;f=72}}}else f=46;a:do if((f|0)==46){if(!(e+ -1&e)){f=a[5584+((e*23|0)>>>5&7)>>0]|0;r=a[o+5321>>0]|0;n=r&255;if(n>>>0<e>>>0){o=n;n=0;do{n=o|n<<f;o=c[k>>2]|0;if(o>>>0<(c[l>>2]|0)>>>0){c[k>>2]=o+1;s=d[o>>0]|0}else s=Zd(b)|0;r=a[s+5321>>0]|0;o=r&255}while(o>>>0<e>>>0&n>>>0<134217728);p=0}else{p=0;n=0;s=o}o=oe(-1,-1,f|0)|0;q=F;if((r&255)>>>0>=e>>>0|(p>>>0>q>>>0|(p|0)==(q|0)&n>>>0>o>>>0)){o=s;f=72;break}while(1){n=le(n|0,p|0,f|0)|0;p=F;n=r&255|n;r=c[k>>2]|0;if(r>>>0<(c[l>>2]|0)>>>0){c[k>>2]=r+1;s=d[r>>0]|0}else s=Zd(b)|0;r=a[s+5321>>0]|0;if((r&255)>>>0>=e>>>0|(p>>>0>q>>>0|(p|0)==(q|0)&n>>>0>o>>>0)){o=s;f=72;break a}}}r=a[o+5321>>0]|0;f=r&255;if(f>>>0<e>>>0){n=0;do{n=f+(ba(n,e)|0)|0;f=c[k>>2]|0;if(f>>>0<(c[l>>2]|0)>>>0){c[k>>2]=f+1;q=d[f>>0]|0}else q=Zd(b)|0;r=a[q+5321>>0]|0;f=r&255}while(f>>>0<e>>>0&n>>>0<119304647);p=0}else{n=0;p=0;q=o}if((r&255)>>>0<e>>>0){f=xe(-1,-1,e|0,0)|0;o=F;while(1){if(p>>>0>o>>>0|(p|0)==(o|0)&n>>>0>f>>>0){o=q;f=72;break a}s=we(n|0,p|0,e|0,0)|0;t=F;r=r&255;if(t>>>0>4294967295|(t|0)==-1&s>>>0>~r>>>0){o=q;f=72;break a}n=ne(r|0,0,s|0,t|0)|0;p=F;q=c[k>>2]|0;if(q>>>0<(c[l>>2]|0)>>>0){c[k>>2]=q+1;q=d[q>>0]|0}else q=Zd(b)|0;r=a[q+5321>>0]|0;if((r&255)>>>0>=e>>>0){o=q;f=72;break}}}else{o=q;f=72}}while(0);if((f|0)==72)if((d[o+5321>>0]|0)>>>0<e>>>0){do{f=c[k>>2]|0;if(f>>>0<(c[l>>2]|0)>>>0){c[k>>2]=f+1;f=d[f>>0]|0}else f=Zd(b)|0}while((d[f+5321>>0]|0)>>>0<e>>>0);c[(Oa()|0)>>2]=34;p=h;n=g}if(c[l>>2]|0)c[k>>2]=(c[k>>2]|0)+ -1;if(!(p>>>0<h>>>0|(p|0)==(h|0)&n>>>0<g>>>0)){if(!((g&1|0)!=0|0!=0|(m|0)!=0)){c[(Oa()|0)>>2]=34;t=ne(g|0,h|0,-1,-1)|0;s=F;F=s;i=j;return t|0}if(p>>>0>h>>>0|(p|0)==(h|0)&n>>>0>g>>>0){c[(Oa()|0)>>2]=34;s=h;t=g;F=s;i=j;return t|0}}t=((m|0)<0)<<31>>31;t=je(n^m|0,p^t|0,m|0,t|0)|0;s=F;F=s;i=j;return t|0}
function Xd(b,e,f){b=b|0;e=e|0;f=f|0;var g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0,s=0,t=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,G=0.0,H=0,I=0.0,J=0.0,K=0.0,L=0.0;g=i;i=i+512|0;k=g;if(!e){e=24;j=-149}else if((e|0)==2){e=53;j=-1074}else if((e|0)==1){e=53;j=-1074}else{J=0.0;i=g;return+J}n=b+4|0;o=b+100|0;do{h=c[n>>2]|0;if(h>>>0<(c[o>>2]|0)>>>0){c[n>>2]=h+1;w=d[h>>0]|0}else w=Zd(b)|0}while((Vd(w)|0)!=0);do if((w|0)==43|(w|0)==45){h=1-(((w|0)==45&1)<<1)|0;m=c[n>>2]|0;if(m>>>0<(c[o>>2]|0)>>>0){c[n>>2]=m+1;w=d[m>>0]|0;break}else{w=Zd(b)|0;break}}else h=1;while(0);r=0;do{if((w|32|0)!=(a[5600+r>>0]|0))break;do if(r>>>0<7){m=c[n>>2]|0;if(m>>>0<(c[o>>2]|0)>>>0){c[n>>2]=m+1;w=d[m>>0]|0;break}else{w=Zd(b)|0;break}}while(0);r=r+1|0}while(r>>>0<8);do if((r|0)==3)p=23;else if((r|0)!=8){m=(f|0)!=0;if(r>>>0>3&m)if((r|0)==8)break;else{p=23;break}a:do if(!r){r=0;do{if((w|32|0)!=(a[5616+r>>0]|0))break a;do if(r>>>0<2){s=c[n>>2]|0;if(s>>>0<(c[o>>2]|0)>>>0){c[n>>2]=s+1;w=d[s>>0]|0;break}else{w=Zd(b)|0;break}}while(0);r=r+1|0}while(r>>>0<3)}while(0);if(!r){do if((w|0)==48){m=c[n>>2]|0;if(m>>>0<(c[o>>2]|0)>>>0){c[n>>2]=m+1;m=d[m>>0]|0}else m=Zd(b)|0;if((m|32|0)!=120){if(!(c[o>>2]|0)){w=48;break}c[n>>2]=(c[n>>2]|0)+ -1;w=48;break}k=c[n>>2]|0;if(k>>>0<(c[o>>2]|0)>>>0){c[n>>2]=k+1;z=d[k>>0]|0;x=0}else{z=Zd(b)|0;x=0}while(1){if((z|0)==46){p=70;break}else if((z|0)!=48){k=0;m=0;s=0;r=0;w=0;y=0;G=1.0;t=0;q=0.0;break}k=c[n>>2]|0;if(k>>>0<(c[o>>2]|0)>>>0){c[n>>2]=k+1;z=d[k>>0]|0;x=1;continue}else{z=Zd(b)|0;x=1;continue}}if((p|0)==70){k=c[n>>2]|0;if(k>>>0<(c[o>>2]|0)>>>0){c[n>>2]=k+1;z=d[k>>0]|0}else z=Zd(b)|0;if((z|0)==48){s=0;r=0;do{k=c[n>>2]|0;if(k>>>0<(c[o>>2]|0)>>>0){c[n>>2]=k+1;z=d[k>>0]|0}else z=Zd(b)|0;s=ne(s|0,r|0,-1,-1)|0;r=F}while((z|0)==48);k=0;m=0;x=1;w=1;y=0;G=1.0;t=0;q=0.0}else{k=0;m=0;s=0;r=0;w=1;y=0;G=1.0;t=0;q=0.0}}b:while(1){B=z+ -48|0;do if(B>>>0>=10){A=z|32;C=(z|0)==46;if(!((A+ -97|0)>>>0<6|C))break b;if(C)if(!w){s=m;r=k;w=1;break}else{z=46;break b}else{B=(z|0)>57?A+ -87|0:B;p=83;break}}else p=83;while(0);if((p|0)==83){p=0;do if(!((k|0)<0|(k|0)==0&m>>>0<8)){if((k|0)<0|(k|0)==0&m>>>0<14){J=G*.0625;I=J;q=q+J*+(B|0);break}if((B|0)==0|(y|0)!=0)I=G;else{y=1;I=G;q=q+G*.5}}else{I=G;t=B+(t<<4)|0}while(0);m=ne(m|0,k|0,1,0)|0;k=F;x=1;G=I}z=c[n>>2]|0;if(z>>>0<(c[o>>2]|0)>>>0){c[n>>2]=z+1;z=d[z>>0]|0;continue}else{z=Zd(b)|0;continue}}if(!x){e=(c[o>>2]|0)==0;if(!e)c[n>>2]=(c[n>>2]|0)+ -1;if(f){if(!e?(l=c[n>>2]|0,c[n>>2]=l+ -1,(w|0)!=0):0)c[n>>2]=l+ -2}else Yd(b,0);J=+(h|0)*0.0;i=g;return+J}p=(w|0)==0;l=p?m:s;p=p?k:r;if((k|0)<0|(k|0)==0&m>>>0<8)do{t=t<<4;m=ne(m|0,k|0,1,0)|0;k=F}while((k|0)<0|(k|0)==0&m>>>0<8);do if((z|32|0)==112){m=he(b,f)|0;k=F;if((m|0)==0&(k|0)==-2147483648)if(!f){Yd(b,0);J=0.0;i=g;return+J}else{if(!(c[o>>2]|0)){m=0;k=0;break}c[n>>2]=(c[n>>2]|0)+ -1;m=0;k=0;break}}else if(!(c[o>>2]|0)){m=0;k=0}else{c[n>>2]=(c[n>>2]|0)+ -1;m=0;k=0}while(0);l=le(l|0,p|0,2)|0;l=ne(l|0,F|0,-32,-1)|0;k=ne(l|0,F|0,m|0,k|0)|0;l=F;if(!t){J=+(h|0)*0.0;i=g;return+J}if((l|0)>0|(l|0)==0&k>>>0>(0-j|0)>>>0){c[(Oa()|0)>>2]=34;J=+(h|0)*1.7976931348623157e+308*1.7976931348623157e+308;i=g;return+J}H=j+ -106|0;E=((H|0)<0)<<31>>31;if((l|0)<(E|0)|(l|0)==(E|0)&k>>>0<H>>>0){c[(Oa()|0)>>2]=34;J=+(h|0)*2.2250738585072014e-308*2.2250738585072014e-308;i=g;return+J}if((t|0)>-1)do{t=t<<1;if(!(q>=.5))G=q;else{G=q+-1.0;t=t|1}q=q+G;k=ne(k|0,l|0,-1,-1)|0;l=F}while((t|0)>-1);j=je(32,0,j|0,((j|0)<0)<<31>>31|0)|0;j=ne(k|0,l|0,j|0,F|0)|0;H=F;if(0>(H|0)|0==(H|0)&e>>>0>j>>>0)if((j|0)<0){e=0;p=126}else{e=j;p=124}else p=124;if((p|0)==124)if((e|0)<53)p=126;else{j=e;G=+(h|0);I=0.0}if((p|0)==126){I=+(h|0);j=e;G=I;I=+Va(+(+_d(1.0,84-e|0)),+I)}H=(j|0)<32&q!=0.0&(t&1|0)==0;q=G*(H?0.0:q)+(I+G*+(((H&1)+t|0)>>>0))-I;if(!(q!=0.0))c[(Oa()|0)>>2]=34;J=+$d(q,k);i=g;return+J}while(0);m=j+e|0;l=0-m|0;B=0;while(1){if((w|0)==46){p=137;break}else if((w|0)!=48){D=0;C=0;A=0;break}r=c[n>>2]|0;if(r>>>0<(c[o>>2]|0)>>>0){c[n>>2]=r+1;w=d[r>>0]|0;B=1;continue}else{w=Zd(b)|0;B=1;continue}}if((p|0)==137){p=c[n>>2]|0;if(p>>>0<(c[o>>2]|0)>>>0){c[n>>2]=p+1;w=d[p>>0]|0}else w=Zd(b)|0;if((w|0)==48){D=0;C=0;do{D=ne(D|0,C|0,-1,-1)|0;C=F;p=c[n>>2]|0;if(p>>>0<(c[o>>2]|0)>>>0){c[n>>2]=p+1;w=d[p>>0]|0}else w=Zd(b)|0}while((w|0)==48);B=1;A=1}else{D=0;C=0;A=1}}c[k>>2]=0;z=w+ -48|0;E=(w|0)==46;c:do if(z>>>0<10|E){p=k+496|0;y=0;x=0;t=0;s=0;r=0;d:while(1){do if(E)if(!A){D=y;C=x;A=1}else break d;else{E=ne(y|0,x|0,1,0)|0;x=F;H=(w|0)!=48;if((s|0)>=125){if(!H){y=E;break}c[p>>2]=c[p>>2]|1;y=E;break}y=k+(s<<2)|0;if(t)z=w+ -48+((c[y>>2]|0)*10|0)|0;c[y>>2]=z;t=t+1|0;z=(t|0)==9;y=E;B=1;t=z?0:t;s=(z&1)+s|0;r=H?E:r}while(0);w=c[n>>2]|0;if(w>>>0<(c[o>>2]|0)>>>0){c[n>>2]=w+1;w=d[w>>0]|0}else w=Zd(b)|0;z=w+ -48|0;E=(w|0)==46;if(!(z>>>0<10|E)){p=160;break c}}z=(B|0)!=0;p=168}else{y=0;x=0;t=0;s=0;r=0;p=160}while(0);do if((p|0)==160){z=(A|0)==0;D=z?y:D;C=z?x:C;z=(B|0)!=0;if(!(z&(w|32|0)==101))if((w|0)>-1){p=168;break}else{p=170;break}z=he(b,f)|0;w=F;do if((z|0)==0&(w|0)==-2147483648)if(!f){Yd(b,0);J=0.0;i=g;return+J}else{if(!(c[o>>2]|0)){z=0;w=0;break}c[n>>2]=(c[n>>2]|0)+ -1;z=0;w=0;break}while(0);b=ne(z|0,w|0,D|0,C|0)|0;C=F}while(0);if((p|0)==168)if(c[o>>2]|0){c[n>>2]=(c[n>>2]|0)+ -1;if(z)b=D;else p=171}else p=170;if((p|0)==170)if(z)b=D;else p=171;if((p|0)==171){c[(Oa()|0)>>2]=22;Yd(b,0);J=0.0;i=g;return+J}n=c[k>>2]|0;if(!n){J=+(h|0)*0.0;i=g;return+J}if((b|0)==(y|0)&(C|0)==(x|0)&((x|0)<0|(x|0)==0&y>>>0<10)?e>>>0>30|(n>>>e|0)==0:0){J=+(h|0)*+(n>>>0);i=g;return+J}H=(j|0)/-2|0;E=((H|0)<0)<<31>>31;if((C|0)>(E|0)|(C|0)==(E|0)&b>>>0>H>>>0){c[(Oa()|0)>>2]=34;J=+(h|0)*1.7976931348623157e+308*1.7976931348623157e+308;i=g;return+J}H=j+ -106|0;E=((H|0)<0)<<31>>31;if((C|0)<(E|0)|(C|0)==(E|0)&b>>>0<H>>>0){c[(Oa()|0)>>2]=34;J=+(h|0)*2.2250738585072014e-308*2.2250738585072014e-308;i=g;return+J}if(t){if((t|0)<9){n=k+(s<<2)|0;o=c[n>>2]|0;do{o=o*10|0;t=t+1|0}while((t|0)!=9);c[n>>2]=o}s=s+1|0}if((r|0)<9?(r|0)<=(b|0)&(b|0)<18:0){if((b|0)==9){J=+(h|0)*+((c[k>>2]|0)>>>0);i=g;return+J}if((b|0)<9){J=+(h|0)*+((c[k>>2]|0)>>>0)/+(c[5632+(8-b<<2)>>2]|0);i=g;return+J}H=e+27+(ba(b,-3)|0)|0;n=c[k>>2]|0;if((H|0)>30|(n>>>H|0)==0){J=+(h|0)*+(n>>>0)*+(c[5632+(b+ -10<<2)>>2]|0);i=g;return+J}}n=(b|0)%9|0;if(!n){n=0;o=0}else{f=(b|0)>-1?n:n+9|0;p=c[5632+(8-f<<2)>>2]|0;if(s){r=1e9/(p|0)|0;n=0;o=0;t=0;do{D=k+(t<<2)|0;E=c[D>>2]|0;H=((E>>>0)/(p>>>0)|0)+o|0;c[D>>2]=H;o=ba((E>>>0)%(p>>>0)|0,r)|0;E=t;t=t+1|0;if((E|0)==(n|0)&(H|0)==0){n=t&127;b=b+ -9|0}}while((t|0)!=(s|0));if(o){c[k+(s<<2)>>2]=o;s=s+1|0}}else{n=0;s=0}o=0;b=9-f+b|0}e:while(1){f=k+(n<<2)|0;if((b|0)<18){do{r=0;f=s+127|0;while(1){f=f&127;p=k+(f<<2)|0;t=le(c[p>>2]|0,0,29)|0;t=ne(t|0,F|0,r|0,0)|0;r=F;if(r>>>0>0|(r|0)==0&t>>>0>1e9){H=xe(t|0,r|0,1e9,0)|0;t=ye(t|0,r|0,1e9,0)|0;r=H}else r=0;c[p>>2]=t;p=(f|0)==(n|0);if(!((f|0)!=(s+127&127|0)|p))s=(t|0)==0?f:s;if(p)break;else f=f+ -1|0}o=o+ -29|0}while((r|0)==0)}else{if((b|0)!=18)break;do{if((c[f>>2]|0)>>>0>=9007199){b=18;break e}r=0;p=s+127|0;while(1){p=p&127;t=k+(p<<2)|0;w=le(c[t>>2]|0,0,29)|0;w=ne(w|0,F|0,r|0,0)|0;r=F;if(r>>>0>0|(r|0)==0&w>>>0>1e9){H=xe(w|0,r|0,1e9,0)|0;w=ye(w|0,r|0,1e9,0)|0;r=H}else r=0;c[t>>2]=w;t=(p|0)==(n|0);if(!((p|0)!=(s+127&127|0)|t))s=(w|0)==0?p:s;if(t)break;else p=p+ -1|0}o=o+ -29|0}while((r|0)==0)}n=n+127&127;if((n|0)==(s|0)){H=s+127&127;s=k+((s+126&127)<<2)|0;c[s>>2]=c[s>>2]|c[k+(H<<2)>>2];s=H}c[k+(n<<2)>>2]=r;b=b+9|0}f:while(1){f=s+1&127;p=k+((s+127&127)<<2)|0;while(1){t=(b|0)==18;r=(b|0)>27?9:1;while(1){w=0;while(1){x=w+n&127;if((x|0)==(s|0)){w=2;break}y=c[k+(x<<2)>>2]|0;z=c[5624+(w<<2)>>2]|0;if(y>>>0<z>>>0){w=2;break}x=w+1|0;if(y>>>0>z>>>0)break;if((x|0)<2)w=x;else{w=x;break}}if((w|0)==2&t)break f;o=r+o|0;if((n|0)==(s|0))n=s;else break}t=(1<<r)+ -1|0;w=1e9>>>r;x=n;y=0;do{D=k+(n<<2)|0;E=c[D>>2]|0;H=(E>>>r)+y|0;c[D>>2]=H;y=ba(E&t,w)|0;H=(n|0)==(x|0)&(H|0)==0;n=n+1&127;b=H?b+ -9|0:b;x=H?n:x}while((n|0)!=(s|0));if(!y){n=x;continue}if((f|0)!=(x|0))break;c[p>>2]=c[p>>2]|1;n=x}c[k+(s<<2)>>2]=y;n=x;s=f}b=n&127;if((b|0)==(s|0)){c[k+(f+ -1<<2)>>2]=0;s=f}G=+((c[k+(b<<2)>>2]|0)>>>0);b=n+1&127;if((b|0)==(s|0)){s=s+1&127;c[k+(s+ -1<<2)>>2]=0}q=+(h|0);I=q*(G*1.0e9+ +((c[k+(b<<2)>>2]|0)>>>0));h=o+53|0;j=h-j|0;if((j|0)<(e|0))if((j|0)<0){e=0;b=1;p=244}else{e=j;b=1;p=243}else{b=0;p=243}if((p|0)==243)if((e|0)<53)p=244;else{G=0.0;J=0.0}if((p|0)==244){L=+Va(+(+_d(1.0,105-e|0)),+I);K=+cb(+I,+(+_d(1.0,53-e|0)));G=L;J=K;I=L+(I-K)}f=n+2&127;do if((f|0)!=(s|0)){k=c[k+(f<<2)>>2]|0;do if(k>>>0>=5e8){if(k>>>0>5e8){J=q*.75+J;break}if((n+3&127|0)==(s|0)){J=q*.5+J;break}else{J=q*.75+J;break}}else{if((k|0)==0?(n+3&127|0)==(s|0):0)break;J=q*.25+J}while(0);if((53-e|0)<=1)break;if(+cb(+J,1.0)!=0.0)break;J=J+1.0}while(0);q=I+J-G;do if((h&2147483647|0)>(-2-m|0)){if(+Q(+q)>=9007199254740992.0){b=(b|0)!=0&(e|0)==(j|0)?0:b;o=o+1|0;q=q*.5}if((o+50|0)<=(l|0)?!((b|0)!=0&J!=0.0):0)break;c[(Oa()|0)>>2]=34}while(0);L=+$d(q,o);i=g;return+L}else if((r|0)==3){e=c[n>>2]|0;if(e>>>0<(c[o>>2]|0)>>>0){c[n>>2]=e+1;e=d[e>>0]|0}else e=Zd(b)|0;if((e|0)==40)e=1;else{if(!(c[o>>2]|0)){L=u;i=g;return+L}c[n>>2]=(c[n>>2]|0)+ -1;L=u;i=g;return+L}while(1){h=c[n>>2]|0;if(h>>>0<(c[o>>2]|0)>>>0){c[n>>2]=h+1;h=d[h>>0]|0}else h=Zd(b)|0;if(!((h+ -48|0)>>>0<10|(h+ -65|0)>>>0<26)?!((h+ -97|0)>>>0<26|(h|0)==95):0)break;e=e+1|0}if((h|0)==41){L=u;i=g;return+L}h=(c[o>>2]|0)==0;if(!h)c[n>>2]=(c[n>>2]|0)+ -1;if(!m){c[(Oa()|0)>>2]=22;Yd(b,0);L=0.0;i=g;return+L}if((e|0)==0|h){L=u;i=g;return+L}do{e=e+ -1|0;c[n>>2]=(c[n>>2]|0)+ -1}while((e|0)!=0);q=u;i=g;return+q}else{if(c[o>>2]|0)c[n>>2]=(c[n>>2]|0)+ -1;c[(Oa()|0)>>2]=22;Yd(b,0);L=0.0;i=g;return+L}}while(0);if((p|0)==23){e=(c[o>>2]|0)==0;if(!e)c[n>>2]=(c[n>>2]|0)+ -1;if(!(r>>>0<4|(f|0)==0|e))do{c[n>>2]=(c[n>>2]|0)+ -1;r=r+ -1|0}while(r>>>0>3)}L=+(h|0)*v;i=g;return+L}function Yd(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0;d=i;c[a+104>>2]=b;f=c[a+8>>2]|0;e=c[a+4>>2]|0;g=f-e|0;c[a+108>>2]=g;if((b|0)!=0&(g|0)>(b|0)){c[a+100>>2]=e+b;i=d;return}else{c[a+100>>2]=f;i=d;return}}function Zd(b){b=b|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0;f=i;j=b+104|0;l=c[j>>2]|0;if(!((l|0)!=0?(c[b+108>>2]|0)>=(l|0):0))k=3;if((k|0)==3?(e=be(b)|0,(e|0)>=0):0){k=c[j>>2]|0;j=c[b+8>>2]|0;if((k|0)!=0?(g=c[b+4>>2]|0,h=k-(c[b+108>>2]|0)+ -1|0,(j-g|0)>(h|0)):0)c[b+100>>2]=g+h;else c[b+100>>2]=j;g=c[b+4>>2]|0;if(j){l=b+108|0;c[l>>2]=j+1-g+(c[l>>2]|0)}b=g+ -1|0;if((d[b>>0]|0|0)==(e|0)){l=e;i=f;return l|0}a[b>>0]=e;l=e;i=f;return l|0}c[b+100>>2]=0;l=-1;i=f;return l|0}function _d(a,b){a=+a;b=b|0;var d=0,e=0;d=i;if((b|0)>1023){a=a*8.98846567431158e+307;e=b+ -1023|0;if((e|0)>1023){b=b+ -2046|0;b=(b|0)>1023?1023:b;a=a*8.98846567431158e+307}else b=e}else if((b|0)<-1022){a=a*2.2250738585072014e-308;e=b+1022|0;if((e|0)<-1022){b=b+2044|0;b=(b|0)<-1022?-1022:b;a=a*2.2250738585072014e-308}else b=e}b=le(b+1023|0,0,52)|0;e=F;c[k>>2]=b;c[k+4>>2]=e;a=a*+h[k>>3];i=d;return+a}function $d(a,b){a=+a;b=b|0;var c=0;c=i;a=+_d(a,b);i=c;return+a}function ae(b){b=b|0;var d=0,e=0,f=0;e=i;f=b+74|0;d=a[f>>0]|0;a[f>>0]=d+255|d;f=b+20|0;d=b+44|0;if((c[f>>2]|0)>>>0>(c[d>>2]|0)>>>0)eb[c[b+36>>2]&1](b,0,0)|0;c[b+16>>2]=0;c[b+28>>2]=0;c[f>>2]=0;f=c[b>>2]|0;if(!(f&20)){f=c[d>>2]|0;c[b+8>>2]=f;c[b+4>>2]=f;f=0;i=e;return f|0}if(!(f&4)){f=-1;i=e;return f|0}c[b>>2]=f|32;f=-1;i=e;return f|0}function be(a){a=a|0;var b=0,e=0;b=i;i=i+16|0;e=b;if((c[a+8>>2]|0)==0?(ae(a)|0)!=0:0)a=-1;else if((eb[c[a+32>>2]&1](a,e,1)|0)==1)a=d[e>>0]|0;else a=-1;i=b;return a|0}function ce(a,b){a=a|0;b=b|0;var d=0,e=0,f=0.0,g=0,h=0;d=i;i=i+112|0;e=d;h=e+0|0;g=h+112|0;do{c[h>>2]=0;h=h+4|0}while((h|0)<(g|0));g=e+4|0;c[g>>2]=a;h=e+8|0;c[h>>2]=-1;c[e+44>>2]=a;c[e+76>>2]=-1;Yd(e,0);f=+Xd(e,1,1);e=(c[g>>2]|0)-(c[h>>2]|0)+(c[e+108>>2]|0)|0;if(!b){i=d;return+f}if(e)a=a+e|0;c[b>>2]=a;i=d;return+f}function de(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0;e=i;i=i+112|0;g=e;c[g>>2]=0;f=g+4|0;c[f>>2]=a;c[g+44>>2]=a;if((a|0)<0)c[g+8>>2]=-1;else c[g+8>>2]=a+2147483647;c[g+76>>2]=-1;Yd(g,0);d=Wd(g,d,1,-2147483648,0)|0;if(!b){i=e;return d|0}c[b>>2]=a+((c[f>>2]|0)+(c[g+108>>2]|0)-(c[g+8>>2]|0));i=e;return d|0}function ee(b,c){b=b|0;c=c|0;var d=0,e=0,f=0;d=i;f=a[b>>0]|0;e=a[c>>0]|0;if(f<<24>>24==0?1:f<<24>>24!=e<<24>>24)c=f;else{do{b=b+1|0;c=c+1|0;f=a[b>>0]|0;e=a[c>>0]|0}while(!(f<<24>>24==0?1:f<<24>>24!=e<<24>>24));c=f}i=d;return(c&255)-(e&255)|0}function fe(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0;d=i;f=a+4|0;e=c[f>>2]|0;l=e&-8;j=a+l|0;m=c[1210]|0;h=e&3;if(!((h|0)!=1&a>>>0>=m>>>0&a>>>0<j>>>0))Wa();g=a+(l|4)|0;p=c[g>>2]|0;if(!(p&1))Wa();if(!h){if(b>>>0<256){r=0;i=d;return r|0}if(l>>>0>=(b+4|0)>>>0?(l-b|0)>>>0<=c[1326]<<1>>>0:0){r=a;i=d;return r|0}r=0;i=d;return r|0}if(l>>>0>=b>>>0){h=l-b|0;if(h>>>0<=15){r=a;i=d;return r|0}c[f>>2]=e&1|b|2;c[a+(b+4)>>2]=h|3;c[g>>2]=c[g>>2]|1;ge(a+b|0,h);r=a;i=d;return r|0}if((j|0)==(c[1212]|0)){g=(c[1209]|0)+l|0;if(g>>>0<=b>>>0){r=0;i=d;return r|0}r=g-b|0;c[f>>2]=e&1|b|2;c[a+(b+4)>>2]=r|1;c[1212]=a+b;c[1209]=r;r=a;i=d;return r|0}if((j|0)==(c[1211]|0)){h=(c[1208]|0)+l|0;if(h>>>0<b>>>0){r=0;i=d;return r|0}g=h-b|0;if(g>>>0>15){c[f>>2]=e&1|b|2;c[a+(b+4)>>2]=g|1;c[a+h>>2]=g;e=a+(h+4)|0;c[e>>2]=c[e>>2]&-2;e=a+b|0}else{c[f>>2]=e&1|h|2;e=a+(h+4)|0;c[e>>2]=c[e>>2]|1;e=0;g=0}c[1208]=g;c[1211]=e;r=a;i=d;return r|0}if(p&2){r=0;i=d;return r|0}g=(p&-8)+l|0;if(g>>>0<b>>>0){r=0;i=d;return r|0}h=g-b|0;o=p>>>3;do if(p>>>0>=256){n=c[a+(l+24)>>2]|0;o=c[a+(l+12)>>2]|0;do if((o|0)==(j|0)){p=a+(l+20)|0;o=c[p>>2]|0;if(!o){p=a+(l+16)|0;o=c[p>>2]|0;if(!o){k=0;break}}while(1){r=o+20|0;q=c[r>>2]|0;if(q){o=q;p=r;continue}q=o+16|0;r=c[q>>2]|0;if(!r)break;else{o=r;p=q}}if(p>>>0<m>>>0)Wa();else{c[p>>2]=0;k=o;break}}else{p=c[a+(l+8)>>2]|0;if(p>>>0<m>>>0)Wa();m=p+12|0;if((c[m>>2]|0)!=(j|0))Wa();q=o+8|0;if((c[q>>2]|0)==(j|0)){c[m>>2]=o;c[q>>2]=p;k=o;break}else Wa()}while(0);if(n){m=c[a+(l+28)>>2]|0;o=5128+(m<<2)|0;if((j|0)==(c[o>>2]|0)){c[o>>2]=k;if(!k){c[1207]=c[1207]&~(1<<m);break}}else{if(n>>>0<(c[1210]|0)>>>0)Wa();m=n+16|0;if((c[m>>2]|0)==(j|0))c[m>>2]=k;else c[n+20>>2]=k;if(!k)break}j=c[1210]|0;if(k>>>0<j>>>0)Wa();c[k+24>>2]=n;m=c[a+(l+16)>>2]|0;do if(m)if(m>>>0<j>>>0)Wa();else{c[k+16>>2]=m;c[m+24>>2]=k;break}while(0);j=c[a+(l+20)>>2]|0;if(j)if(j>>>0<(c[1210]|0)>>>0)Wa();else{c[k+20>>2]=j;c[j+24>>2]=k;break}}}else{k=c[a+(l+8)>>2]|0;l=c[a+(l+12)>>2]|0;p=4864+(o<<1<<2)|0;if((k|0)!=(p|0)){if(k>>>0<m>>>0)Wa();if((c[k+12>>2]|0)!=(j|0))Wa()}if((l|0)==(k|0)){c[1206]=c[1206]&~(1<<o);break}if((l|0)!=(p|0)){if(l>>>0<m>>>0)Wa();m=l+8|0;if((c[m>>2]|0)==(j|0))n=m;else Wa()}else n=l+8|0;c[k+12>>2]=l;c[n>>2]=k}while(0);if(h>>>0<16){c[f>>2]=g|e&1|2;r=a+(g|4)|0;c[r>>2]=c[r>>2]|1;r=a;i=d;return r|0}else{c[f>>2]=e&1|b|2;c[a+(b+4)>>2]=h|3;r=a+(g|4)|0;c[r>>2]=c[r>>2]|1;ge(a+b|0,h);r=a;i=d;return r|0}return 0}function ge(a,b){a=a|0;b=b|0;var d=0,e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0;d=i;h=a+b|0;l=c[a+4>>2]|0;do if(!(l&1)){p=c[a>>2]|0;if(!(l&3)){i=d;return}l=a+(0-p)|0;m=p+b|0;r=c[1210]|0;if(l>>>0<r>>>0)Wa();if((l|0)==(c[1211]|0)){e=a+(b+4)|0;n=c[e>>2]|0;if((n&3|0)!=3){e=l;n=m;break}c[1208]=m;c[e>>2]=n&-2;c[a+(4-p)>>2]=m|1;c[h>>2]=m;i=d;return}s=p>>>3;if(p>>>0<256){e=c[a+(8-p)>>2]|0;n=c[a+(12-p)>>2]|0;o=4864+(s<<1<<2)|0;if((e|0)!=(o|0)){if(e>>>0<r>>>0)Wa();if((c[e+12>>2]|0)!=(l|0))Wa()}if((n|0)==(e|0)){c[1206]=c[1206]&~(1<<s);e=l;n=m;break}if((n|0)!=(o|0)){if(n>>>0<r>>>0)Wa();o=n+8|0;if((c[o>>2]|0)==(l|0))q=o;else Wa()}else q=n+8|0;c[e+12>>2]=n;c[q>>2]=e;e=l;n=m;break}q=c[a+(24-p)>>2]|0;s=c[a+(12-p)>>2]|0;do if((s|0)==(l|0)){u=16-p|0;t=a+(u+4)|0;s=c[t>>2]|0;if(!s){t=a+u|0;s=c[t>>2]|0;if(!s){o=0;break}}while(1){v=s+20|0;u=c[v>>2]|0;if(u){s=u;t=v;continue}u=s+16|0;v=c[u>>2]|0;if(!v)break;else{s=v;t=u}}if(t>>>0<r>>>0)Wa();else{c[t>>2]=0;o=s;break}}else{t=c[a+(8-p)>>2]|0;if(t>>>0<r>>>0)Wa();r=t+12|0;if((c[r>>2]|0)!=(l|0))Wa();u=s+8|0;if((c[u>>2]|0)==(l|0)){c[r>>2]=s;c[u>>2]=t;o=s;break}else Wa()}while(0);if(q){s=c[a+(28-p)>>2]|0;r=5128+(s<<2)|0;if((l|0)==(c[r>>2]|0)){c[r>>2]=o;if(!o){c[1207]=c[1207]&~(1<<s);e=l;n=m;break}}else{if(q>>>0<(c[1210]|0)>>>0)Wa();r=q+16|0;if((c[r>>2]|0)==(l|0))c[r>>2]=o;else c[q+20>>2]=o;if(!o){e=l;n=m;break}}r=c[1210]|0;if(o>>>0<r>>>0)Wa();c[o+24>>2]=q;p=16-p|0;q=c[a+p>>2]|0;do if(q)if(q>>>0<r>>>0)Wa();else{c[o+16>>2]=q;c[q+24>>2]=o;break}while(0);p=c[a+(p+4)>>2]|0;if(p)if(p>>>0<(c[1210]|0)>>>0)Wa();else{c[o+20>>2]=p;c[p+24>>2]=o;e=l;n=m;break}else{e=l;n=m}}else{e=l;n=m}}else{e=a;n=b}while(0);l=c[1210]|0;if(h>>>0<l>>>0)Wa();m=a+(b+4)|0;o=c[m>>2]|0;if(!(o&2)){if((h|0)==(c[1212]|0)){v=(c[1209]|0)+n|0;c[1209]=v;c[1212]=e;c[e+4>>2]=v|1;if((e|0)!=(c[1211]|0)){i=d;return}c[1211]=0;c[1208]=0;i=d;return}if((h|0)==(c[1211]|0)){v=(c[1208]|0)+n|0;c[1208]=v;c[1211]=e;c[e+4>>2]=v|1;c[e+v>>2]=v;i=d;return}n=(o&-8)+n|0;m=o>>>3;do if(o>>>0>=256){k=c[a+(b+24)>>2]|0;o=c[a+(b+12)>>2]|0;do if((o|0)==(h|0)){o=a+(b+20)|0;m=c[o>>2]|0;if(!m){o=a+(b+16)|0;m=c[o>>2]|0;if(!m){j=0;break}}while(1){p=m+20|0;q=c[p>>2]|0;if(q){m=q;o=p;continue}q=m+16|0;p=c[q>>2]|0;if(!p)break;else{m=p;o=q}}if(o>>>0<l>>>0)Wa();else{c[o>>2]=0;j=m;break}}else{m=c[a+(b+8)>>2]|0;if(m>>>0<l>>>0)Wa();p=m+12|0;if((c[p>>2]|0)!=(h|0))Wa();l=o+8|0;if((c[l>>2]|0)==(h|0)){c[p>>2]=o;c[l>>2]=m;j=o;break}else Wa()}while(0);if(k){m=c[a+(b+28)>>2]|0;l=5128+(m<<2)|0;if((h|0)==(c[l>>2]|0)){c[l>>2]=j;if(!j){c[1207]=c[1207]&~(1<<m);break}}else{if(k>>>0<(c[1210]|0)>>>0)Wa();l=k+16|0;if((c[l>>2]|0)==(h|0))c[l>>2]=j;else c[k+20>>2]=j;if(!j)break}h=c[1210]|0;if(j>>>0<h>>>0)Wa();c[j+24>>2]=k;k=c[a+(b+16)>>2]|0;do if(k)if(k>>>0<h>>>0)Wa();else{c[j+16>>2]=k;c[k+24>>2]=j;break}while(0);h=c[a+(b+20)>>2]|0;if(h)if(h>>>0<(c[1210]|0)>>>0)Wa();else{c[j+20>>2]=h;c[h+24>>2]=j;break}}}else{j=c[a+(b+8)>>2]|0;a=c[a+(b+12)>>2]|0;b=4864+(m<<1<<2)|0;if((j|0)!=(b|0)){if(j>>>0<l>>>0)Wa();if((c[j+12>>2]|0)!=(h|0))Wa()}if((a|0)==(j|0)){c[1206]=c[1206]&~(1<<m);break}if((a|0)!=(b|0)){if(a>>>0<l>>>0)Wa();b=a+8|0;if((c[b>>2]|0)==(h|0))k=b;else Wa()}else k=a+8|0;c[j+12>>2]=a;c[k>>2]=j}while(0);c[e+4>>2]=n|1;c[e+n>>2]=n;if((e|0)==(c[1211]|0)){c[1208]=n;i=d;return}}else{c[m>>2]=o&-2;c[e+4>>2]=n|1;c[e+n>>2]=n}b=n>>>3;if(n>>>0<256){a=b<<1;h=4864+(a<<2)|0;j=c[1206]|0;b=1<<b;if(j&b){a=4864+(a+2<<2)|0;j=c[a>>2]|0;if(j>>>0<(c[1210]|0)>>>0)Wa();else{g=a;f=j}}else{c[1206]=j|b;g=4864+(a+2<<2)|0;f=h}c[g>>2]=e;c[f+12>>2]=e;c[e+8>>2]=f;c[e+12>>2]=h;i=d;return}f=n>>>8;if(f)if(n>>>0>16777215)f=31;else{u=(f+1048320|0)>>>16&8;v=f<<u;t=(v+520192|0)>>>16&4;v=v<<t;f=(v+245760|0)>>>16&2;f=14-(t|u|f)+(v<<f>>>15)|0;f=n>>>(f+7|0)&1|f<<1}else f=0;g=5128+(f<<2)|0;c[e+28>>2]=f;c[e+20>>2]=0;c[e+16>>2]=0;a=c[1207]|0;h=1<<f;if(!(a&h)){c[1207]=a|h;c[g>>2]=e;c[e+24>>2]=g;c[e+12>>2]=e;c[e+8>>2]=e;i=d;return}g=c[g>>2]|0;if((f|0)==31)f=0;else f=25-(f>>>1)|0;a:do if((c[g+4>>2]&-8|0)!=(n|0)){f=n<<f;a=g;while(1){h=a+(f>>>31<<2)+16|0;g=c[h>>2]|0;if(!g)break;if((c[g+4>>2]&-8|0)==(n|0))break a;else{f=f<<1;a=g}}if(h>>>0<(c[1210]|0)>>>0)Wa();c[h>>2]=e;c[e+24>>2]=a;c[e+12>>2]=e;c[e+8>>2]=e;i=d;return}while(0);f=g+8|0;h=c[f>>2]|0;v=c[1210]|0;if(!(g>>>0>=v>>>0&h>>>0>=v>>>0))Wa();c[h+12>>2]=e;c[f>>2]=e;c[e+8>>2]=h;c[e+12>>2]=g;c[e+24>>2]=0;i=d;return}function he(a,b){a=a|0;b=b|0;var e=0,f=0,g=0,h=0,j=0,k=0;e=i;g=a+4|0;h=c[g>>2]|0;f=a+100|0;if(h>>>0<(c[f>>2]|0)>>>0){c[g>>2]=h+1;j=d[h>>0]|0}else j=Zd(a)|0;if((j|0)==43|(j|0)==45){k=c[g>>2]|0;h=(j|0)==45&1;if(k>>>0<(c[f>>2]|0)>>>0){c[g>>2]=k+1;j=d[k>>0]|0}else j=Zd(a)|0;if((j+ -48|0)>>>0>9&(b|0)!=0?(c[f>>2]|0)!=0:0)c[g>>2]=(c[g>>2]|0)+ -1}else h=0;if((j+ -48|0)>>>0>9){if(!(c[f>>2]|0)){j=-2147483648;k=0;F=j;i=e;return k|0}c[g>>2]=(c[g>>2]|0)+ -1;j=-2147483648;k=0;F=j;i=e;return k|0}else b=0;do{b=j+ -48+(b*10|0)|0;j=c[g>>2]|0;if(j>>>0<(c[f>>2]|0)>>>0){c[g>>2]=j+1;j=d[j>>0]|0}else j=Zd(a)|0}while((j+ -48|0)>>>0<10&(b|0)<214748364);k=((b|0)<0)<<31>>31;if((j+ -48|0)>>>0<10)do{k=we(b|0,k|0,10,0)|0;b=F;j=ne(j|0,((j|0)<0)<<31>>31|0,-48,-1)|0;b=ne(j|0,F|0,k|0,b|0)|0;k=F;j=c[g>>2]|0;if(j>>>0<(c[f>>2]|0)>>>0){c[g>>2]=j+1;j=d[j>>0]|0}else j=Zd(a)|0}while((j+ -48|0)>>>0<10&((k|0)<21474836|(k|0)==21474836&b>>>0<2061584302));if((j+ -48|0)>>>0<10)do{j=c[g>>2]|0;if(j>>>0<(c[f>>2]|0)>>>0){c[g>>2]=j+1;j=d[j>>0]|0}else j=Zd(a)|0}while((j+ -48|0)>>>0<10);if(c[f>>2]|0)c[g>>2]=(c[g>>2]|0)+ -1;g=(h|0)!=0;h=je(0,0,b|0,k|0)|0;j=g?F:k;k=g?h:b;F=j;i=e;return k|0}function ie(){}function je(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;b=b-d-(c>>>0>a>>>0|0)>>>0;return(F=b,a-c>>>0|0)|0}function ke(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;f=b+e|0;if((e|0)>=20){d=d&255;i=b&3;h=d|d<<8|d<<16|d<<24;g=f&~3;if(i){i=b+4-i|0;while((b|0)<(i|0)){a[b>>0]=d;b=b+1|0}}while((b|0)<(g|0)){c[b>>2]=h;b=b+4|0}}while((b|0)<(f|0)){a[b>>0]=d;b=b+1|0}return b-e|0}function le(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){F=b<<c|(a&(1<<c)-1<<32-c)>>>32-c;return a<<c}F=a<<c-32;return 0}function me(b){b=b|0;var c=0;c=b;while(a[c>>0]|0)c=c+1|0;return c-b|0}function ne(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;c=a+c>>>0;return(F=b+d+(c>>>0<a>>>0|0)>>>0,c|0)|0}function oe(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){F=b>>>c;return a>>>c|(b&(1<<c)-1)<<32-c}F=0;return b>>>c-32|0}function pe(b,d,e){b=b|0;d=d|0;e=e|0;var f=0;if((e|0)>=4096)return Ca(b|0,d|0,e|0)|0;f=b|0;if((b&3)==(d&3)){while(b&3){if(!e)return f|0;a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}while((e|0)>=4){c[b>>2]=c[d>>2];b=b+4|0;d=d+4|0;e=e-4|0}}while((e|0)>0){a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}return f|0}function qe(a,b,c){a=a|0;b=b|0;c=c|0;if((c|0)<32){F=b>>c;return a>>>c|(b&(1<<c)-1)<<32-c}F=(b|0)<0?-1:0;return b>>c-32|0}function re(b){b=b|0;var c=0;c=a[n+(b>>>24)>>0]|0;if((c|0)<8)return c|0;c=a[n+(b>>16&255)>>0]|0;if((c|0)<8)return c+8|0;c=a[n+(b>>8&255)>>0]|0;if((c|0)<8)return c+16|0;return(a[n+(b&255)>>0]|0)+24|0}function se(b){b=b|0;var c=0;c=a[m+(b&255)>>0]|0;if((c|0)<8)return c|0;c=a[m+(b>>8&255)>>0]|0;if((c|0)<8)return c+8|0;c=a[m+(b>>16&255)>>0]|0;if((c|0)<8)return c+16|0;return(a[m+(b>>>24)>>0]|0)+24|0}function te(a,b){a=a|0;b=b|0;var c=0,d=0,e=0,f=0;f=a&65535;d=b&65535;c=ba(d,f)|0;e=a>>>16;d=(c>>>16)+(ba(d,e)|0)|0;b=b>>>16;a=ba(b,f)|0;return(F=(d>>>16)+(ba(b,e)|0)+(((d&65535)+a|0)>>>16)|0,d+a<<16|c&65535|0)|0}function ue(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0,g=0,h=0,i=0,j=0;j=b>>31|((b|0)<0?-1:0)<<1;i=((b|0)<0?-1:0)>>31|((b|0)<0?-1:0)<<1;f=d>>31|((d|0)<0?-1:0)<<1;e=((d|0)<0?-1:0)>>31|((d|0)<0?-1:0)<<1;h=je(j^a,i^b,j,i)|0;g=F;b=f^j;a=e^i;a=je((ze(h,g,je(f^c,e^d,f,e)|0,F,0)|0)^b,F^a,b,a)|0;return a|0}function ve(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,j=0,k=0,l=0;f=i;i=i+8|0;j=f|0;h=b>>31|((b|0)<0?-1:0)<<1;g=((b|0)<0?-1:0)>>31|((b|0)<0?-1:0)<<1;l=e>>31|((e|0)<0?-1:0)<<1;k=((e|0)<0?-1:0)>>31|((e|0)<0?-1:0)<<1;b=je(h^a,g^b,h,g)|0;a=F;ze(b,a,je(l^d,k^e,l,k)|0,F,j)|0;a=je(c[j>>2]^h,c[j+4>>2]^g,h,g)|0;b=F;i=f;return(F=b,a)|0}function we(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;var e=0,f=0;e=a;f=c;a=te(e,f)|0;c=F;return(F=(ba(b,f)|0)+(ba(d,e)|0)+c|c&0,a|0|0)|0}function xe(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;a=ze(a,b,c,d,0)|0;return a|0}function ye(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0;g=i;i=i+8|0;f=g|0;ze(a,b,d,e,f)|0;i=g;return(F=c[f+4>>2]|0,c[f>>2]|0)|0}function ze(a,b,d,e,f){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0;h=a;j=b;i=j;l=d;g=e;k=g;if(!i){g=(f|0)!=0;if(!k){if(g){c[f>>2]=(h>>>0)%(l>>>0);c[f+4>>2]=0}k=0;m=(h>>>0)/(l>>>0)>>>0;return(F=k,m)|0}else{if(!g){l=0;m=0;return(F=l,m)|0}c[f>>2]=a|0;c[f+4>>2]=b&0;l=0;m=0;return(F=l,m)|0}}m=(k|0)==0;do if(l){if(!m){k=(re(k|0)|0)-(re(i|0)|0)|0;if(k>>>0<=31){m=k+1|0;l=31-k|0;a=k-31>>31;j=m;b=h>>>(m>>>0)&a|i<<l;a=i>>>(m>>>0)&a;k=0;l=h<<l;break}if(!f){l=0;m=0;return(F=l,m)|0}c[f>>2]=a|0;c[f+4>>2]=j|b&0;l=0;m=0;return(F=l,m)|0}k=l-1|0;if(k&l){l=(re(l|0)|0)+33-(re(i|0)|0)|0;p=64-l|0;m=32-l|0;n=m>>31;o=l-32|0;a=o>>31;j=l;b=m-1>>31&i>>>(o>>>0)|(i<<m|h>>>(l>>>0))&a;a=a&i>>>(l>>>0);k=h<<p&n;l=(i<<p|h>>>(o>>>0))&n|h<<m&l-33>>31;break}if(f){c[f>>2]=k&h;c[f+4>>2]=0}if((l|0)==1){o=j|b&0;p=a|0|0;return(F=o,p)|0}else{p=se(l|0)|0;o=i>>>(p>>>0)|0;p=i<<32-p|h>>>(p>>>0)|0;return(F=o,p)|0}}else{if(m){if(f){c[f>>2]=(i>>>0)%(l>>>0);c[f+4>>2]=0}o=0;p=(i>>>0)/(l>>>0)>>>0;return(F=o,p)|0}if(!h){if(f){c[f>>2]=0;c[f+4>>2]=(i>>>0)%(k>>>0)}o=0;p=(i>>>0)/(k>>>0)>>>0;return(F=o,p)|0}l=k-1|0;if(!(l&k)){if(f){c[f>>2]=a|0;c[f+4>>2]=l&i|b&0}o=0;p=i>>>((se(k|0)|0)>>>0);return(F=o,p)|0}k=(re(k|0)|0)-(re(i|0)|0)|0;if(k>>>0<=30){a=k+1|0;l=31-k|0;j=a;b=i<<l|h>>>(a>>>0);a=i>>>(a>>>0);k=0;l=h<<l;break}if(!f){o=0;p=0;return(F=o,p)|0}c[f>>2]=a|0;c[f+4>>2]=j|b&0;o=0;p=0;return(F=o,p)|0}while(0);if(!j){g=l;e=0;i=0}else{h=d|0|0;g=g|e&0;e=ne(h,g,-1,-1)|0;d=F;i=0;do{m=l;l=k>>>31|l<<1;k=i|k<<1;m=b<<1|m>>>31|0;n=b>>>31|a<<1|0;je(e,d,m,n)|0;p=F;o=p>>31|((p|0)<0?-1:0)<<1;i=o&1;b=je(m,n,o&h,(((p|0)<0?-1:0)>>31|((p|0)<0?-1:0)<<1)&g)|0;a=F;j=j-1|0}while((j|0)!=0);g=l;e=0}h=0;if(f){c[f>>2]=b;c[f+4>>2]=a}o=(k|0)>>>31|(g|h)<<1|(h<<1|k>>>31)&0|e;p=(k<<1|0>>>31)&-2|i;return(F=o,p)|0}function Ae(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return eb[a&1](b|0,c|0,d|0)|0}function Be(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;fb[a&3](b|0,c|0,d|0,e|0,f|0)}function Ce(a,b){a=a|0;b=b|0;gb[a&31](b|0)}function De(a,b,c){a=a|0;b=b|0;c=c|0;hb[a&3](b|0,c|0)}function Ee(a,b){a=a|0;b=b|0;return ib[a&1](b|0)|0}function Fe(a){a=a|0;jb[a&3]()}function Ge(a,b,c,d,e,f,g){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;kb[a&3](b|0,c|0,d|0,e|0,f|0,g|0)}function He(a,b,c){a=a|0;b=b|0;c=c|0;return lb[a&3](b|0,c|0)|0}function Ie(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;mb[a&3](b|0,c|0,d|0,e|0)}function Je(a,b,c){a=a|0;b=b|0;c=c|0;ca(0);return 0}function Ke(a,b,c,d,e){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;ca(1)}function Le(a){a=a|0;ca(2)}function Me(a,b){a=a|0;b=b|0;ca(3)}function Ne(a){a=a|0;ca(4);return 0}function Oe(){ca(5)}function Pe(){bb()}function Qe(a,b,c,d,e,f){a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;ca(6)}function Re(a,b){a=a|0;b=b|0;ca(7);return 0}function Se(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;ca(8)}
// EMSCRIPTEN_END_FUNCS
// (start of meteor/midamble.js)
// This "midamble" is hacked into the output JS in a place
// where it has access to the inner function generated
// by Emscripten, the one that starts with "use asm".
// NOTE: This doesn't work with minification on!
/////setInnerMalloc = function (hookedMalloc) {
///// _malloc = hookedMalloc;
/////};
/////setInnerFree = function (hookedFree) {
///// _free = hookedFree;
/////};
// (end of meteor/midamble.js)
var eb=[Je,Dd];var fb=[Ke,Kd,Jd,Ke];var gb=[Le,wb,yb,Ab,Db,Ib,Hb,bc,dc,zc,yc,Oc,rd,qd,yd,Bd,zd,Ad,Cd,zb,Rd,Le,Le,Le,Le,Le,Le,Le,Le,Le,Le,Le];var hb=[Me,Cb,Fb,fc];var ib=[Ne,sd];var jb=[Oe,Pe,Pd,Qd];var kb=[Qe,Md,Ld,Qe];var lb=[Re,Bb,Eb,ec];var mb=[Se,Fd,Gd,Se];return{_yo:$c,_strlen:me,_retireVar:id,_bitshift64Lshr:oe,_unyo:ad,_solve:ed,_bitshift64Shl:le,_getSolution:fd,___cxa_is_pointer_type:Od,_memset:ke,_getNumVars:gd,_memcpy:pe,_getConflictClauseSize:jd,_addClause:dd,_i64Subtract:je,_createTheSolver:bd,_realloc:Ud,_i64Add:ne,_solveAssuming:hd,___cxa_can_catch:Nd,_ensureVar:cd,_getConflictClause:kd,_free:Td,_malloc:Sd,__GLOBAL__I_a:cc,__GLOBAL__I_a127:Pc,runPostSets:ie,stackAlloc:nb,stackSave:ob,stackRestore:pb,setThrew:qb,setTempRet0:tb,getTempRet0:ub,dynCall_iiii:Ae,dynCall_viiiii:Be,dynCall_vi:Ce,dynCall_vii:De,dynCall_ii:Ee,dynCall_v:Fe,dynCall_viiiiii:Ge,dynCall_iii:He,dynCall_viiii:Ie}})
// EMSCRIPTEN_END_ASM
(Module.asmGlobalArg,Module.asmLibraryArg,buffer);var _yo=Module["_yo"]=asm["_yo"];var _strlen=Module["_strlen"]=asm["_strlen"];var _retireVar=Module["_retireVar"]=asm["_retireVar"];var _bitshift64Lshr=Module["_bitshift64Lshr"]=asm["_bitshift64Lshr"];var _unyo=Module["_unyo"]=asm["_unyo"];var _solve=Module["_solve"]=asm["_solve"];var _bitshift64Shl=Module["_bitshift64Shl"]=asm["_bitshift64Shl"];var _getSolution=Module["_getSolution"]=asm["_getSolution"];var ___cxa_is_pointer_type=Module["___cxa_is_pointer_type"]=asm["___cxa_is_pointer_type"];var _memset=Module["_memset"]=asm["_memset"];var _getNumVars=Module["_getNumVars"]=asm["_getNumVars"];var _memcpy=Module["_memcpy"]=asm["_memcpy"];var _getConflictClauseSize=Module["_getConflictClauseSize"]=asm["_getConflictClauseSize"];var _addClause=Module["_addClause"]=asm["_addClause"];var _i64Subtract=Module["_i64Subtract"]=asm["_i64Subtract"];var _createTheSolver=Module["_createTheSolver"]=asm["_createTheSolver"];var _realloc=Module["_realloc"]=asm["_realloc"];var _i64Add=Module["_i64Add"]=asm["_i64Add"];var _solveAssuming=Module["_solveAssuming"]=asm["_solveAssuming"];var ___cxa_can_catch=Module["___cxa_can_catch"]=asm["___cxa_can_catch"];var _ensureVar=Module["_ensureVar"]=asm["_ensureVar"];var _getConflictClause=Module["_getConflictClause"]=asm["_getConflictClause"];var _free=Module["_free"]=asm["_free"];var _malloc=Module["_malloc"]=asm["_malloc"];var __GLOBAL__I_a=Module["__GLOBAL__I_a"]=asm["__GLOBAL__I_a"];var __GLOBAL__I_a127=Module["__GLOBAL__I_a127"]=asm["__GLOBAL__I_a127"];var runPostSets=Module["runPostSets"]=asm["runPostSets"];var dynCall_iiii=Module["dynCall_iiii"]=asm["dynCall_iiii"];var dynCall_viiiii=Module["dynCall_viiiii"]=asm["dynCall_viiiii"];var dynCall_vi=Module["dynCall_vi"]=asm["dynCall_vi"];var dynCall_vii=Module["dynCall_vii"]=asm["dynCall_vii"];var dynCall_ii=Module["dynCall_ii"]=asm["dynCall_ii"];var dynCall_v=Module["dynCall_v"]=asm["dynCall_v"];var dynCall_viiiiii=Module["dynCall_viiiiii"]=asm["dynCall_viiiiii"];var dynCall_iii=Module["dynCall_iii"]=asm["dynCall_iii"];var dynCall_viiii=Module["dynCall_viiii"]=asm["dynCall_viiii"];Runtime.stackAlloc=asm["stackAlloc"];Runtime.stackSave=asm["stackSave"];Runtime.stackRestore=asm["stackRestore"];Runtime.setTempRet0=asm["setTempRet0"];Runtime.getTempRet0=asm["getTempRet0"];var i64Math=(function(){var goog={math:{}};goog.math.Long=(function(low,high){this.low_=low|0;this.high_=high|0});goog.math.Long.IntCache_={};goog.math.Long.fromInt=(function(value){if(-128<=value&&value<128){var cachedObj=goog.math.Long.IntCache_[value];if(cachedObj){return cachedObj}}var obj=new goog.math.Long(value|0,value<0?-1:0);if(-128<=value&&value<128){goog.math.Long.IntCache_[value]=obj}return obj});goog.math.Long.fromNumber=(function(value){if(isNaN(value)||!isFinite(value)){return goog.math.Long.ZERO}else if(value<=-goog.math.Long.TWO_PWR_63_DBL_){return goog.math.Long.MIN_VALUE}else if(value+1>=goog.math.Long.TWO_PWR_63_DBL_){return goog.math.Long.MAX_VALUE}else if(value<0){return goog.math.Long.fromNumber(-value).negate()}else{return new goog.math.Long(value%goog.math.Long.TWO_PWR_32_DBL_|0,value/goog.math.Long.TWO_PWR_32_DBL_|0)}});goog.math.Long.fromBits=(function(lowBits,highBits){return new goog.math.Long(lowBits,highBits)});goog.math.Long.fromString=(function(str,opt_radix){if(str.length==0){throw Error("number format error: empty string")}var radix=opt_radix||10;if(radix<2||36<radix){throw Error("radix out of range: "+radix)}if(str.charAt(0)=="-"){return goog.math.Long.fromString(str.substring(1),radix).negate()}else if(str.indexOf("-")>=0){throw Error('number format error: interior "-" character: '+str)}var radixToPower=goog.math.Long.fromNumber(Math.pow(radix,8));var result=goog.math.Long.ZERO;for(var i=0;i<str.length;i+=8){var size=Math.min(8,str.length-i);var value=parseInt(str.substring(i,i+size),radix);if(size<8){var power=goog.math.Long.fromNumber(Math.pow(radix,size));result=result.multiply(power).add(goog.math.Long.fromNumber(value))}else{result=result.multiply(radixToPower);result=result.add(goog.math.Long.fromNumber(value))}}return result});goog.math.Long.TWO_PWR_16_DBL_=1<<16;goog.math.Long.TWO_PWR_24_DBL_=1<<24;goog.math.Long.TWO_PWR_32_DBL_=goog.math.Long.TWO_PWR_16_DBL_*goog.math.Long.TWO_PWR_16_DBL_;goog.math.Long.TWO_PWR_31_DBL_=goog.math.Long.TWO_PWR_32_DBL_/2;goog.math.Long.TWO_PWR_48_DBL_=goog.math.Long.TWO_PWR_32_DBL_*goog.math.Long.TWO_PWR_16_DBL_;goog.math.Long.TWO_PWR_64_DBL_=goog.math.Long.TWO_PWR_32_DBL_*goog.math.Long.TWO_PWR_32_DBL_;goog.math.Long.TWO_PWR_63_DBL_=goog.math.Long.TWO_PWR_64_DBL_/2;goog.math.Long.ZERO=goog.math.Long.fromInt(0);goog.math.Long.ONE=goog.math.Long.fromInt(1);goog.math.Long.NEG_ONE=goog.math.Long.fromInt(-1);goog.math.Long.MAX_VALUE=goog.math.Long.fromBits(4294967295|0,2147483647|0);goog.math.Long.MIN_VALUE=goog.math.Long.fromBits(0,2147483648|0);goog.math.Long.TWO_PWR_24_=goog.math.Long.fromInt(1<<24);goog.math.Long.prototype.toInt=(function(){return this.low_});goog.math.Long.prototype.toNumber=(function(){return this.high_*goog.math.Long.TWO_PWR_32_DBL_+this.getLowBitsUnsigned()});goog.math.Long.prototype.toString=(function(opt_radix){var radix=opt_radix||10;if(radix<2||36<radix){throw Error("radix out of range: "+radix)}if(this.isZero()){return"0"}if(this.isNegative()){if(this.equals(goog.math.Long.MIN_VALUE)){var radixLong=goog.math.Long.fromNumber(radix);var div=this.div(radixLong);var rem=div.multiply(radixLong).subtract(this);return div.toString(radix)+rem.toInt().toString(radix)}else{return"-"+this.negate().toString(radix)}}var radixToPower=goog.math.Long.fromNumber(Math.pow(radix,6));var rem=this;var result="";while(true){var remDiv=rem.div(radixToPower);var intval=rem.subtract(remDiv.multiply(radixToPower)).toInt();var digits=intval.toString(radix);rem=remDiv;if(rem.isZero()){return digits+result}else{while(digits.length<6){digits="0"+digits}result=""+digits+result}}});goog.math.Long.prototype.getHighBits=(function(){return this.high_});goog.math.Long.prototype.getLowBits=(function(){return this.low_});goog.math.Long.prototype.getLowBitsUnsigned=(function(){return this.low_>=0?this.low_:goog.math.Long.TWO_PWR_32_DBL_+this.low_});goog.math.Long.prototype.getNumBitsAbs=(function(){if(this.isNegative()){if(this.equals(goog.math.Long.MIN_VALUE)){return 64}else{return this.negate().getNumBitsAbs()}}else{var val=this.high_!=0?this.high_:this.low_;for(var bit=31;bit>0;bit--){if((val&1<<bit)!=0){break}}return this.high_!=0?bit+33:bit+1}});goog.math.Long.prototype.isZero=(function(){return this.high_==0&&this.low_==0});goog.math.Long.prototype.isNegative=(function(){return this.high_<0});goog.math.Long.prototype.isOdd=(function(){return(this.low_&1)==1});goog.math.Long.prototype.equals=(function(other){return this.high_==other.high_&&this.low_==other.low_});goog.math.Long.prototype.notEquals=(function(other){return this.high_!=other.high_||this.low_!=other.low_});goog.math.Long.prototype.lessThan=(function(other){return this.compare(other)<0});goog.math.Long.prototype.lessThanOrEqual=(function(other){return this.compare(other)<=0});goog.math.Long.prototype.greaterThan=(function(other){return this.compare(other)>0});goog.math.Long.prototype.greaterThanOrEqual=(function(other){return this.compare(other)>=0});goog.math.Long.prototype.compare=(function(other){if(this.equals(other)){return 0}var thisNeg=this.isNegative();var otherNeg=other.isNegative();if(thisNeg&&!otherNeg){return-1}if(!thisNeg&&otherNeg){return 1}if(this.subtract(other).isNegative()){return-1}else{return 1}});goog.math.Long.prototype.negate=(function(){if(this.equals(goog.math.Long.MIN_VALUE)){return goog.math.Long.MIN_VALUE}else{return this.not().add(goog.math.Long.ONE)}});goog.math.Long.prototype.add=(function(other){var a48=this.high_>>>16;var a32=this.high_&65535;var a16=this.low_>>>16;var a00=this.low_&65535;var b48=other.high_>>>16;var b32=other.high_&65535;var b16=other.low_>>>16;var b00=other.low_&65535;var c48=0,c32=0,c16=0,c00=0;c00+=a00+b00;c16+=c00>>>16;c00&=65535;c16+=a16+b16;c32+=c16>>>16;c16&=65535;c32+=a32+b32;c48+=c32>>>16;c32&=65535;c48+=a48+b48;c48&=65535;return goog.math.Long.fromBits(c16<<16|c00,c48<<16|c32)});goog.math.Long.prototype.subtract=(function(other){return this.add(other.negate())});goog.math.Long.prototype.multiply=(function(other){if(this.isZero()){return goog.math.Long.ZERO}else if(other.isZero()){return goog.math.Long.ZERO}if(this.equals(goog.math.Long.MIN_VALUE)){return other.isOdd()?goog.math.Long.MIN_VALUE:goog.math.Long.ZERO}else if(other.equals(goog.math.Long.MIN_VALUE)){return this.isOdd()?goog.math.Long.MIN_VALUE:goog.math.Long.ZERO}if(this.isNegative()){if(other.isNegative()){return this.negate().multiply(other.negate())}else{return this.negate().multiply(other).negate()}}else if(other.isNegative()){return this.multiply(other.negate()).negate()}if(this.lessThan(goog.math.Long.TWO_PWR_24_)&&other.lessThan(goog.math.Long.TWO_PWR_24_)){return goog.math.Long.fromNumber(this.toNumber()*other.toNumber())}var a48=this.high_>>>16;var a32=this.high_&65535;var a16=this.low_>>>16;var a00=this.low_&65535;var b48=other.high_>>>16;var b32=other.high_&65535;var b16=other.low_>>>16;var b00=other.low_&65535;var c48=0,c32=0,c16=0,c00=0;c00+=a00*b00;c16+=c00>>>16;c00&=65535;c16+=a16*b00;c32+=c16>>>16;c16&=65535;c16+=a00*b16;c32+=c16>>>16;c16&=65535;c32+=a32*b00;c48+=c32>>>16;c32&=65535;c32+=a16*b16;c48+=c32>>>16;c32&=65535;c32+=a00*b32;c48+=c32>>>16;c32&=65535;c48+=a48*b00+a32*b16+a16*b32+a00*b48;c48&=65535;return goog.math.Long.fromBits(c16<<16|c00,c48<<16|c32)});goog.math.Long.prototype.div=(function(other){if(other.isZero()){throw Error("division by zero")}else if(this.isZero()){return goog.math.Long.ZERO}if(this.equals(goog.math.Long.MIN_VALUE)){if(other.equals(goog.math.Long.ONE)||other.equals(goog.math.Long.NEG_ONE)){return goog.math.Long.MIN_VALUE}else if(other.equals(goog.math.Long.MIN_VALUE)){return goog.math.Long.ONE}else{var halfThis=this.shiftRight(1);var approx=halfThis.div(other).shiftLeft(1);if(approx.equals(goog.math.Long.ZERO)){return other.isNegative()?goog.math.Long.ONE:goog.math.Long.NEG_ONE}else{var rem=this.subtract(other.multiply(approx));var result=approx.add(rem.div(other));return result}}}else if(other.equals(goog.math.Long.MIN_VALUE)){return goog.math.Long.ZERO}if(this.isNegative()){if(other.isNegative()){return this.negate().div(other.negate())}else{return this.negate().div(other).negate()}}else if(other.isNegative()){return this.div(other.negate()).negate()}var res=goog.math.Long.ZERO;var rem=this;while(rem.greaterThanOrEqual(other)){var approx=Math.max(1,Math.floor(rem.toNumber()/other.toNumber()));var log2=Math.ceil(Math.log(approx)/Math.LN2);var delta=log2<=48?1:Math.pow(2,log2-48);var approxRes=goog.math.Long.fromNumber(approx);var approxRem=approxRes.multiply(other);while(approxRem.isNegative()||approxRem.greaterThan(rem)){approx-=delta;approxRes=goog.math.Long.fromNumber(approx);approxRem=approxRes.multiply(other)}if(approxRes.isZero()){approxRes=goog.math.Long.ONE}res=res.add(approxRes);rem=rem.subtract(approxRem)}return res});goog.math.Long.prototype.modulo=(function(other){return this.subtract(this.div(other).multiply(other))});goog.math.Long.prototype.not=(function(){return goog.math.Long.fromBits(~this.low_,~this.high_)});goog.math.Long.prototype.and=(function(other){return goog.math.Long.fromBits(this.low_&other.low_,this.high_&other.high_)});goog.math.Long.prototype.or=(function(other){return goog.math.Long.fromBits(this.low_|other.low_,this.high_|other.high_)});goog.math.Long.prototype.xor=(function(other){return goog.math.Long.fromBits(this.low_^other.low_,this.high_^other.high_)});goog.math.Long.prototype.shiftLeft=(function(numBits){numBits&=63;if(numBits==0){return this}else{var low=this.low_;if(numBits<32){var high=this.high_;return goog.math.Long.fromBits(low<<numBits,high<<numBits|low>>>32-numBits)}else{return goog.math.Long.fromBits(0,low<<numBits-32)}}});goog.math.Long.prototype.shiftRight=(function(numBits){numBits&=63;if(numBits==0){return this}else{var high=this.high_;if(numBits<32){var low=this.low_;return goog.math.Long.fromBits(low>>>numBits|high<<32-numBits,high>>numBits)}else{return goog.math.Long.fromBits(high>>numBits-32,high>=0?0:-1)}}});goog.math.Long.prototype.shiftRightUnsigned=(function(numBits){numBits&=63;if(numBits==0){return this}else{var high=this.high_;if(numBits<32){var low=this.low_;return goog.math.Long.fromBits(low>>>numBits|high<<32-numBits,high>>>numBits)}else if(numBits==32){return goog.math.Long.fromBits(high,0)}else{return goog.math.Long.fromBits(high>>>numBits-32,0)}}});var navigator={appName:"Modern Browser"};var dbits;var canary=0xdeadbeefcafe;var j_lm=(canary&16777215)==15715070;function BigInteger(a,b,c){if(a!=null)if("number"==typeof a)this.fromNumber(a,b,c);else if(b==null&&"string"!=typeof a)this.fromString(a,256);else this.fromString(a,b)}function nbi(){return new BigInteger(null)}function am1(i,x,w,j,c,n){while(--n>=0){var v=x*this[i++]+w[j]+c;c=Math.floor(v/67108864);w[j++]=v&67108863}return c}function am2(i,x,w,j,c,n){var xl=x&32767,xh=x>>15;while(--n>=0){var l=this[i]&32767;var h=this[i++]>>15;var m=xh*l+h*xl;l=xl*l+((m&32767)<<15)+w[j]+(c&1073741823);c=(l>>>30)+(m>>>15)+xh*h+(c>>>30);w[j++]=l&1073741823}return c}function am3(i,x,w,j,c,n){var xl=x&16383,xh=x>>14;while(--n>=0){var l=this[i]&16383;var h=this[i++]>>14;var m=xh*l+h*xl;l=xl*l+((m&16383)<<14)+w[j]+c;c=(l>>28)+(m>>14)+xh*h;w[j++]=l&268435455}return c}if(j_lm&&navigator.appName=="Microsoft Internet Explorer"){BigInteger.prototype.am=am2;dbits=30}else if(j_lm&&navigator.appName!="Netscape"){BigInteger.prototype.am=am1;dbits=26}else{BigInteger.prototype.am=am3;dbits=28}BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=(1<<dbits)-1;BigInteger.prototype.DV=1<<dbits;var BI_FP=52;BigInteger.prototype.FV=Math.pow(2,BI_FP);BigInteger.prototype.F1=BI_FP-dbits;BigInteger.prototype.F2=2*dbits-BI_FP;var BI_RM="0123456789abcdefghijklmnopqrstuvwxyz";var BI_RC=new Array;var rr,vv;rr="0".charCodeAt(0);for(vv=0;vv<=9;++vv)BI_RC[rr++]=vv;rr="a".charCodeAt(0);for(vv=10;vv<36;++vv)BI_RC[rr++]=vv;rr="A".charCodeAt(0);for(vv=10;vv<36;++vv)BI_RC[rr++]=vv;function int2char(n){return BI_RM.charAt(n)}function intAt(s,i){var c=BI_RC[s.charCodeAt(i)];return c==null?-1:c}function bnpCopyTo(r){for(var i=this.t-1;i>=0;--i)r[i]=this[i];r.t=this.t;r.s=this.s}function bnpFromInt(x){this.t=1;this.s=x<0?-1:0;if(x>0)this[0]=x;else if(x<-1)this[0]=x+DV;else this.t=0}function nbv(i){var r=nbi();r.fromInt(i);return r}function bnpFromString(s,b){var k;if(b==16)k=4;else if(b==8)k=3;else if(b==256)k=8;else if(b==2)k=1;else if(b==32)k=5;else if(b==4)k=2;else{this.fromRadix(s,b);return}this.t=0;this.s=0;var i=s.length,mi=false,sh=0;while(--i>=0){var x=k==8?s[i]&255:intAt(s,i);if(x<0){if(s.charAt(i)=="-")mi=true;continue}mi=false;if(sh==0)this[this.t++]=x;else if(sh+k>this.DB){this[this.t-1]|=(x&(1<<this.DB-sh)-1)<<sh;this[this.t++]=x>>this.DB-sh}else this[this.t-1]|=x<<sh;sh+=k;if(sh>=this.DB)sh-=this.DB}if(k==8&&(s[0]&128)!=0){this.s=-1;if(sh>0)this[this.t-1]|=(1<<this.DB-sh)-1<<sh}this.clamp();if(mi)BigInteger.ZERO.subTo(this,this)}function bnpClamp(){var c=this.s&this.DM;while(this.t>0&&this[this.t-1]==c)--this.t}function bnToString(b){if(this.s<0)return"-"+this.negate().toString(b);var k;if(b==16)k=4;else if(b==8)k=3;else if(b==2)k=1;else if(b==32)k=5;else if(b==4)k=2;else return this.toRadix(b);var km=(1<<k)-1,d,m=false,r="",i=this.t;var p=this.DB-i*this.DB%k;if(i-->0){if(p<this.DB&&(d=this[i]>>p)>0){m=true;r=int2char(d)}while(i>=0){if(p<k){d=(this[i]&(1<<p)-1)<<k-p;d|=this[--i]>>(p+=this.DB-k)}else{d=this[i]>>(p-=k)&km;if(p<=0){p+=this.DB;--i}}if(d>0)m=true;if(m)r+=int2char(d)}}return m?r:"0"}function bnNegate(){var r=nbi();BigInteger.ZERO.subTo(this,r);return r}function bnAbs(){return this.s<0?this.negate():this}function bnCompareTo(a){var r=this.s-a.s;if(r!=0)return r;var i=this.t;r=i-a.t;if(r!=0)return this.s<0?-r:r;while(--i>=0)if((r=this[i]-a[i])!=0)return r;return 0}function nbits(x){var r=1,t;if((t=x>>>16)!=0){x=t;r+=16}if((t=x>>8)!=0){x=t;r+=8}if((t=x>>4)!=0){x=t;r+=4}if((t=x>>2)!=0){x=t;r+=2}if((t=x>>1)!=0){x=t;r+=1}return r}function bnBitLength(){if(this.t<=0)return 0;return this.DB*(this.t-1)+nbits(this[this.t-1]^this.s&this.DM)}function bnpDLShiftTo(n,r){var i;for(i=this.t-1;i>=0;--i)r[i+n]=this[i];for(i=n-1;i>=0;--i)r[i]=0;r.t=this.t+n;r.s=this.s}function bnpDRShiftTo(n,r){for(var i=n;i<this.t;++i)r[i-n]=this[i];r.t=Math.max(this.t-n,0);r.s=this.s}function bnpLShiftTo(n,r){var bs=n%this.DB;var cbs=this.DB-bs;var bm=(1<<cbs)-1;var ds=Math.floor(n/this.DB),c=this.s<<bs&this.DM,i;for(i=this.t-1;i>=0;--i){r[i+ds+1]=this[i]>>cbs|c;c=(this[i]&bm)<<bs}for(i=ds-1;i>=0;--i)r[i]=0;r[ds]=c;r.t=this.t+ds+1;r.s=this.s;r.clamp()}function bnpRShiftTo(n,r){r.s=this.s;var ds=Math.floor(n/this.DB);if(ds>=this.t){r.t=0;return}var bs=n%this.DB;var cbs=this.DB-bs;var bm=(1<<bs)-1;r[0]=this[ds]>>bs;for(var i=ds+1;i<this.t;++i){r[i-ds-1]|=(this[i]&bm)<<cbs;r[i-ds]=this[i]>>bs}if(bs>0)r[this.t-ds-1]|=(this.s&bm)<<cbs;r.t=this.t-ds;r.clamp()}function bnpSubTo(a,r){var i=0,c=0,m=Math.min(a.t,this.t);while(i<m){c+=this[i]-a[i];r[i++]=c&this.DM;c>>=this.DB}if(a.t<this.t){c-=a.s;while(i<this.t){c+=this[i];r[i++]=c&this.DM;c>>=this.DB}c+=this.s}else{c+=this.s;while(i<a.t){c-=a[i];r[i++]=c&this.DM;c>>=this.DB}c-=a.s}r.s=c<0?-1:0;if(c<-1)r[i++]=this.DV+c;else if(c>0)r[i++]=c;r.t=i;r.clamp()}function bnpMultiplyTo(a,r){var x=this.abs(),y=a.abs();var i=x.t;r.t=i+y.t;while(--i>=0)r[i]=0;for(i=0;i<y.t;++i)r[i+x.t]=x.am(0,y[i],r,i,0,x.t);r.s=0;r.clamp();if(this.s!=a.s)BigInteger.ZERO.subTo(r,r)}function bnpSquareTo(r){var x=this.abs();var i=r.t=2*x.t;while(--i>=0)r[i]=0;for(i=0;i<x.t-1;++i){var c=x.am(i,x[i],r,2*i,0,1);if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1))>=x.DV){r[i+x.t]-=x.DV;r[i+x.t+1]=1}}if(r.t>0)r[r.t-1]+=x.am(i,x[i],r,2*i,0,1);r.s=0;r.clamp()}function bnpDivRemTo(m,q,r){var pm=m.abs();if(pm.t<=0)return;var pt=this.abs();if(pt.t<pm.t){if(q!=null)q.fromInt(0);if(r!=null)this.copyTo(r);return}if(r==null)r=nbi();var y=nbi(),ts=this.s,ms=m.s;var nsh=this.DB-nbits(pm[pm.t-1]);if(nsh>0){pm.lShiftTo(nsh,y);pt.lShiftTo(nsh,r)}else{pm.copyTo(y);pt.copyTo(r)}var ys=y.t;var y0=y[ys-1];if(y0==0)return;var yt=y0*(1<<this.F1)+(ys>1?y[ys-2]>>this.F2:0);var d1=this.FV/yt,d2=(1<<this.F1)/yt,e=1<<this.F2;var i=r.t,j=i-ys,t=q==null?nbi():q;y.dlShiftTo(j,t);if(r.compareTo(t)>=0){r[r.t++]=1;r.subTo(t,r)}BigInteger.ONE.dlShiftTo(ys,t);t.subTo(y,y);while(y.t<ys)y[y.t++]=0;while(--j>=0){var qd=r[--i]==y0?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);if((r[i]+=y.am(0,qd,r,j,0,ys))<qd){y.dlShiftTo(j,t);r.subTo(t,r);while(r[i]<--qd)r.subTo(t,r)}}if(q!=null){r.drShiftTo(ys,q);if(ts!=ms)BigInteger.ZERO.subTo(q,q)}r.t=ys;r.clamp();if(nsh>0)r.rShiftTo(nsh,r);if(ts<0)BigInteger.ZERO.subTo(r,r)}function bnMod(a){var r=nbi();this.abs().divRemTo(a,null,r);if(this.s<0&&r.compareTo(BigInteger.ZERO)>0)a.subTo(r,r);return r}function Classic(m){this.m=m}function cConvert(x){if(x.s<0||x.compareTo(this.m)>=0)return x.mod(this.m);else return x}function cRevert(x){return x}function cReduce(x){x.divRemTo(this.m,null,x)}function cMulTo(x,y,r){x.multiplyTo(y,r);this.reduce(r)}function cSqrTo(x,r){x.squareTo(r);this.reduce(r)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1)return 0;var x=this[0];if((x&1)==0)return 0;var y=x&3;y=y*(2-(x&15)*y)&15;y=y*(2-(x&255)*y)&255;y=y*(2-((x&65535)*y&65535))&65535;y=y*(2-x*y%this.DV)%this.DV;return y>0?this.DV-y:-y}function Montgomery(m){this.m=m;this.mp=m.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<m.DB-15)-1;this.mt2=2*m.t}function montConvert(x){var r=nbi();x.abs().dlShiftTo(this.m.t,r);r.divRemTo(this.m,null,r);if(x.s<0&&r.compareTo(BigInteger.ZERO)>0)this.m.subTo(r,r);return r}function montRevert(x){var r=nbi();x.copyTo(r);this.reduce(r);return r}function montReduce(x){while(x.t<=this.mt2)x[x.t++]=0;for(var i=0;i<this.m.t;++i){var j=x[i]&32767;var u0=j*this.mpl+((j*this.mph+(x[i]>>15)*this.mpl&this.um)<<15)&x.DM;j=i+this.m.t;x[j]+=this.m.am(0,u0,x,i,0,this.m.t);while(x[j]>=x.DV){x[j]-=x.DV;x[++j]++}}x.clamp();x.drShiftTo(this.m.t,x);if(x.compareTo(this.m)>=0)x.subTo(this.m,x)}function montSqrTo(x,r){x.squareTo(r);this.reduce(r)}function montMulTo(x,y,r){x.multiplyTo(y,r);this.reduce(r)}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return(this.t>0?this[0]&1:this.s)==0}function bnpExp(e,z){if(e>4294967295||e<1)return BigInteger.ONE;var r=nbi(),r2=nbi(),g=z.convert(this),i=nbits(e)-1;g.copyTo(r);while(--i>=0){z.sqrTo(r,r2);if((e&1<<i)>0)z.mulTo(r2,g,r);else{var t=r;r=r2;r2=t}}return z.revert(r)}function bnModPowInt(e,m){var z;if(e<256||m.isEven())z=new Classic(m);else z=new Montgomery(m);return this.exp(e,z)}BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);function bnpFromRadix(s,b){this.fromInt(0);if(b==null)b=10;var cs=this.chunkSize(b);var d=Math.pow(b,cs),mi=false,j=0,w=0;for(var i=0;i<s.length;++i){var x=intAt(s,i);if(x<0){if(s.charAt(i)=="-"&&this.signum()==0)mi=true;continue}w=b*w+x;if(++j>=cs){this.dMultiply(d);this.dAddOffset(w,0);j=0;w=0}}if(j>0){this.dMultiply(Math.pow(b,j));this.dAddOffset(w,0)}if(mi)BigInteger.ZERO.subTo(this,this)}function bnpChunkSize(r){return Math.floor(Math.LN2*this.DB/Math.log(r))}function bnSigNum(){if(this.s<0)return-1;else if(this.t<=0||this.t==1&&this[0]<=0)return 0;else return 1}function bnpDMultiply(n){this[this.t]=this.am(0,n-1,this,0,0,this.t);++this.t;this.clamp()}function bnpDAddOffset(n,w){if(n==0)return;while(this.t<=w)this[this.t++]=0;this[w]+=n;while(this[w]>=this.DV){this[w]-=this.DV;if(++w>=this.t)this[this.t++]=0;++this[w]}}function bnpToRadix(b){if(b==null)b=10;if(this.signum()==0||b<2||b>36)return"0";var cs=this.chunkSize(b);var a=Math.pow(b,cs);var d=nbv(a),y=nbi(),z=nbi(),r="";this.divRemTo(d,y,z);while(y.signum()>0){r=(a+z.intValue()).toString(b).substr(1)+r;y.divRemTo(d,y,z)}return z.intValue().toString(b)+r}function bnIntValue(){if(this.s<0){if(this.t==1)return this[0]-this.DV;else if(this.t==0)return-1}else if(this.t==1)return this[0];else if(this.t==0)return 0;return(this[1]&(1<<32-this.DB)-1)<<this.DB|this[0]}function bnpAddTo(a,r){var i=0,c=0,m=Math.min(a.t,this.t);while(i<m){c+=this[i]+a[i];r[i++]=c&this.DM;c>>=this.DB}if(a.t<this.t){c+=a.s;while(i<this.t){c+=this[i];r[i++]=c&this.DM;c>>=this.DB}c+=this.s}else{c+=this.s;while(i<a.t){c+=a[i];r[i++]=c&this.DM;c>>=this.DB}c+=a.s}r.s=c<0?-1:0;if(c>0)r[i++]=c;else if(c<-1)r[i++]=this.DV+c;r.t=i;r.clamp()}BigInteger.prototype.fromRadix=bnpFromRadix;BigInteger.prototype.chunkSize=bnpChunkSize;BigInteger.prototype.signum=bnSigNum;BigInteger.prototype.dMultiply=bnpDMultiply;BigInteger.prototype.dAddOffset=bnpDAddOffset;BigInteger.prototype.toRadix=bnpToRadix;BigInteger.prototype.intValue=bnIntValue;BigInteger.prototype.addTo=bnpAddTo;var Wrapper={abs:(function(l,h){var x=new goog.math.Long(l,h);var ret;if(x.isNegative()){ret=x.negate()}else{ret=x}HEAP32[tempDoublePtr>>2]=ret.low_;HEAP32[tempDoublePtr+4>>2]=ret.high_}),ensureTemps:(function(){if(Wrapper.ensuredTemps)return;Wrapper.ensuredTemps=true;Wrapper.two32=new BigInteger;Wrapper.two32.fromString("4294967296",10);Wrapper.two64=new BigInteger;Wrapper.two64.fromString("18446744073709551616",10);Wrapper.temp1=new BigInteger;Wrapper.temp2=new BigInteger}),lh2bignum:(function(l,h){var a=new BigInteger;a.fromString(h.toString(),10);var b=new BigInteger;a.multiplyTo(Wrapper.two32,b);var c=new BigInteger;c.fromString(l.toString(),10);var d=new BigInteger;c.addTo(b,d);return d}),stringify:(function(l,h,unsigned){var ret=(new goog.math.Long(l,h)).toString();if(unsigned&&ret[0]=="-"){Wrapper.ensureTemps();var bignum=new BigInteger;bignum.fromString(ret,10);ret=new BigInteger;Wrapper.two64.addTo(bignum,ret);ret=ret.toString(10)}return ret}),fromString:(function(str,base,min,max,unsigned){Wrapper.ensureTemps();var bignum=new BigInteger;bignum.fromString(str,base);var bigmin=new BigInteger;bigmin.fromString(min,10);var bigmax=new BigInteger;bigmax.fromString(max,10);if(unsigned&&bignum.compareTo(BigInteger.ZERO)<0){var temp=new BigInteger;bignum.addTo(Wrapper.two64,temp);bignum=temp}var error=false;if(bignum.compareTo(bigmin)<0){bignum=bigmin;error=true}else if(bignum.compareTo(bigmax)>0){bignum=bigmax;error=true}var ret=goog.math.Long.fromString(bignum.toString());HEAP32[tempDoublePtr>>2]=ret.low_;HEAP32[tempDoublePtr+4>>2]=ret.high_;if(error)throw"range error"})};return Wrapper})();if(memoryInitializer){if(typeof Module["locateFile"]==="function"){memoryInitializer=Module["locateFile"](memoryInitializer)}else if(Module["memoryInitializerPrefixURL"]){memoryInitializer=Module["memoryInitializerPrefixURL"]+memoryInitializer}if(ENVIRONMENT_IS_NODE||ENVIRONMENT_IS_SHELL){var data=Module["readBinary"](memoryInitializer);HEAPU8.set(data,STATIC_BASE)}else{addRunDependency("memory initializer");Browser.asyncLoad(memoryInitializer,(function(data){HEAPU8.set(data,STATIC_BASE);removeRunDependency("memory initializer")}),(function(data){throw"could not load memory initializer "+memoryInitializer}))}}function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;var preloadStartTime=null;var calledMain=false;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"]&&shouldRunNow)run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};Module["callMain"]=Module.callMain=function callMain(args){assert(runDependencies==0,"cannot call main when async dependencies remain! (listen on __ATMAIN__)");assert(__ATPRERUN__.length==0,"cannot call main when preRun functions remain to be called");args=args||[];ensureInitRuntime();var argc=args.length+1;function pad(){for(var i=0;i<4-1;i++){argv.push(0)}}var argv=[allocate(intArrayFromString(Module["thisProgram"]),"i8",ALLOC_NORMAL)];pad();for(var i=0;i<argc-1;i=i+1){argv.push(allocate(intArrayFromString(args[i]),"i8",ALLOC_NORMAL));pad()}argv.push(0);argv=allocate(argv,"i32",ALLOC_NORMAL);initialStackTop=STACKTOP;try{var ret=Module["_main"](argc,argv,0);exit(ret)}catch(e){if(e instanceof ExitStatus){return}else if(e=="SimulateInfiniteLoop"){Module["noExitRuntime"]=true;return}else{if(e&&typeof e==="object"&&e.stack)Module.printErr("exception thrown: "+[e,e.stack]);throw e}}finally{calledMain=true}};function run(args){args=args||Module["arguments"];if(preloadStartTime===null)preloadStartTime=Date.now();if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(ENVIRONMENT_IS_WEB&&preloadStartTime!==null){Module.printErr("pre-main prep time: "+(Date.now()-preloadStartTime)+" ms")}if(Module["_main"]&&shouldRunNow){Module["callMain"](args)}postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=Module.run=run;function exit(status){if(Module["noExitRuntime"]){return}ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(ENVIRONMENT_IS_NODE){process["stdout"]["once"]("drain",(function(){process["exit"](status)}));console.log(" ");setTimeout((function(){process["exit"](status)}),500)}else if(ENVIRONMENT_IS_SHELL&&typeof quit==="function"){quit(status)}throw new ExitStatus(status)}Module["exit"]=Module.exit=exit;function abort(text){if(text){Module.print(text);Module.printErr(text)}ABORT=true;EXITSTATUS=1;var extra="\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";throw"abort() at "+stackTrace()+extra}Module["abort"]=Module.abort=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"]){shouldRunNow=false}run();var origMalloc=Module._malloc;var origFree=Module._free;var MEMSTATS={totalMemory:Module.HEAPU8.length,heapUsed:0};var MEMSTATS_DATA={pointerToSizeMap:{},getSizeOfPointer:(function(ptr){return MEMSTATS_DATA.pointerToSizeMap[ptr]})};Module.MEMSTATS=MEMSTATS;Module.MEMSTATS_DATA=MEMSTATS_DATA;var hookedMalloc=(function(size){var ptr=origMalloc(size);if(!ptr){return 0}MEMSTATS.heapUsed+=size;MEMSTATS_DATA.pointerToSizeMap[ptr]=size;return ptr});var hookedFree=(function(ptr){if(ptr){MEMSTATS.heapUsed-=MEMSTATS_DATA.getSizeOfPointer(ptr)||0;delete MEMSTATS_DATA.pointerToSizeMap[ptr]}return origFree(ptr)});Module._malloc=hookedMalloc;Module._free=hookedFree;_malloc=hookedMalloc;_free=hookedFree;var setInnerMalloc,setInnerFree;if(setInnerMalloc){setInnerMalloc(hookedMalloc);setInnerFree(hookedFree)}return module.exports});if(typeof module!=="undefined"){module.exports=C_MINISAT}
};
BundleModuleCode['ext/underscore']=function (module,exports){
// Underscore.js 1.9.0
// http://underscorejs.org
// (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` (`self`) in the browser, `global`
// on the server, or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
var root = typeof self == 'object' && self.self === self && self ||
typeof global == 'object' && global.global === global && global ||
this ||
{};
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
// Create quick reference variables for speed access to core prototypes.
var push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeCreate = Object.create;
// Naked function reference for surrogate-prototype-swapping.
var Ctor = function(){};
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for their old module API. If we're in
// the browser, add `_` as a global object.
// (`nodeType` is checked to ensure that `module`
// and `exports` are not HTML elements.)
if (typeof exports != 'undefined' && !exports.nodeType) {
if (typeof module != 'undefined' && !module.nodeType && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Current version.
_.VERSION = '1.9.0';
// Internal function that returns an efficient (for current engines) version
// of the passed-in callback, to be repeatedly applied in other Underscore
// functions.
var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
// The 2-argument case is omitted because were not using it.
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
var builtinIteratee;
// An internal function to generate callbacks that can be applied to each
// element in a collection, returning the desired result — either `identity`,
// an arbitrary callback, a property matcher, or a property accessor.
var cb = function(value, context, argCount) {
if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
if (value == null) return _.identity;
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
return _.property(value);
};
// External wrapper for our callback generator. Users may customize
// `_.iteratee` if they want additional predicate/iteratee shorthand styles.
// This abstraction hides the internal-only argCount argument.
_.iteratee = builtinIteratee = function(value, context) {
return cb(value, context, Infinity);
};
// Some functions take a variable number of arguments, or a few expected
// arguments at the beginning and then a variable number of values to operate
// on. This helper accumulates all remaining arguments past the functions
// argument length (or an explicit `startIndex`), into an array that becomes
// the last argument. Similar to ES6s "rest parameter".
var restArguments = function(func, startIndex) {
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
var length = Math.max(arguments.length - startIndex, 0),
rest = Array(length),
index = 0;
for (; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
switch (startIndex) {
case 0: return func.call(this, rest);
case 1: return func.call(this, arguments[0], rest);
case 2: return func.call(this, arguments[0], arguments[1], rest);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
};
};
// An internal function for creating a new object that inherits from another.
var baseCreate = function(prototype) {
if (!_.isObject(prototype)) return {};
if (nativeCreate) return nativeCreate(prototype);
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
};
var shallowProperty = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
var has = function(obj, path) {
return obj != null && hasOwnProperty.call(obj, path);
}
var deepGet = function(obj, path) {
var length = path.length;
for (var i = 0; i < length; i++) {
if (obj == null) return void 0;
obj = obj[path[i]];
}
return length ? obj : void 0;
};
// Helper for collection methods to determine whether a collection
// should be iterated as an array or as an object.
// Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = shallowProperty('length');
var isArrayLike = function(collection) {
var length = getLength(collection);
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles raw objects in addition to array-likes. Treats all
// sparse array-likes as if they were dense.
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
// Return the results of applying the iteratee to each element.
_.map = _.collect = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
results = Array(length);
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
// Create a reducing function iterating left or right.
var createReduce = function(dir) {
// Wrap code that reassigns argument variables in a separate function than
// the one that accesses `arguments.length` to avoid a perf hit. (#1991)
var reducer = function(obj, iteratee, memo, initial) {
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
if (!initial) {
memo = obj[keys ? keys[index] : index];
index += dir;
}
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
};
return function(obj, iteratee, memo, context) {
var initial = arguments.length >= 3;
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
};
};
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`.
_.reduce = _.foldl = _.inject = createReduce(1);
// The right-associative version of reduce, also known as `foldr`.
_.reduceRight = _.foldr = createReduce(-1);
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey;
var key = keyFinder(obj, predicate, context);
if (key !== void 0 && key !== -1) return obj[key];
};
// Return all the elements that pass a truth test.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
predicate = cb(predicate, context);
_.each(obj, function(value, index, list) {
if (predicate(value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, predicate, context) {
return _.filter(obj, _.negate(cb(predicate)), context);
};
// Determine whether all of the elements match a truth test.
// Aliased as `all`.
_.every = _.all = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length;
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
if (!predicate(obj[currentKey], currentKey, obj)) return false;
}
return true;
};
// Determine if at least one element in the object matches a truth test.
// Aliased as `any`.
_.some = _.any = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length;
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
if (predicate(obj[currentKey], currentKey, obj)) return true;
}
return false;
};
// Determine if the array or object contains a given item (using `===`).
// Aliased as `includes` and `include`.
_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
if (!isArrayLike(obj)) obj = _.values(obj);
if (typeof fromIndex != 'number' || guard) fromIndex = 0;
return _.indexOf(obj, item, fromIndex) >= 0;
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = restArguments(function(obj, path, args) {
var contextPath, func;
if (_.isFunction(path)) {
func = path;
} else if (_.isArray(path)) {
contextPath = path.slice(0, -1);
path = path[path.length - 1];
}
return _.map(obj, function(context) {
var method = func;
if (!method) {
if (contextPath && contextPath.length) {
context = deepGet(context, contextPath);
}
if (context == null) return void 0;
method = context[path];
}
return method == null ? method : method.apply(context, args);
});
});
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, _.property(key));
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_.where = function(obj, attrs) {
return _.filter(obj, _.matcher(attrs));
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.find(obj, _.matcher(attrs));
};
// Return the maximum element (or element-based computation).
_.max = function(obj, iteratee, context) {
var result = -Infinity, lastComputed = -Infinity,
value, computed;
if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value != null && value > result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
result = v;
lastComputed = computed;
}
});
}
return result;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iteratee, context) {
var result = Infinity, lastComputed = Infinity,
value, computed;
if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value != null && value < result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
if (computed < lastComputed || computed === Infinity && result === Infinity) {
result = v;
lastComputed = computed;
}
});
}
return result;
};
// Shuffle a collection.
_.shuffle = function(obj) {
return _.sample(obj, Infinity);
};
// Sample **n** random values from a collection using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/FisherYates_shuffle).
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
if (n == null || guard) {
if (!isArrayLike(obj)) obj = _.values(obj);
return obj[_.random(obj.length - 1)];
}
var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
var length = getLength(sample);
n = Math.max(Math.min(n, length), 0);
var last = length - 1;
for (var index = 0; index < n; index++) {
var rand = _.random(index, last);
var temp = sample[index];
sample[index] = sample[rand];
sample[rand] = temp;
}
return sample.slice(0, n);
};
// Sort the object's values by a criterion produced by an iteratee.
_.sortBy = function(obj, iteratee, context) {
var index = 0;
iteratee = cb(iteratee, context);
return _.pluck(_.map(obj, function(value, key, list) {
return {
value: value,
index: index++,
criteria: iteratee(value, key, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index - right.index;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
var group = function(behavior, partition) {
return function(obj, iteratee, context) {
var result = partition ? [[], []] : {};
iteratee = cb(iteratee, context);
_.each(obj, function(value, index) {
var key = iteratee(value, index, obj);
behavior(result, value, key);
});
return result;
};
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = group(function(result, value, key) {
if (has(result, key)) result[key].push(value); else result[key] = [value];
});
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
_.indexBy = group(function(result, value, key) {
result[key] = value;
});
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = group(function(result, value, key) {
if (has(result, key)) result[key]++; else result[key] = 1;
});
var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (_.isString(obj)) {
// Keep surrogate pair characters together
return obj.match(reStrSymbol);
}
if (isArrayLike(obj)) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};
// Split a collection into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = group(function(result, value, pass) {
result[pass ? 0 : 1].push(value);
}, true);
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null || array.length < 1) return void 0;
if (n == null || guard) return array[0];
return _.initial(array, array.length - n);
};
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N.
_.initial = function(array, n, guard) {
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array.
_.last = function(array, n, guard) {
if (array == null || array.length < 1) return void 0;
if (n == null || guard) return array[array.length - 1];
return _.rest(array, Math.max(0, array.length - n));
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array.
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, n == null || guard ? 1 : n);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, Boolean);
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, strict, output) {
output = output || [];
var idx = output.length;
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
// Flatten current level of array or arguments object.
if (shallow) {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
} else {
flatten(value, shallow, strict, output);
idx = output.length;
}
} else if (!strict) {
output[idx++] = value;
}
}
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
return flatten(array, shallow, false);
};
// Return a version of the array that does not contain the specified value(s).
_.without = restArguments(function(array, otherArrays) {
return _.difference(array, otherArrays);
});
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// The faster algorithm will not work with an iteratee if the iteratee
// is not a one-to-one function, so providing an iteratee will disable
// the faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
if (!_.isBoolean(isSorted)) {
context = iteratee;
iteratee = isSorted;
isSorted = false;
}
if (iteratee != null) iteratee = cb(iteratee, context);
var result = [];
var seen = [];
for (var i = 0, length = getLength(array); i < length; i++) {
var value = array[i],
computed = iteratee ? iteratee(value, i, array) : value;
if (isSorted && !iteratee) {
if (!i || seen !== computed) result.push(value);
seen = computed;
} else if (iteratee) {
if (!_.contains(seen, computed)) {
seen.push(computed);
result.push(value);
}
} else if (!_.contains(result, value)) {
result.push(value);
}
}
return result;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = restArguments(function(arrays) {
return _.uniq(flatten(arrays, true, true));
});
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
var result = [];
var argsLength = arguments.length;
for (var i = 0, length = getLength(array); i < length; i++) {
var item = array[i];
if (_.contains(result, item)) continue;
var j;
for (j = 1; j < argsLength; j++) {
if (!_.contains(arguments[j], item)) break;
}
if (j === argsLength) result.push(item);
}
return result;
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = restArguments(function(array, rest) {
rest = flatten(rest, true, true);
return _.filter(array, function(value){
return !_.contains(rest, value);
});
});
// Complement of _.zip. Unzip accepts an array of arrays and groups
// each array's elements on shared indices.
_.unzip = function(array) {
var length = array && _.max(array, getLength).length || 0;
var result = Array(length);
for (var index = 0; index < length; index++) {
result[index] = _.pluck(array, index);
}
return result;
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = restArguments(_.unzip);
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values. Passing by pairs is the reverse of _.pairs.
_.object = function(list, values) {
var result = {};
for (var i = 0, length = getLength(list); i < length; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// Generator function to create the findIndex and findLastIndex functions.
var createPredicateIndexFinder = function(dir) {
return function(array, predicate, context) {
predicate = cb(predicate, context);
var length = getLength(array);
var index = dir > 0 ? 0 : length - 1;
for (; index >= 0 && index < length; index += dir) {
if (predicate(array[index], index, array)) return index;
}
return -1;
};
};
// Returns the first index on an array-like that passes a predicate test.
_.findIndex = createPredicateIndexFinder(1);
_.findLastIndex = createPredicateIndexFinder(-1);
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iteratee, context) {
iteratee = cb(iteratee, context, 1);
var value = iteratee(obj);
var low = 0, high = getLength(array);
while (low < high) {
var mid = Math.floor((low + high) / 2);
if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
}
return low;
};
// Generator function to create the indexOf and lastIndexOf functions.
var createIndexFinder = function(dir, predicateFind, sortedIndex) {
return function(array, item, idx) {
var i = 0, length = getLength(array);
if (typeof idx == 'number') {
if (dir > 0) {
i = idx >= 0 ? idx : Math.max(idx + length, i);
} else {
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
}
} else if (sortedIndex && idx && length) {
idx = sortedIndex(array, item);
return array[idx] === item ? idx : -1;
}
if (item !== item) {
idx = predicateFind(slice.call(array, i, length), _.isNaN);
return idx >= 0 ? idx + i : -1;
}
for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
if (array[idx] === item) return idx;
}
return -1;
};
};
// Return the position of the first occurrence of an item in an array,
// or -1 if the item is not included in the array.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (stop == null) {
stop = start || 0;
start = 0;
}
if (!step) {
step = stop < start ? -1 : 1;
}
var length = Math.max(Math.ceil((stop - start) / step), 0);
var range = Array(length);
for (var idx = 0; idx < length; idx++, start += step) {
range[idx] = start;
}
return range;
};
// Chunk a single array into multiple arrays, each containing `count` or fewer
// items.
_.chunk = function(array, count) {
if (count == null || count < 1) return [];
var result = [];
var i = 0, length = array.length;
while (i < length) {
result.push(slice.call(array, i, i += count));
}
return result;
};
// Function (ahem) Functions
// ------------------
// Determines whether to execute a function as a constructor
// or a normal function with the provided arguments.
var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
var self = baseCreate(sourceFunc.prototype);
var result = sourceFunc.apply(self, args);
if (_.isObject(result)) return result;
return self;
};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = restArguments(function(func, context, args) {
if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
var bound = restArguments(function(callArgs) {
return executeBound(func, bound, context, this, args.concat(callArgs));
});
return bound;
});
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context. _ acts
// as a placeholder by default, allowing any combination of arguments to be
// pre-filled. Set `_.partial.placeholder` for a custom placeholder argument.
_.partial = restArguments(function(func, boundArgs) {
var placeholder = _.partial.placeholder;
var bound = function() {
var position = 0, length = boundArgs.length;
var args = Array(length);
for (var i = 0; i < length; i++) {
args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
}
while (position < arguments.length) args.push(arguments[position++]);
return executeBound(func, bound, this, this, args);
};
return bound;
});
_.partial.placeholder = _;
// Bind a number of an object's methods to that object. Remaining arguments
// are the method names to be bound. Useful for ensuring that all callbacks
// defined on an object belong to it.
_.bindAll = restArguments(function(obj, keys) {
keys = flatten(keys, false, false);
var index = keys.length;
if (index < 1) throw new Error('bindAll must be passed function names');
while (index--) {
var key = keys[index];
obj[key] = _.bind(obj[key], obj);
}
});
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memoize = function(key) {
var cache = memoize.cache;
var address = '' + (hasher ? hasher.apply(this, arguments) : key);
if (!has(cache, address)) cache[address] = func.apply(this, arguments);
return cache[address];
};
memoize.cache = {};
return memoize;
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = restArguments(function(func, wait, args) {
return setTimeout(function() {
return func.apply(null, args);
}, wait);
});
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = _.partial(_.delay, _, 1);
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var timeout, context, args, result;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
var throttled = function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = context = args = null;
};
return throttled;
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, result;
var later = function(context, args) {
timeout = null;
if (args) result = func.apply(context, args);
};
var debounced = restArguments(function(args) {
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
timeout = _.delay(later, wait, this, args);
}
return result;
});
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return _.partial(wrapper, func);
};
// Returns a negated version of the passed-in predicate.
_.negate = function(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var args = arguments;
var start = args.length - 1;
return function() {
var i = start;
var result = args[start].apply(this, arguments);
while (i--) result = args[i].call(this, result);
return result;
};
};
// Returns a function that will only be executed on and after the Nth call.
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
// Returns a function that will only be executed up to (but not including) the Nth call.
_.before = function(times, func) {
var memo;
return function() {
if (--times > 0) {
memo = func.apply(this, arguments);
}
if (times <= 1) func = null;
return memo;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = _.partial(_.before, 2);
_.restArguments = restArguments;
// Object Functions
// ----------------
// Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
var collectNonEnumProps = function(obj, keys) {
var nonEnumIdx = nonEnumerableProps.length;
var constructor = obj.constructor;
var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
// Constructor is a special case.
var prop = 'constructor';
if (has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
while (nonEnumIdx--) {
prop = nonEnumerableProps[nonEnumIdx];
if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
keys.push(prop);
}
}
};
// Retrieve the names of an object's own properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`.
_.keys = function(obj) {
if (!_.isObject(obj)) return [];
if (nativeKeys) return nativeKeys(obj);
var keys = [];
for (var key in obj) if (has(obj, key)) keys.push(key);
// Ahem, IE < 9.
if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
};
// Retrieve all the property names of an object.
_.allKeys = function(obj) {
if (!_.isObject(obj)) return [];
var keys = [];
for (var key in obj) keys.push(key);
// Ahem, IE < 9.
if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
return values;
};
// Returns the results of applying the iteratee to each element of the object.
// In contrast to _.map it returns an object.
_.mapObject = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var keys = _.keys(obj),
length = keys.length,
results = {};
for (var index = 0; index < length; index++) {
var currentKey = keys[index];
results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
// Convert an object into a list of `[key, value]` pairs.
// The opposite of _.object.
_.pairs = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var pairs = Array(length);
for (var i = 0; i < length; i++) {
pairs[i] = [keys[i], obj[keys[i]]];
}
return pairs;
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
var result = {};
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
result[obj[keys[i]]] = keys[i];
}
return result;
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`.
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// An internal function for creating assigner functions.
var createAssigner = function(keysFunc, defaults) {
return function(obj) {
var length = arguments.length;
if (defaults) obj = Object(obj);
if (length < 2 || obj == null) return obj;
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length;
for (var i = 0; i < l; i++) {
var key = keys[i];
if (!defaults || obj[key] === void 0) obj[key] = source[key];
}
}
return obj;
};
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = createAssigner(_.allKeys);
// Assigns a given object with all the own properties in the passed-in object(s).
// (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
_.extendOwn = _.assign = createAssigner(_.keys);
// Returns the first key on an object that passes a predicate test.
_.findKey = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = _.keys(obj), key;
for (var i = 0, length = keys.length; i < length; i++) {
key = keys[i];
if (predicate(obj[key], key, obj)) return key;
}
};
// Internal pick helper function to determine if `obj` has key `key`.
var keyInObj = function(value, key, obj) {
return key in obj;
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = restArguments(function(obj, keys) {
var result = {}, iteratee = keys[0];
if (obj == null) return result;
if (_.isFunction(iteratee)) {
if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
keys = _.allKeys(obj);
} else {
iteratee = keyInObj;
keys = flatten(keys, false, false);
obj = Object(obj);
}
for (var i = 0, length = keys.length; i < length; i++) {
var key = keys[i];
var value = obj[key];
if (iteratee(value, key, obj)) result[key] = value;
}
return result;
});
// Return a copy of the object without the blacklisted properties.
_.omit = restArguments(function(obj, keys) {
var iteratee = keys[0], context;
if (_.isFunction(iteratee)) {
iteratee = _.negate(iteratee);
if (keys.length > 1) context = keys[1];
} else {
keys = _.map(flatten(keys, false, false), String);
iteratee = function(value, key) {
return !_.contains(keys, key);
};
}
return _.pick(obj, iteratee, context);
});
// Fill in a given object with default properties.
_.defaults = createAssigner(_.allKeys, true);
// Creates an object that inherits from the given prototype object.
// If additional properties are provided then they will be added to the
// created object.
_.create = function(prototype, props) {
var result = baseCreate(prototype);
if (props) _.extendOwn(result, props);
return result;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Returns whether an object has a given set of `key:value` pairs.
_.isMatch = function(object, attrs) {
var keys = _.keys(attrs), length = keys.length;
if (object == null) return !length;
var obj = Object(object);
for (var i = 0; i < length; i++) {
var key = keys[i];
if (attrs[key] !== obj[key] || !(key in obj)) return false;
}
return true;
};
// Internal recursive comparison function for `isEqual`.
var eq, deepEq;
eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) return a !== 0 || 1 / a === 1 / b;
// `null` or `undefined` only equal to itself (strict comparison).
if (a == null || b == null) return false;
// `NaN`s are equivalent, but non-reflexive.
if (a !== a) return b !== b;
// Exhaust primitive checks
var type = typeof a;
if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
return deepEq(a, b, aStack, bStack);
};
// Internal recursive comparison function for `isEqual`.
deepEq = function(a, b, aStack, bStack) {
// Unwrap any wrapped objects.
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className !== toString.call(b)) return false;
switch (className) {
// Strings, numbers, regular expressions, dates, and booleans are compared by value.
case '[object RegExp]':
// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return '' + a === '' + b;
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive.
// Object(NaN) is equivalent to NaN.
if (+a !== +a) return +b !== +b;
// An `egal` comparison is performed for other numeric values.
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a === +b;
case '[object Symbol]':
return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
}
var areArrays = className === '[object Array]';
if (!areArrays) {
if (typeof a != 'object' || typeof b != 'object') return false;
// Objects with different constructors are not equivalent, but `Object`s or `Array`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
_.isFunction(bCtor) && bCtor instanceof bCtor)
&& ('constructor' in a && 'constructor' in b)) {
return false;
}
}
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
// Initializing stack of traversed objects.
// It's done here since we only need them for objects and arrays comparison.
aStack = aStack || [];
bStack = bStack || [];
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] === a) return bStack[length] === b;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
// Recursively compare objects and arrays.
if (areArrays) {
// Compare array lengths to determine if a deep comparison is necessary.
length = a.length;
if (length !== b.length) return false;
// Deep compare the contents, ignoring non-numeric properties.
while (length--) {
if (!eq(a[length], b[length], aStack, bStack)) return false;
}
} else {
// Deep compare objects.
var keys = _.keys(a), key;
length = keys.length;
// Ensure that both objects contain the same number of properties before comparing deep equality.
if (_.keys(b).length !== length) return false;
while (length--) {
// Deep compare each member
key = keys[length];
if (!(has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return true;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
return _.keys(obj).length === 0;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) === '[object ' + name + ']';
};
});
// Define a fallback version of the method in browsers (ahem, IE < 9), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return has(obj, 'callee');
};
}
// Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
// IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).
var nodelist = root.document && root.document.childNodes;
if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
_.isFunction = function(obj) {
return typeof obj == 'function' || false;
};
}
// Is a given object a finite number?
_.isFinite = function(obj) {
return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`?
_.isNaN = function(obj) {
return _.isNumber(obj) && isNaN(obj);
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, path) {
if (!_.isArray(path)) {
return has(obj, path);
}
var length = path.length;
for (var i = 0; i < length; i++) {
var key = path[i];
if (obj == null || !hasOwnProperty.call(obj, key)) {
return false;
}
obj = obj[key];
}
return !!length;
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iteratees.
_.identity = function(value) {
return value;
};
// Predicate-generating functions. Often useful outside of Underscore.
_.constant = function(value) {
return function() {
return value;
};
};
_.noop = function(){};
// Creates a function that, when passed an object, will traverse that objects
// properties down the given `path`, specified as an array of keys or indexes.
_.property = function(path) {
if (!_.isArray(path)) {
return shallowProperty(path);
}
return function(obj) {
return deepGet(obj, path);
};
};
// Generates a function for a given object that returns a given property.
_.propertyOf = function(obj) {
if (obj == null) {
return function(){};
}
return function(path) {
return !_.isArray(path) ? obj[path] : deepGet(obj, path);
};
};
// Returns a predicate for checking whether an object has a given set of
// `key:value` pairs.
_.matcher = _.matches = function(attrs) {
attrs = _.extendOwn({}, attrs);
return function(obj) {
return _.isMatch(obj, attrs);
};
};
// Run a function **n** times.
_.times = function(n, iteratee, context) {
var accum = Array(Math.max(0, n));
iteratee = optimizeCb(iteratee, context, 1);
for (var i = 0; i < n; i++) accum[i] = iteratee(i);
return accum;
};
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
};
// A (possibly faster) way to get the current timestamp as an integer.
_.now = Date.now || function() {
return new Date().getTime();
};
// List of HTML entities for escaping.
var escapeMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'`': '&#x60;'
};
var unescapeMap = _.invert(escapeMap);
// Functions for escaping and unescaping strings to/from HTML interpolation.
var createEscaper = function(map) {
var escaper = function(match) {
return map[match];
};
// Regexes for identifying a key that needs to be escaped.
var source = '(?:' + _.keys(map).join('|') + ')';
var testRegexp = RegExp(source);
var replaceRegexp = RegExp(source, 'g');
return function(string) {
string = string == null ? '' : '' + string;
return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
};
};
_.escape = createEscaper(escapeMap);
_.unescape = createEscaper(unescapeMap);
// Traverses the children of `obj` along `path`. If a child is a function, it
// is invoked with its parent as context. Returns the value of the final
// child, or `fallback` if any child is undefined.
_.result = function(obj, path, fallback) {
if (!_.isArray(path)) path = [path];
var length = path.length;
if (!length) {
return _.isFunction(fallback) ? fallback.call(obj) : fallback;
}
for (var i = 0; i < length; i++) {
var prop = obj == null ? void 0 : obj[path[i]];
if (prop === void 0) {
prop = fallback;
i = length; // Ensure we don't continue iterating.
}
obj = _.isFunction(prop) ? prop.call(obj) : prop;
}
return obj;
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate: /<%([\s\S]+?)%>/g,
interpolate: /<%=([\s\S]+?)%>/g,
escape: /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
var escapeChar = function(match) {
return '\\' + escapes[match];
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
// NB: `oldSettings` only exists for backwards compatibility.
_.template = function(text, settings, oldSettings) {
if (!settings && oldSettings) settings = oldSettings;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
index = offset + match.length;
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
} else if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
} else if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
// Adobe VMs need the match returned to produce the correct offset.
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + 'return __p;\n';
var render;
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled source as a convenience for precompilation.
var argument = settings.variable || 'obj';
template.source = 'function(' + argument + '){\n' + source + '}';
return template;
};
// Add a "chain" function. Start chaining a wrapped Underscore object.
_.chain = function(obj) {
var instance = _(obj);
instance._chain = true;
return instance;
};
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var chainResult = function(instance, obj) {
return instance._chain ? _(obj).chain() : obj;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
_.each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return chainResult(this, func.apply(_, args));
};
});
return _;
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
return chainResult(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
_.each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return chainResult(this, method.apply(this._wrapped, arguments));
};
});
// Extracts the result from a wrapped and chained object.
_.prototype.value = function() {
return this._wrapped;
};
// Provide unwrapping proxy for some methods used in engine operations
// such as arithmetic and JSON stringification.
_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
_.prototype.toString = function() {
return String(this._wrapped);
};
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define == 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
}());
};
BundleModuleCode['ext/jsep']=function (module,exports){
// JavaScript Expression Parser (JSEP) <%= version %>
// JSEP may be freely distributed under the MIT License
// http://jsep.from.so/
/*global module: true, exports: true, console: true */
(function (root) {
'use strict';
// Node Types
// ----------
// This is the full set of types that any JSEP node can be.
// Store them here to save space when minified
var COMPOUND = 'Compound',
IDENTIFIER = 'Identifier',
MEMBER_EXP = 'MemberExpression',
LITERAL = 'Literal',
THIS_EXP = 'ThisExpression',
CALL_EXP = 'CallExpression',
UNARY_EXP = 'UnaryExpression',
BINARY_EXP = 'BinaryExpression',
LOGICAL_EXP = 'LogicalExpression',
CONDITIONAL_EXP = 'ConditionalExpression',
ARRAY_EXP = 'ArrayExpression',
PERIOD_CODE = 46, // '.'
COMMA_CODE = 44, // ','
SQUOTE_CODE = 39, // single quote
DQUOTE_CODE = 34, // double quotes
OPAREN_CODE = 40, // (
CPAREN_CODE = 41, // )
OBRACK_CODE = 91, // [
CBRACK_CODE = 93, // ]
QUMARK_CODE = 63, // ?
SEMCOL_CODE = 59, // ;
COLON_CODE = 58, // :
throwError = function(message, index) {
var error = new Error(message + ' at character ' + index);
error.index = index;
error.description = message;
throw error;
},
// Operations
// ----------
// Set `t` to `true` to save space (when minified, not gzipped)
t = true,
// Use a quickly-accessible map to store all of the unary operators
// Values are set to `true` (it really doesn't matter)
unary_ops = {'-': t, '!': t, '~': t, '+': t},
// Also use a map for the binary operations but set their values to their
// binary precedence for quick reference:
// see [Order of operations](http://en.wikipedia.org/wiki/Order_of_operations#Programming_language)
binary_ops = {
'||': 1, '&&': 2, '|': 3, /* or */
'^': 4 /*xor*/, '^1': 4 /*exactlyonce*/, '^0': 4 /*atmostonce*/,
'&': 5, /* and */ '->':5, /* implies */
'==': 6, '!=': 6, '===': 6, '!==': 6, '==~':6,
'<': 7, '>': 7, '<=': 7, '>=': 7,
'<<':8, '>>': 8, '>>>': 8,
'+': 9, '-': 9 /* not */,
'*': 10, '/': 10, '%': 10,
},
// Get return the longest key length of any object
getMaxKeyLen = function(obj) {
var max_len = 0, len;
for(var key in obj) {
if((len = key.length) > max_len && obj.hasOwnProperty(key)) {
max_len = len;
}
}
return max_len;
},
max_unop_len = getMaxKeyLen(unary_ops),
max_binop_len = getMaxKeyLen(binary_ops),
// Literals
// ----------
// Store the values to return for the various literals we may encounter
literals = {
'true': true,
'false': false,
'null': null
},
// Except for `this`, which is special. This could be changed to something like `'self'` as well
this_str = 'this',
// Returns the precedence of a binary operator or `0` if it isn't a binary operator
binaryPrecedence = function(op_val) {
return binary_ops[op_val] || 0;
},
// Utility function (gets called from multiple places)
// Also note that `a && b` and `a || b` are *logical* expressions, not binary expressions
createBinaryExpression = function (operator, left, right) {
var type = (operator === '||' || operator === '&&') ? LOGICAL_EXP : BINARY_EXP;
return {
type: type,
operator: operator,
left: left,
right: right
};
},
// `ch` is a character code in the next three functions
isDecimalDigit = function(ch) {
return (ch >= 48 && ch <= 57); // 0...9
},
isIdentifierStart = function(ch) {
return (ch === 36) || (ch === 95) || // `$` and `_`
(ch >= 65 && ch <= 90) || // A...Z
(ch >= 97 && ch <= 122) || // a...z
(ch >= 128 && !binary_ops[String.fromCharCode(ch)]); // any non-ASCII that is not an operator
},
isIdentifierPart = function(ch) {
return (ch === 36) || (ch === 95) || // `$` and `_`
(ch >= 65 && ch <= 90) || // A...Z
(ch >= 97 && ch <= 122) || // a...z
(ch >= 48 && ch <= 57) || // 0...9
(ch >= 128 && !binary_ops[String.fromCharCode(ch)]); // any non-ASCII that is not an operator
},
// Parsing
// -------
// `expr` is a string with the passed in expression
jsep = function(expr) {
// `index` stores the character number we are currently at while `length` is a constant
// All of the gobbles below will modify `index` as we move along
var index = 0,
charAtFunc = expr.charAt,
charCodeAtFunc = expr.charCodeAt,
exprI = function(i) { return charAtFunc.call(expr, i); },
exprICode = function(i) { return charCodeAtFunc.call(expr, i); },
length = expr.length,
// Push `index` up to the next non-space character
gobbleSpaces = function() {
var ch = exprICode(index);
// space or tab
while(ch === 32 || ch === 9 || ch === 10 || ch === 13) {
ch = exprICode(++index);
}
},
// The main parsing function. Much of this code is dedicated to ternary expressions
gobbleExpression = function() {
var test = gobbleBinaryExpression(),
consequent, alternate;
gobbleSpaces();
if(exprICode(index) === QUMARK_CODE) {
// Ternary expression: test ? consequent : alternate
index++;
consequent = gobbleExpression();
if(!consequent) {
throwError('Expected expression', index);
}
gobbleSpaces();
if(exprICode(index) === COLON_CODE) {
index++;
alternate = gobbleExpression();
if(!alternate) {
throwError('Expected expression', index);
}
return {
type: CONDITIONAL_EXP,
test: test,
consequent: consequent,
alternate: alternate
};
} else {
throwError('Expected :', index);
}
} else {
return test;
}
},
// Search for the operation portion of the string (e.g. `+`, `===`)
// Start by taking the longest possible binary operations (3 characters: `===`, `!==`, `>>>`)
// and move down from 3 to 2 to 1 character until a matching binary operation is found
// then, return that binary operation
gobbleBinaryOp = function() {
gobbleSpaces();
var biop, to_check = expr.substr(index, max_binop_len), tc_len = to_check.length;
while(tc_len > 0) {
// Don't accept a binary op when it is an identifier.
// Binary ops that start with a identifier-valid character must be followed
// by a non identifier-part valid character
if(binary_ops.hasOwnProperty(to_check) && (
!isIdentifierStart(exprICode(index)) ||
(index+to_check.length< expr.length && !isIdentifierPart(exprICode(index+to_check.length)))
)) {
index += tc_len;
return to_check;
}
to_check = to_check.substr(0, --tc_len);
}
return false;
},
// This function is responsible for gobbling an individual expression,
// e.g. `1`, `1+2`, `a+(b*2)-Math.sqrt(2)`
gobbleBinaryExpression = function() {
var ch_i, node, biop, prec, stack, biop_info, left, right, i, cur_biop;
// First, try to get the leftmost thing
// Then, check to see if there's a binary operator operating on that leftmost thing
left = gobbleToken();
biop = gobbleBinaryOp();
// If there wasn't a binary operator, just return the leftmost node
if(!biop) {
return left;
}
// Otherwise, we need to start a stack to properly place the binary operations in their
// precedence structure
biop_info = { value: biop, prec: binaryPrecedence(biop)};
right = gobbleToken();
if(!right) {
throwError("Expected expression after " + biop, index);
}
stack = [left, biop_info, right];
// Properly deal with precedence using [recursive descent](http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm)
while((biop = gobbleBinaryOp())) {
prec = binaryPrecedence(biop);
if(prec === 0) {
break;
}
biop_info = { value: biop, prec: prec };
cur_biop = biop;
// Reduce: make a binary expression from the three topmost entries.
while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
right = stack.pop();
biop = stack.pop().value;
left = stack.pop();
node = createBinaryExpression(biop, left, right);
stack.push(node);
}
node = gobbleToken();
if(!node) {
throwError("Expected expression after " + cur_biop, index);
}
stack.push(biop_info, node);
}
i = stack.length - 1;
node = stack[i];
while(i > 1) {
node = createBinaryExpression(stack[i - 1].value, stack[i - 2], node);
i -= 2;
}
return node;
},
// An individual part of a binary expression:
// e.g. `foo.bar(baz)`, `1`, `"abc"`, `(a % 2)` (because it's in parenthesis)
gobbleToken = function() {
var ch, to_check, tc_len;
gobbleSpaces();
ch = exprICode(index);
if(isDecimalDigit(ch) || ch === PERIOD_CODE) {
// Char code 46 is a dot `.` which can start off a numeric literal
return gobbleNumericLiteral();
} else if(ch === SQUOTE_CODE || ch === DQUOTE_CODE) {
// Single or double quotes
return gobbleStringLiteral();
} else if (ch === OBRACK_CODE) {
return gobbleArray();
} else {
to_check = expr.substr(index, max_unop_len);
tc_len = to_check.length;
while(tc_len > 0) {
// Don't accept an unary op when it is an identifier.
// Unary ops that start with a identifier-valid character must be followed
// by a non identifier-part valid character
if(unary_ops.hasOwnProperty(to_check) && (
!isIdentifierStart(exprICode(index)) ||
(index+to_check.length < expr.length && !isIdentifierPart(exprICode(index+to_check.length)))
)) {
index += tc_len;
return {
type: UNARY_EXP,
operator: to_check,
argument: gobbleToken(),
prefix: true
};
}
to_check = to_check.substr(0, --tc_len);
}
if (isIdentifierStart(ch) || ch === OPAREN_CODE) { // open parenthesis
// `foo`, `bar.baz`
return gobbleVariable();
}
}
return false;
},
// Parse simple numeric literals: `12`, `3.4`, `.5`. Do this by using a string to
// keep track of everything in the numeric literal and then calling `parseFloat` on that string
gobbleNumericLiteral = function() {
var number = '', ch, chCode;
while(isDecimalDigit(exprICode(index))) {
number += exprI(index++);
}
if(exprICode(index) === PERIOD_CODE) { // can start with a decimal marker
number += exprI(index++);
while(isDecimalDigit(exprICode(index))) {
number += exprI(index++);
}
}
ch = exprI(index);
if(ch === 'e' || ch === 'E') { // exponent marker
number += exprI(index++);
ch = exprI(index);
if(ch === '+' || ch === '-') { // exponent sign
number += exprI(index++);
}
while(isDecimalDigit(exprICode(index))) { //exponent itself
number += exprI(index++);
}
if(!isDecimalDigit(exprICode(index-1)) ) {
throwError('Expected exponent (' + number + exprI(index) + ')', index);
}
}
chCode = exprICode(index);
// Check to make sure this isn't a variable name that start with a number (123abc)
if(isIdentifierStart(chCode)) {
throwError('Variable names cannot start with a number (' +
number + exprI(index) + ')', index);
} else if(chCode === PERIOD_CODE) {
throwError('Unexpected period', index);
}
return {
type: LITERAL,
value: parseFloat(number),
raw: number
};
},
// Parses a string literal, staring with single or double quotes with basic support for escape codes
// e.g. `"hello world"`, `'this is\nJSEP'`
gobbleStringLiteral = function() {
var str = '', quote = exprI(index++), closed = false, ch;
while(index < length) {
ch = exprI(index++);
if(ch === quote) {
closed = true;
break;
} else if(ch === '\\') {
// Check for all of the common escape codes
ch = exprI(index++);
switch(ch) {
case 'n': str += '\n'; break;
case 'r': str += '\r'; break;
case 't': str += '\t'; break;
case 'b': str += '\b'; break;
case 'f': str += '\f'; break;
case 'v': str += '\x0B'; break;
default : str += ch;
}
} else {
str += ch;
}
}
if(!closed) {
throwError('Unclosed quote after "'+str+'"', index);
}
return {
type: LITERAL,
value: str,
raw: quote + str + quote
};
},
// Gobbles only identifiers
// e.g.: `foo`, `_value`, `$x1`
// Also, this function checks if that identifier is a literal:
// (e.g. `true`, `false`, `null`) or `this`
gobbleIdentifier = function() {
var ch = exprICode(index), start = index, identifier;
if(isIdentifierStart(ch)) {
index++;
} else {
throwError('Unexpected ' + exprI(index), index);
}
while(index < length) {
ch = exprICode(index);
if(isIdentifierPart(ch)) {
index++;
} else {
break;
}
}
identifier = expr.slice(start, index);
if(literals.hasOwnProperty(identifier)) {
return {
type: LITERAL,
value: literals[identifier],
raw: identifier
};
} else if(identifier === this_str) {
return { type: THIS_EXP };
} else {
return {
type: IDENTIFIER,
name: identifier
};
}
},
// Gobbles a list of arguments within the context of a function call
// or array literal. This function also assumes that the opening character
// `(` or `[` has already been gobbled, and gobbles expressions and commas
// until the terminator character `)` or `]` is encountered.
// e.g. `foo(bar, baz)`, `my_func()`, or `[bar, baz]`
gobbleArguments = function(termination) {
var ch_i, args = [], node, closed = false;
while(index < length) {
gobbleSpaces();
ch_i = exprICode(index);
if(ch_i === termination) { // done parsing
closed = true;
index++;
break;
} else if (ch_i === COMMA_CODE) { // between expressions
index++;
} else {
node = gobbleExpression();
if(!node || node.type === COMPOUND) {
throwError('Expected comma', index);
}
args.push(node);
}
}
if (!closed) {
throwError('Expected ' + String.fromCharCode(termination), index);
}
return args;
},
// Gobble a non-literal variable name. This variable name may include properties
// e.g. `foo`, `bar.baz`, `foo['bar'].baz`
// It also gobbles function calls:
// e.g. `Math.acos(obj.angle)`
gobbleVariable = function() {
var ch_i, node;
ch_i = exprICode(index);
if(ch_i === OPAREN_CODE) {
node = gobbleGroup();
} else {
node = gobbleIdentifier();
}
gobbleSpaces();
ch_i = exprICode(index);
while(ch_i === PERIOD_CODE || ch_i === OBRACK_CODE || ch_i === OPAREN_CODE) {
index++;
if(ch_i === PERIOD_CODE) {
gobbleSpaces();
node = {
type: MEMBER_EXP,
computed: false,
object: node,
property: gobbleIdentifier()
};
} else if(ch_i === OBRACK_CODE) {
node = {
type: MEMBER_EXP,
computed: true,
object: node,
property: gobbleExpression()
};
gobbleSpaces();
ch_i = exprICode(index);
if(ch_i !== CBRACK_CODE) {
throwError('Unclosed [', index);
}
index++;
} else if(ch_i === OPAREN_CODE) {
// A function call is being made; gobble all the arguments
node = {
type: CALL_EXP,
'arguments': gobbleArguments(CPAREN_CODE),
callee: node
};
}
gobbleSpaces();
ch_i = exprICode(index);
}
return node;
},
// Responsible for parsing a group of things within parentheses `()`
// This function assumes that it needs to gobble the opening parenthesis
// and then tries to gobble everything within that parenthesis, assuming
// that the next thing it should see is the close parenthesis. If not,
// then the expression probably doesn't have a `)`
gobbleGroup = function() {
index++;
var node = gobbleExpression();
gobbleSpaces();
if(exprICode(index) === CPAREN_CODE) {
index++;
return node;
} else {
throwError('Unclosed (', index);
}
},
// Responsible for parsing Array literals `[1, 2, 3]`
// This function assumes that it needs to gobble the opening bracket
// and then tries to gobble the expressions as arguments.
gobbleArray = function() {
index++;
return {
type: ARRAY_EXP,
elements: gobbleArguments(CBRACK_CODE)
};
},
nodes = [], ch_i, node;
while(index < length) {
ch_i = exprICode(index);
// Expressions can be separated by semicolons, commas, or just inferred without any
// separators
if(ch_i === SEMCOL_CODE || ch_i === COMMA_CODE) {
index++; // ignore separators
} else {
// Try to gobble each expression individually
if((node = gobbleExpression())) {
nodes.push(node);
// If we weren't able to find a binary expression and are out of room, then
// the expression passed in probably has too much
} else if(index < length) {
throwError('Unexpected "' + exprI(index) + '"', index);
}
}
}
// If there's only one expression just try returning the expression
if(nodes.length === 1) {
return nodes[0];
} else {
return {
type: COMPOUND,
body: nodes
};
}
};
// To be filled in by the template
jsep.version = '<%= version %>';
jsep.toString = function() { return 'JavaScript Expression Parser (JSEP) v' + jsep.version; };
/**
* @method jsep.addUnaryOp
* @param {string} op_name The name of the unary op to add
* @return jsep
*/
jsep.addUnaryOp = function(op_name) {
max_unop_len = Math.max(op_name.length, max_unop_len);
unary_ops[op_name] = t; return this;
};
/**
* @method jsep.addBinaryOp
* @param {string} op_name The name of the binary op to add
* @param {number} precedence The precedence of the binary op (can be a float)
* @return jsep
*/
jsep.addBinaryOp = function(op_name, precedence) {
max_binop_len = Math.max(op_name.length, max_binop_len);
binary_ops[op_name] = precedence;
return this;
};
/**
* @method jsep.addLiteral
* @param {string} literal_name The name of the literal to add
* @param {*} literal_value The value of the literal
* @return jsep
*/
jsep.addLiteral = function(literal_name, literal_value) {
literals[literal_name] = literal_value;
return this;
};
/**
* @method jsep.removeUnaryOp
* @param {string} op_name The name of the unary op to remove
* @return jsep
*/
jsep.removeUnaryOp = function(op_name) {
delete unary_ops[op_name];
if(op_name.length === max_unop_len) {
max_unop_len = getMaxKeyLen(unary_ops);
}
return this;
};
/**
* @method jsep.removeAllUnaryOps
* @return jsep
*/
jsep.removeAllUnaryOps = function() {
unary_ops = {};
max_unop_len = 0;
return this;
};
/**
* @method jsep.removeBinaryOp
* @param {string} op_name The name of the binary op to remove
* @return jsep
*/
jsep.removeBinaryOp = function(op_name) {
delete binary_ops[op_name];
if(op_name.length === max_binop_len) {
max_binop_len = getMaxKeyLen(binary_ops);
}
return this;
};
/**
* @method jsep.removeAllBinaryOps
* @return jsep
*/
jsep.removeAllBinaryOps = function() {
binary_ops = {};
max_binop_len = 0;
return this;
};
/**
* @method jsep.removeLiteral
* @param {string} literal_name The name of the literal to remove
* @return jsep
*/
jsep.removeLiteral = function(literal_name) {
delete literals[literal_name];
return this;
};
/**
* @method jsep.removeAllLiterals
* @return jsep
*/
jsep.removeAllLiterals = function() {
literals = {};
return this;
};
// In desktop environments, have a way to restore the old value for `jsep`
if (typeof exports === 'undefined') {
var old_jsep = root.jsep;
// The star of the show! It's a function!
root.jsep = jsep;
// And a courteous function willing to move out of the way for other similarly-named objects!
jsep.noConflict = function() {
if(root.jsep === jsep) {
root.jsep = old_jsep;
}
return jsep;
};
} else {
// In Node.JS environments
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = jsep;
} else {
exports.parse = jsep;
}
}
}(this));
};
BundleModuleCode['logic/prolog']=function (module,exports){
/**
*
* Tau Prolog. A Prolog interpreter in JavaScript.
*
* http://tau-prolog.org/documentation
*
* Copyright (C) 2017 - 2019 José Antonio Riaza Valverde
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
**/
var prolog;
(function() {
// VERSION
var version = { major: 0, minor: 2, patch: 87, status: "beta" };
// IO FILE SYSTEM
function cd(working_directory, path) {
if(path[0] === "/")
working_directory = path;
else
working_directory += working_directory[working_directory.length-1] === "/" ? path : "/" + path;
working_directory = working_directory.replace(/\/\.\//g, "/");
var dirs = working_directory.split("/");
var dirs2 = [];
for(var i = 0; i < dirs.length; i++) {
if(dirs[i] !== "..") {
dirs2.push(dirs[i]);
} else {
if(dirs2.length !== 0)
dirs2.pop();
}
}
return dirs2.join("/").replace(/\/\.$/, "/");
}
// Virtual file
function TauFile(name, type, parent, text) {
text = text === undefined ? "" : text;
this.name = name;
this.type = type;
this.parent = parent;
this.text = text;
this.created = Date.now() / 1000;
this.modified = this.created;
}
TauFile.prototype.get = function(length, position) {
if(position === this.text.length) {
return "end_of_file";
} else if(position > this.text.length) {
return "end_of_file";
} else {
return this.text.substring(position, position+length);
}
};
TauFile.prototype.put = function(text, position) {
if(position === "end_of_file") {
this.text += text;
return true;
} else if(position === "past_end_of_file") {
return null;
} else {
this.text = this.text.substring(0, position) + text + this.text.substring(position+text.length);
return true;
}
};
TauFile.prototype.get_byte = function(position) {
if(position === "end_of_stream")
return -1;
var index = Math.floor(position/2);
if(this.text.length <= index)
return -1;
var code = codePointAt(this.text[Math.floor(position/2)], 0);
if(position % 2 === 0)
return code & 0xff;
else
return code / 256 >>> 0;
};
TauFile.prototype.put_byte = function(byte, position) {
var index = position === "end_of_stream" ? this.text.length : Math.floor(position/2);
if(this.text.length < index)
return null;
var code = this.text.length === index ? -1 : codePointAt(this.text[Math.floor(position/2)], 0);
if(position % 2 === 0) {
code = code / 256 >>> 0;
code = ((code & 0xff) << 8) | (byte & 0xff);
} else {
code = code & 0xff;
code = ((byte & 0xff) << 8) | (code & 0xff);
}
if(this.text.length === index)
this.text += fromCodePoint(code);
else
this.text = this.text.substring(0, index) + fromCodePoint(code) + this.text.substring(index+1);
return true;
};
TauFile.prototype.flush = function() {
return true;
};
TauFile.prototype.close = function() {
this.modified = Date.now() / 1000;
return true;
};
TauFile.prototype.size = function() {
return this.text.length;
};
// Virtual directory
function TauDirectory(name, parent) {
this.name = name;
this.parent = parent;
this.files = {};
this.length = 0;
this.created = Date.now() / 1000;
this.modified = this.created;
}
TauDirectory.prototype.lookup = function(file) {
if(this.files.hasOwnProperty(file))
return this.files[file];
return null;
};
TauDirectory.prototype.push = function(name, file) {
if(!this.files.hasOwnProperty(name))
this.length++;
this.files[name] = file;
this.modified = Date.now() / 1000;
};
TauDirectory.prototype.remove = function(name) {
if(this.files.hasOwnProperty(name)) {
this.length--;
delete this.files[name];
this.modified = Date.now() / 1000;
}
};
TauDirectory.prototype.empty = function() {
return this.length === 0;
};
TauDirectory.prototype.size = function() {
return 4096;
};
// Virtual file system for browser
tau_file_system = {
// Current files
files: new TauDirectory("/", "/", null),
// Open file
open: function(path, type, mode) {
var dirs = path.replace(/\/$/, "").split("/");
var dir = tau_file_system.files;
var name = dirs[dirs.length-1];
for(var i = 1; i < dirs.length-1; i++) {
dir = dir.lookup(dirs[i]);
if(!pl.type.is_directory(dir))
return null;
}
var file = dir.lookup(name);
if(file === null) {
if(mode === "read")
return null;
file = new TauFile(name, type, dir);
dir.push(name, file);
} else if(!pl.type.is_file(file)) {
return null;
}
if(mode === "write")
file.text = "";
return file;
},
// Get item
get: function(path) {
var dirs = path.replace(/\/$/, "").split("/");
var file = tau_file_system.files;
for(var i = 1; i < dirs.length; i++)
if(pl.type.is_directory(file))
file = file.lookup(dirs[i]);
else
return null;
return file;
}
};
// User input for browser
tau_user_input = {
buffer: "",
get: function( length, _ ) {
var text;
while( tau_user_input.buffer.length < length ) {
text = window.prompt();
if( text ) {
tau_user_input.buffer += text;
}
}
text = tau_user_input.buffer.substr( 0, length );
tau_user_input.buffer = tau_user_input.buffer.substr( length );
return text;
}
};
// User output for browser
tau_user_output = {
put: function( text, _ ) {
console.log( text );
return true;
},
flush: function() {
return true;
}
};
// Virtual file system for Node.js
nodejs_file_system = {
// Open file
open: function( path, type, mode ) {
var fs = require('fs');
var fd = fs.openSync( path, mode[0] );
if( mode === "read" && !fs.existsSync( path ) )
return null;
return {
get: function( length, position ) {
var buffer = new Buffer( length );
fs.readSync( fd, buffer, 0, length, position );
return buffer.toString();
},
put: function( text, position ) {
var buffer = Buffer.from( text );
if( position === "end_of_file" )
fs.writeSync( fd, buffer );
else if( position === "past_end_of_file" )
return null;
else
fs.writeSync( fd, buffer, 0, buffer.length, position );
return true;
},
get_byte: function( position ) {
return null;
},
put_byte: function( byte, position ) {
return null;
},
flush: function() {
return true;
},
close: function() {
fs.closeSync( fd );
return true;
}
};
}
};
// User input for Node.js
nodejs_user_input = {
buffer: "",
get: function( length, _ ) {
var text;
var readlineSync = require('readline-sync');
while( nodejs_user_input.buffer.length < length )
nodejs_user_input.buffer += readlineSync.question();
text = nodejs_user_input.buffer.substr( 0, length );
nodejs_user_input.buffer = nodejs_user_input.buffer.substr( length );
return text;
}
};
// User output for Node.js
nodejs_user_output = {
put: function( text, _ ) {
process.stdout.write( text );
return true;
},
flush: function() {
return true;
}
};
// PARSER
var indexOf;
if(!Array.prototype.indexOf) {
indexOf = function(array, elem) {
var len = array.length;
for(var i = 0; i < len; i++) {
if(elem === array[i]) return i;
}
return -1;
};
} else {
indexOf = function(array, elem) {
return array.indexOf(elem);
};
}
var reduce = function(array, fn) {
if(array.length === 0) return undefined;
var elem = array[0];
var len = array.length;
for(var i = 1; i < len; i++) {
elem = fn(elem, array[i]);
}
return elem;
};
var map;
if(!Array.prototype.map) {
map = function(array, fn) {
var a = [];
var len = array.length;
for(var i = 0; i < len; i++) {
a.push( fn(array[i]) );
}
return a;
};
} else {
map = function(array, fn) {
return array.map(fn);
};
}
var filter;
if(!Array.prototype.filter) {
filter = function(array, fn) {
var a = [];
var len = array.length;
for(var i = 0; i < len; i++) {
if(fn(array[i]))
a.push( array[i] );
}
return a;
};
} else {
filter = function(array, fn) {
return array.filter(fn);
};
}
var codePointAt;
if(!String.prototype.codePointAt) {
codePointAt = function(str, i) {
return str.charCodeAt(i);
};
} else {
codePointAt = function(str, i) {
return str.codePointAt(i);
};
}
var fromCodePoint;
if(!String.fromCodePoint) {
fromCodePoint = function() {
return String.fromCharCode.apply(null, arguments);
};
} else {
fromCodePoint = function() {
return String.fromCodePoint.apply(null, arguments);
};
}
var stringLength;
var regexAstralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
if(Array.from)
stringLength = function(str) {
return Array.from(str).length;
};
else
stringLength = function(str) {
return str.replace(regexAstralSymbols, '_').length;
};
var ERROR = 0;
var SUCCESS = 1;
var regex_escape = /(\\a)|(\\b)|(\\f)|(\\n)|(\\r)|(\\t)|(\\v)|\\x([0-9a-fA-F]+)\\|\\([0-7]+)\\|(\\\\)|(\\')|('')|(\\")|(\\`)|(\\.)|(.)/g;
var escape_map = {"\\a": 7, "\\b": 8, "\\f": 12, "\\n": 10, "\\r": 13, "\\t": 9, "\\v": 11};
function escape(str) {
var s = [];
var _error = false;
str.replace(regex_escape, function(match, a, b, f, n, r, t, v, hex, octal, back, single, dsingle, double, backquote, error, char) {
switch(true) {
case hex !== undefined:
s.push( parseInt(hex, 16) );
return "";
case octal !== undefined:
s.push( parseInt(octal, 8) );
return "";
case back !== undefined:
case single !== undefined:
case dsingle !== undefined:
case double !== undefined:
case backquote !== undefined:
s.push( codePointAt(match.substr(1),0) );
return "";
case char !== undefined:
s.push( codePointAt(char,0) );
return "";
case error !== undefined:
_error = true;
default:
s.push(escape_map[match]);
return "";
}
});
if(_error)
return null;
return s;
}
// Escape atoms
function escapeAtom(str, quote) {
var atom = '';
if( str === "\\" ) return null;
if( str.length < 2 ) return str;
try {
str = str.replace(/\\([0-7]+)\\/g, function(match, g1) {
return fromCodePoint(parseInt(g1, 8));
});
str = str.replace(/\\x([0-9a-fA-F]+)\\/g, function(match, g1) {
return fromCodePoint(parseInt(g1, 16));
});
} catch(error) {
return null;
}
for( var i = 0; i < str.length; i++) {
var a = str.charAt(i);
var b = str.charAt(i+1);
if( a === quote && b === quote ) {
i++;
atom += quote;
} else if( a === '\\' ) {
if( ['a','b','f','n','r','t','v',"'",'"','\\','\a','\b','\f','\n','\r','\t','\v'].indexOf(b) !== -1 ) {
i += 1;
switch( b ) {
case 'a': atom += '\a'; break;
case 'b': atom += '\b'; break;
case 'f': atom += '\f'; break;
case 'n': atom += '\n'; break;
case 'r': atom += '\r'; break;
case 't': atom += '\t'; break;
case 'v': atom += '\v'; break;
case "'": atom += "'"; break;
case '"': atom += '"'; break;
case '\\': atom += '\\'; break;
}
} else {
return null;
}
} else {
atom += a;
}
}
return atom;
}
// Redo escape
function redoEscape(str) {
var atom = '';
for( var i = 0; i < str.length; i++) {
switch( str.charAt(i) ) {
case "'": atom += "\\'"; break;
case '\\': atom += '\\\\'; break;
//case '\a': atom += '\\a'; break;
case '\b': atom += '\\b'; break;
case '\f': atom += '\\f'; break;
case '\n': atom += '\\n'; break;
case '\r': atom += '\\r'; break;
case '\t': atom += '\\t'; break;
case '\v': atom += '\\v'; break;
default: atom += str.charAt(i); break;
}
}
return atom;
}
// String to num
function convertNum(num) {
var n = num.substr(2);
switch(num.substr(0,2).toLowerCase()) {
case "0x":
return parseInt(n, 16);
case "0b":
return parseInt(n, 2);
case "0o":
return parseInt(n, 8);
case "0'":
return escape(n)[0];
default:
return parseFloat(num);
}
}
// Regular expressions for tokens
var rules = {
whitespace: /^\s*(?:(?:%.*)|(?:\/\*(?:\n|\r|.)*?\*\/)|(?:\s+))\s*/,
variable: /^(?:[A-Z_][a-zA-Z0-9_]*)/,
atom: /^(\!|,|;|[a-z][0-9a-zA-Z_]*|[#\$\&\*\+\-\.\/\:\<\=\>\?\@\^\~\\]+|'(?:(?:'')|(?:\\')|[^'])*')/,
number: /^(?:0o[0-7]+|0x[0-9a-fA-F]+|0b[01]+|0'(?:''|\\[abfnrtv\\'"`]|\\x?\d+\\|[^\\])|\d+(?:\.\d+(?:[eE][+-]?\d+)?)?)/,
string: /^(?:"([^"]|""|\\")*"|`([^`]|``|\\`)*`)/,
l_brace: /^(?:\[)/,
r_brace: /^(?:\])/,
l_bracket: /^(?:\{)/,
r_bracket: /^(?:\})/,
bar: /^(?:\|)/,
l_paren: /^(?:\()/,
r_paren: /^(?:\))/
};
// Replace chars of char_conversion session
function replace( thread, text ) {
if( thread.get_flag( "char_conversion" ).id === "on" ) {
return text.replace(/./g, function(char) {
return thread.get_char_conversion( char );
});
}
return text;
}
// Tokenize strings
function Tokenizer(thread) {
this.thread = thread;
this.text = ""; // Current text to be analized
this.tokens = []; // Consumed tokens
}
Tokenizer.prototype.set_last_tokens = function(tokens) {
return this.tokens = tokens;
};
Tokenizer.prototype.new_text = function(text) {
this.text = text;
this.tokens = [];
};
Tokenizer.prototype.get_tokens = function(init) {
var text;
var len = 0; // Total length respect to text
var line = 0;
var start = 0;
var tokens = [];
var last_in_blank = false;
if(init) {
var token = this.tokens[init-1];
len = token.len;
text = replace( this.thread, this.text.substr(token.len) );
line = token.line;
start = token.start;
}
else
text = this.text;
// If there is nothing to be analized, return null
if(/^\s*$/.test(text))
return null;
while(text !== "") {
var matches = [];
var last_is_blank = false;
if(/^\n/.exec(text) !== null) {
line++;
start = 0;
len++;
text = text.replace(/\n/, "");
last_in_blank = true;
continue;
}
for(var rule in rules) {
if(rules.hasOwnProperty(rule)) {
var matchs = rules[rule].exec( text );
if(matchs) {
matches.push({
value: matchs[0],
name: rule,
matches: matchs
});
}
}
}
// Lexical error
if(!matches.length)
return this.set_last_tokens( [{ value: text, matches: [], name: "lexical", line: line, start: start }] );
var token = reduce( matches, function(a, b) {
return a.value.length >= b.value.length ? a : b;
} );
token.start = start;
token.line = line;
text = text.replace(token.value, "");
start += token.value.length;
len += token.value.length;
switch(token.name) {
case "atom":
token.raw = token.value;
if(token.value.charAt(0) === "'") {
token.value = escapeAtom( token.value.substr(1, token.value.length - 2), "'" );
if( token.value === null ) {
token.name = "lexical";
token.value = "unknown escape sequence";
}
}
break;
case "number":
token.float = token.value.substring(0,2) !== "0x" && token.value.match(/[.eE]/) !== null && token.value !== "0'.";
token.value = convertNum( token.value );
token.blank = last_is_blank;
break;
case "string":
var del = token.value.charAt(0);
token.value = escapeAtom( token.value.substr(1, token.value.length - 2), del );
if( token.value === null ) {
token.name = "lexical";
token.value = "unknown escape sequence";
}
break;
case "whitespace":
var last = tokens[tokens.length-1];
if(last) last.space = true;
last_is_blank = true;
continue;
case "r_bracket":
if( tokens.length > 0 && tokens[tokens.length-1].name === "l_bracket" ) {
token = tokens.pop();
token.name = "atom";
token.value = "{}";
token.raw = "{}";
token.space = false;
}
break;
case "r_brace":
if( tokens.length > 0 && tokens[tokens.length-1].name === "l_brace" ) {
token = tokens.pop();
token.name = "atom";
token.value = "[]";
token.raw = "[]";
token.space = false;
}
break;
}
token.len = len;
tokens.push( token );
last_is_blank = false;
}
var t = this.set_last_tokens( tokens );
return t.length === 0 ? null : t;
};
// Parse an expression
function parseExpr(thread, tokens, start, priority, toplevel) {
if(!tokens[start]) return {type: ERROR, value: pl.error.syntax(tokens[start-1], "expression expected", true)};
var error;
if(priority === "0") {
var token = tokens[start];
switch(token.name) {
case "number":
return {type: SUCCESS, len: start+1, value: new pl.type.Num(token.value, token.float)};
case "variable":
return {type: SUCCESS, len: start+1, value: new pl.type.Var(token.value)};
case "string":
var str;
switch( thread.get_flag( "double_quotes" ).id ) {
case "atom":;
str = new Term( token.value, [] );
break;
case "codes":
str = new Term( "[]", [] );
for(var i = token.value.length-1; i >= 0; i-- )
str = new Term( ".", [new pl.type.Num( codePointAt(token.value,i), false ), str] );
break;
case "chars":
str = new Term( "[]", [] );
for(var i = token.value.length-1; i >= 0; i-- )
str = new Term( ".", [new pl.type.Term( token.value.charAt(i), [] ), str] );
break;
}
return {type: SUCCESS, len: start+1, value: str};
case "l_paren":
var expr = parseExpr(thread, tokens, start+1, thread.__get_max_priority(), true);
if(expr.type !== SUCCESS) return expr;
if(tokens[expr.len] && tokens[expr.len].name === "r_paren") {
expr.len++;
return expr;
}
return {type: ERROR, derived: true, value: pl.error.syntax(tokens[expr.len] ? tokens[expr.len] : tokens[expr.len-1], ") or operator expected", !tokens[expr.len])}
case "l_bracket":
var expr = parseExpr(thread, tokens, start+1, thread.__get_max_priority(), true);
if(expr.type !== SUCCESS) return expr;
if(tokens[expr.len] && tokens[expr.len].name === "r_bracket") {
expr.len++;
expr.value = new Term( "{}", [expr.value] );
return expr;
}
return {type: ERROR, derived: true, value: pl.error.syntax(tokens[expr.len] ? tokens[expr.len] : tokens[expr.len-1], "} or operator expected", !tokens[expr.len])}
}
// Compound term
var result = parseTerm(thread, tokens, start, toplevel);
if(result.type === SUCCESS || result.derived)
return result;
// List
result = parseList(thread, tokens, start);
if(result.type === SUCCESS || result.derived)
return result;
// Unexpected
return {type: ERROR, derived: false, value: pl.error.syntax(tokens[start], "unexpected token")};
}
var max_priority = thread.__get_max_priority();
var next_priority = thread.__get_next_priority(priority);
var aux_start = start;
// Prefix operators
if(tokens[start].name === "atom" && tokens[start+1] && (tokens[start].space || tokens[start+1].name !== "l_paren")) {
var token = tokens[start++];
var classes = thread.__lookup_operator_classes(priority, token.value);
// Associative prefix operator
if(classes && classes.indexOf("fy") > -1) {
var expr = parseExpr(thread, tokens, start, priority, toplevel);
if(expr.type !== ERROR) {
if( token.value === "-" && !token.space && pl.type.is_number( expr.value ) ) {
return {
value: new pl.type.Num(-expr.value.value, expr.value.is_float),
len: expr.len,
type: SUCCESS
};
} else {
return {
value: new pl.type.Term(token.value, [expr.value]),
len: expr.len,
type: SUCCESS
};
}
} else {
error = expr;
}
// Non-associative prefix operator
} else if(classes && classes.indexOf("fx") > -1) {
var expr = parseExpr(thread, tokens, start, next_priority, toplevel);
if(expr.type !== ERROR) {
return {
value: new pl.type.Term(token.value, [expr.value]),
len: expr.len,
type: SUCCESS
};
} else {
error = expr;
}
}
}
start = aux_start;
var expr = parseExpr(thread, tokens, start, next_priority, toplevel);
if(expr.type === SUCCESS) {
start = expr.len;
var token = tokens[start];
if(tokens[start] && (
tokens[start].name === "atom" && thread.__lookup_operator_classes(priority, token.value) ||
tokens[start].name === "bar" && thread.__lookup_operator_classes(priority, "|")
) ) {
var next_priority_lt = next_priority;
var next_priority_eq = priority;
var classes = thread.__lookup_operator_classes(priority, token.value);
if(classes.indexOf("xf") > -1) {
return {
value: new pl.type.Term(token.value, [expr.value]),
len: ++expr.len,
type: SUCCESS
};
} else if(classes.indexOf("xfx") > -1) {
var expr2 = parseExpr(thread, tokens, start + 1, next_priority_lt, toplevel);
if(expr2.type === SUCCESS) {
return {
value: new pl.type.Term(token.value, [expr.value, expr2.value]),
len: expr2.len,
type: SUCCESS
};
} else {
expr2.derived = true;
return expr2;
}
} else if(classes.indexOf("xfy") > -1) {
var expr2 = parseExpr(thread, tokens, start + 1, next_priority_eq, toplevel);
if(expr2.type === SUCCESS) {
return {
value: new pl.type.Term(token.value, [expr.value, expr2.value]),
len: expr2.len,
type: SUCCESS
};
} else {
expr2.derived = true;
return expr2;
}
} else if(expr.type !== ERROR) {
while(true) {
start = expr.len;
var token = tokens[start];
if(token && token.name === "atom" && thread.__lookup_operator_classes(priority, token.value)) {
var classes = thread.__lookup_operator_classes(priority, token.value);
if( classes.indexOf("yf") > -1 ) {
expr = {
value: new pl.type.Term(token.value, [expr.value]),
len: ++start,
type: SUCCESS
};
} else if( classes.indexOf("yfx") > -1 ) {
var expr2 = parseExpr(thread, tokens, ++start, next_priority_lt, toplevel);
if(expr2.type === ERROR) {
expr2.derived = true;
return expr2;
}
start = expr2.len;
expr = {
value: new pl.type.Term(token.value, [expr.value, expr2.value]),
len: start,
type: SUCCESS
};
} else { break; }
} else { break; }
}
}
} else {
error = {type: ERROR, value: pl.error.syntax(tokens[expr.len-1], "operator expected")};
}
return expr;
}
return expr;
}
// Parse a compound term
function parseTerm(thread, tokens, start, toplevel) {
if(!tokens[start] || (tokens[start].name === "atom" && tokens[start].raw === "." && !toplevel && (tokens[start].space || !tokens[start+1] || tokens[start+1].name !== "l_paren")))
return {type: ERROR, derived: false, value: pl.error.syntax(tokens[start-1], "unfounded token")};
var atom = tokens[start];
var exprs = [];
if(tokens[start].name === "atom" && tokens[start].raw !== ",") {
start++;
if(tokens[start-1].space) return {type: SUCCESS, len: start, value: new pl.type.Term(atom.value, exprs)};
if(tokens[start] && tokens[start].name === "l_paren") {
if(tokens[start+1] && tokens[start+1].name === "r_paren")
return {type: ERROR, derived: true, value: pl.error.syntax(tokens[start+1], "argument expected")};
var expr = parseExpr(thread, tokens, ++start, "999", true);
if(expr.type === ERROR) {
if( expr.derived )
return expr;
else
return {type: ERROR, derived: true, value: pl.error.syntax(tokens[start] ? tokens[start] : tokens[start-1], "argument expected", !tokens[start])};
}
exprs.push(expr.value);
start = expr.len;
while(tokens[start] && tokens[start].name === "atom" && tokens[start].value === ",") {
expr = parseExpr(thread, tokens, start+1, "999", true);
if(expr.type === ERROR) {
if( expr.derived )
return expr;
else
return {type: ERROR, derived: true, value: pl.error.syntax(tokens[start+1] ? tokens[start+1] : tokens[start], "argument expected", !tokens[start+1])};
}
exprs.push(expr.value);
start = expr.len;
}
if(tokens[start] && tokens[start].name === "r_paren") start++;
else return {type: ERROR, derived: true, value: pl.error.syntax(tokens[start] ? tokens[start] : tokens[start-1], ", or ) expected", !tokens[start])};
}
return {type: SUCCESS, len: start, value: new pl.type.Term(atom.value, exprs)};
}
return {type: ERROR, derived: false, value: pl.error.syntax(tokens[start], "term expected")};
}
// Parse a list
function parseList(thread, tokens, start) {
if(!tokens[start])
return {type: ERROR, derived: false, value: pl.error.syntax(tokens[start-1], "[ expected")};
if(tokens[start] && tokens[start].name === "l_brace") {
var expr = parseExpr(thread, tokens, ++start, "999", true);
var exprs = [expr.value];
var cons = undefined;
if(expr.type === ERROR) {
if(tokens[start] && tokens[start].name === "r_brace") {
return {type: SUCCESS, len: start+1, value: new pl.type.Term("[]", [])};
}
return {type: ERROR, derived: true, value: pl.error.syntax(tokens[start], "] expected")};
}
start = expr.len;
while(tokens[start] && tokens[start].name === "atom" && tokens[start].value === ",") {
expr = parseExpr(thread, tokens, start+1, "999", true);
if(expr.type === ERROR) {
if( expr.derived )
return expr;
else
return {type: ERROR, derived: true, value: pl.error.syntax(tokens[start+1] ? tokens[start+1] : tokens[start], "argument expected", !tokens[start+1])};
}
exprs.push(expr.value);
start = expr.len;
}
var bar = false
if(tokens[start] && tokens[start].name === "bar") {
bar = true;
expr = parseExpr(thread, tokens, start+1, "999", true);
if(expr.type === ERROR) {
if( expr.derived )
return expr;
else
return {type: ERROR, derived: true, value: pl.error.syntax(tokens[start+1] ? tokens[start+1] : tokens[start], "argument expected", !tokens[start+1])};
}
cons = expr.value;
start = expr.len;
}
if(tokens[start] && tokens[start].name === "r_brace")
return {type: SUCCESS, len: start+1, value: arrayToList(exprs, cons) };
else
return {type: ERROR, derived: true, value: pl.error.syntax(tokens[start] ? tokens[start] : tokens[start-1], bar ? "] expected" : ", or | or ] expected", !tokens[start])};
}
return {type: ERROR, derived: false, value: pl.error.syntax(tokens[start], "list expected")};
}
// Parse a rule
function parseRule(thread, tokens, start) {
var line = tokens[start].line;
var expr = parseExpr(thread, tokens, start, thread.__get_max_priority(), false);
var rule = null;
var obj;
if(expr.type !== ERROR) {
start = expr.len;
if(tokens[start] && tokens[start].name === "atom" && tokens[start].raw === ".") {
start++;
if( pl.type.is_term(expr.value) ) {
if(expr.value.indicator === ":-/2") {
rule = new pl.type.Rule(expr.value.args[0], body_conversion(expr.value.args[1]))
obj = {
value: rule,
len: start,
type: SUCCESS
};
} else if(expr.value.indicator === "-->/2") {
rule = rule_to_dcg(new pl.type.Rule(expr.value.args[0], expr.value.args[1]), thread);
if(!pl.type.is_rule(rule))
return {
value: rule,
len: start,
type: ERROR
};
rule.body = body_conversion( rule.body );
obj = {
value: rule,
len: start,
type: pl.type.is_rule( rule ) ? SUCCESS : ERROR
};
} else {
rule = new pl.type.Rule(expr.value, null);
obj = {
value: rule,
len: start,
type: SUCCESS
};
}
if( rule ) {
var singleton = rule.singleton_variables();
if( singleton.length > 0 )
thread.throw_warning( pl.warning.singleton( singleton, rule.head.indicator, line ) );
}
return obj;
} else {
return { type: ERROR, value: pl.error.syntax(tokens[start], "callable expected") };
}
} else {
return { type: ERROR, value: pl.error.syntax(tokens[start] ? tokens[start] : tokens[start-1], ". or operator expected") };
}
}
return expr;
}
// Parse a program
function parseProgram(thread, string, options) {
options = options ? options : {};
options.from = options.from ? options.from : "$tau-js";
options.reconsult = options.reconsult !== undefined ? options.reconsult : true;
var tokenizer = new Tokenizer( thread );
var reconsulted = {};
var indicator;
tokenizer.new_text( string );
var n = 0;
var tokens = tokenizer.get_tokens( n );
while( tokens !== null && tokens[n] ) {
var expr = parseRule(thread, tokens, n);
if( expr.type === ERROR ) {
return new Term("throw", [expr.value]);
} else {
// Term expansion
var term_expansion = thread.session.rules["term_expansion/2"];
if(term_expansion && term_expansion.length > 0) {
var n_thread = new Thread( thread.session );
var term = expr.value.body ? new Term(":-", [expr.value.head, expr.value.body]) : expr.value.head;
term = term.rename( thread.session );
n_thread.query("term_expansion(" + term.toString() + ", X).");
n_thread.answer(function(answer) {
if(answer && !pl.type.is_error(answer) && pl.type.is_term(answer.links['X'])) {
var term = answer.links['X'];
var rule = term.indicator === ":-/2" ? new Rule(term.args[0], term.args[1]) : new Rule( term, null ) ;
parseProgramExpansion(thread, options, reconsulted, {value: rule, len: expr.len, type: expr.type});
} else {
parseProgramExpansion(thread, options, reconsulted, expr);
}
});
} else {
parseProgramExpansion(thread, options, reconsulted, expr);
}
n = expr.len;
if(expr.value.body === null && expr.value.head.indicator === ":-/1" &&
expr.value.head.args[0].indicator === "char_conversion/2") {
tokens = tokenizer.get_tokens( n );
n = 0;
}
}
}
return true;
}
function parseGoalExpansion(thread, head, term, set, origin) {
var n_thread = new Thread( thread.session );
n_thread.__goal_expansion = true;
var varterm = thread.next_free_variable();
var varhead = thread.next_free_variable();
var goal = varhead + " = " + head + ", goal_expansion(" + term + ", " + varterm + ").";
n_thread.query(goal);
n_thread.answer(function(answer) {
if(answer && !pl.type.is_error(answer) && answer.links[varterm]) {
set(answer.links[varhead], body_conversion(answer.links[varterm]));
parseGoalExpansion(thread, origin.head(), origin.term(), origin.set, origin);
}
});
}
function parseQueryExpansion(thread, term) {
var n_thread = new Thread( thread.session );
n_thread.__goal_expansion = true;
var varterm = thread.next_free_variable();
var goal = "goal_expansion(" + term + ", " + varterm + ").";
n_thread.query(goal);
var variables = n_thread.head_point().substitution.domain();
n_thread.answer(function(answer) {
if(answer && !pl.type.is_error(answer) && answer.links[varterm]) {
for(var i = 0; i < variables.length; i++) {
if(variables[i] !== varterm.id && answer.links[variables[i]]) {
var subs = new Substitution();
subs.links[answer.links[variables[i]]] = variables[i];
answer.links[varterm] = answer.links[varterm].apply( subs );
}
}
parseQueryExpansion(thread, body_conversion(answer.links[varterm]));
} else {
thread.add_goal(term);
}
});
}
function parseProgramExpansion(thread, options, reconsulted, expr) {
var exprs = [];
if(pl.type.is_instantiated_list(expr.value.head) && expr.value.body === null) {
var pointer = expr.value.head;
while(pointer.indicator === "./2") {
var rule = pointer.args[0];
if(rule.indicator === ":-/2")
exprs.push(new Rule(rule.args[0], rule.args[1]));
else
exprs.push(new Rule(rule, null));
pointer = pointer.args[1];
}
} else {
exprs.push(expr.value);
}
for(var i = 0; i < exprs.length; i++) {
expr.value = exprs[i];
if(expr.value.body === null && expr.value.head.indicator === "?-/1") {
var n_thread = new Thread( thread.session );
n_thread.add_goal( expr.value.head.args[0] );
n_thread.answer( function( answer ) {
if( pl.type.is_error( answer ) ) {
thread.throw_warning( answer.args[0] );
} else if( answer === false || answer === null ) {
thread.throw_warning( pl.warning.failed_goal( expr.value.head.args[0], expr.len ) );
}
} );
} else if(expr.value.body === null && expr.value.head.indicator === ":-/1") {
thread.run_directive(expr.value.head.args[0]);
} else {
indicator = expr.value.head.indicator;
if( options.reconsult !== false && reconsulted[indicator] !== true && !thread.is_multifile_predicate( indicator ) ) {
thread.session.rules[indicator] = filter( thread.session.rules[indicator] || [], function( rule ) { return rule.dynamic; } );
reconsulted[indicator] = true;
}
var goal_expansion = thread.session.rules["goal_expansion/2"];
if(expr.value.body !== null && goal_expansion && goal_expansion.length > 0) {
thread.renamed_variables = {};
var origin = {
head: function() { return expr.value.head; },
term: function() { return expr.value.body; },
set: function(h, p){
expr.value.head = h;
expr.value.body = p;
}
};
parseGoalExpansion(thread, expr.value.head, body_conversion(expr.value.body), origin.set, origin);
}
thread.add_rule(expr.value, options);
}
}
}
// Parse a query
function parseQuery(thread, string) {
var tokenizer = new Tokenizer( thread );
tokenizer.new_text( string );
var n = 0;
do {
var tokens = tokenizer.get_tokens( n );
if( tokens === null ) break;
var expr = parseExpr(thread, tokens, 0, thread.__get_max_priority(), false);
if(expr.type !== ERROR) {
var expr_position = expr.len;
var tokens_pos = expr_position;
if(tokens[expr_position] && tokens[expr_position].name === "atom" && tokens[expr_position].raw === ".") {
expr.value = body_conversion(expr.value);
// Goal expansion
var goal_expansion = thread.session.rules["goal_expansion/2"];
if(!thread.__goal_expansion && goal_expansion && goal_expansion.length > 0) {
parseQueryExpansion(thread, expr.value);
} else {
thread.add_goal( expr.value );
}
} else {
var token = tokens[expr_position];
return new Term("throw", [pl.error.syntax(token ? token : tokens[expr_position-1], ". or operator expected", !token)] );
}
n = expr.len + 1;
} else {
return new Term("throw", [expr.value]);
}
} while( true );
return true;
}
// UTILS
// Rule to DCG
function rule_to_dcg(rule, thread) {
rule = rule.rename( thread );
var begin = thread.next_free_variable();
var dcg = body_to_dcg( rule.body, begin, thread );
if( dcg.error )
return dcg.value;
rule.body = dcg.value;
// push-back lists
if(rule.head.indicator === ",/2") {
var terminals = rule.head.args[1];
rule.head = rule.head.args[0];
var last = thread.next_free_variable();
var pointer = terminals;
if(!pl.type.is_list(pointer)) {
return pl.error.type("list", pointer, "DCG/0");
}
if(pointer.indicator === "[]/0") {
terminals = dcg.variable;
} else {
while(pointer.indicator === "./2" && pl.type.is_list(pointer) && pointer.args[1].indicator !== "[]/0") {
pointer = pointer.args[1];
}
if(pl.type.is_variable(pointer))
return pl.error.instantiation("DCG/0");
else if(!pl.type.is_list(pointer))
return pl.error.type("list", terminals, "DCG/0");
pointer.args[1] = dcg.variable;
}
rule.body = new Term(",", [rule.body, new Term("=", [last, terminals])]);
rule.head = new Term(rule.head.id, rule.head.args.concat([begin, last]));
} else {
// replace first assignment
var first_assign = rule.body;
if(pl.type.is_term(first_assign) && first_assign.indicator === ",/2")
first_assign = first_assign.args[0];
if(pl.type.is_term(first_assign) && first_assign.indicator === "=/2" &&
pl.type.is_variable(first_assign.args[0]) && first_assign.args[0] === begin) {
begin = first_assign.args[1];
rule.body = rule.body.replace(null);
}
// add last variable
rule.head = new Term(rule.head.id, rule.head.args.concat([begin, dcg.variable]));
}
return rule;
}
// Body to DCG
function body_to_dcg(expr, last, thread) {
var free;
if( pl.type.is_term( expr ) && expr.indicator === "!/0" ) {
free = thread.next_free_variable();
return {
value: new Term(",", [expr, new Term("=", [last, free])]),
variable: free,
error: false
};
} else if( pl.type.is_term( expr ) && expr.indicator === "\\+/1" ) {
var left = body_to_dcg(expr.args[0], last, thread);
if( left.error ) return left;
return {
value: new Term(expr.id, [left.value]),
variable: last,
error: false
};
} else if( pl.type.is_term( expr ) && (expr.indicator === ",/2" || expr.indicator === "->/2") ) {
var left = body_to_dcg(expr.args[0], last, thread);
if( left.error ) return left;
var right = body_to_dcg(expr.args[1], left.variable, thread);
if( right.error ) return right;
return {
value: new Term(expr.id, [left.value, right.value]),
variable: right.variable,
error: false
};
} else if( pl.type.is_term( expr ) && expr.indicator === ";/2" ) {
var left = body_to_dcg(expr.args[0], last, thread);
if( left.error ) return left;
var right = body_to_dcg(expr.args[1], last, thread);
if( right.error ) return right;
return {
value: new Term(",", [new Term(";", [left.value, right.value]), new Term("=", [left.variable, right.variable])]),
variable: right.variable,
error: false
};
} else if( pl.type.is_term( expr ) && expr.indicator === "{}/1" ) {
free = thread.next_free_variable();
return {
value: new Term(",", [expr.args[0], new Term("=", [last, free])]),
variable: free,
error: false
};
} else if( pl.type.is_empty_list( expr ) ) {
return {
value: new Term("true", []),
variable: last,
error: false
};
} else if( pl.type.is_list( expr ) ) {
free = thread.next_free_variable();
var pointer = expr;
var prev;
while( pointer.indicator === "./2" ) {
prev = pointer;
pointer = pointer.args[1];
}
if( pl.type.is_variable( pointer ) ) {
return {
value: pl.error.instantiation("DCG/0"),
variable: last,
error: true
};
} else if( !pl.type.is_empty_list( pointer ) ) {
return {
value: pl.error.type("list", expr, "DCG/0"),
variable: last,
error: true
};
} else {
prev.args[1] = free;
return {
value: new Term("=", [last, expr]),
variable: free,
error: false
};
}
} else if( pl.type.is_callable( expr ) ) {
free = thread.next_free_variable();
expr = new Term( expr.id, expr.args.concat([last,free]) );
return {
value: expr,
variable: free,
error: false
};
} else {
return {
value: pl.error.type( "callable", expr, "DCG/0" ),
variable: last,
error: true
};
}
}
// Body conversion
function body_conversion( expr ) {
if( pl.type.is_variable( expr ) )
return new Term( "call", [expr] );
else if( pl.type.is_term( expr ) && [",/2", ";/2", "->/2"].indexOf(expr.indicator) !== -1 )
return new Term( expr.id, [body_conversion( expr.args[0] ), body_conversion( expr.args[1] )] );
return expr;
}
// List to Prolog list
function arrayToList( array, cons ) {
var list = cons ? cons : new Term( "[]", [] );
for(var i = array.length-1; i >= 0; i-- )
list = new Term( ".", [array[i], list] );
return list;
}
// Remove element from array
function remove( array, element ) {
for( var i = array.length - 1; i >= 0; i-- ) {
if( array[i] === element ) {
array.splice(i, 1);
}
}
}
// Remove duplicate elements
function nub( array ) {
var seen = {};
var unique = [];
for( var i = 0; i < array.length; i++ ) {
if( !(array[i] in seen) ) {
unique.push( array[i] );
seen[array[i]] = true;
}
}
return unique;
}
// Retract a rule
function retract( thread, point, indicator, rule ) {
if( thread.session.rules[indicator] !== null ) {
for( var i = 0; i < thread.session.rules[indicator].length; i++ ) {
if( thread.session.rules[indicator][i] === rule ) {
thread.session.rules[indicator].splice( i, 1 );
thread.success( point );
break;
}
}
}
}
// call/n
function callN( n ) {
return function ( thread, point, atom ) {
var closure = atom.args[0], args = atom.args.slice(1, n);
if( pl.type.is_variable( closure ) ) {
thread.throw_error( pl.error.instantiation( thread.level ) );
} else if( !pl.type.is_callable( closure ) ) {
thread.throw_error( pl.error.type( "callable", closure, thread.level ) );
} else {
var goal = new Term( closure.id, closure.args.concat( args ) );
thread.prepend( [new State( point.goal.replace( goal ), point.substitution, point )] );
}
};
}
// String to indicator
function str_indicator( str ) {
for( var i = str.length - 1; i >= 0; i-- )
if( str.charAt(i) === "/" )
return new Term( "/", [new Term( str.substring(0, i) ), new Num( parseInt(str.substring(i+1)), false )] );
}
// PROLOG OBJECTS
// Variables
function Var( id ) {
this.id = id;
}
// Numbers
function Num( value, is_float ) {
this.is_float = is_float !== undefined ? is_float : parseInt( value ) !== value;
this.value = this.is_float ? value : parseInt( value );
}
// Terms
var term_ref = 0;
function Term( id, args, ref ) {
term_ref++;
this.ref = ref || term_ref;
this.id = id;
this.args = args || [];
this.indicator = id + "/" + this.args.length;
}
// Streams
var stream_ref = 0;
function Stream( stream, mode, alias, type, reposition, eof_action ) {
this.id = stream_ref++;
this.stream = stream;
this.mode = mode; // "read" or "write" or "append"
this.alias = alias;
this.type = type !== undefined ? type : "text"; // "text" or "binary"
this.reposition = reposition !== undefined ? reposition : true; // true or false
this.eof_action = eof_action !== undefined ? eof_action : "eof_code"; // "error" or "eof_code" or "reset"
this.position = this.mode === "append" ? "end_of_stream" : 0;
this.output = this.mode === "write" || this.mode === "append";
this.input = this.mode === "read";
}
// Substitutions
function Substitution( links, attrs ) {
links = links || {};
attrs = attrs || {};
this.links = links;
this.attrs = attrs;
}
// States
function State( goal, subs, parent ) {
subs = subs || new Substitution();
parent = parent || null;
this.goal = goal;
this.substitution = subs;
this.parent = parent;
}
// Rules
function Rule( head, body, dynamic ) {
this.head = head;
this.body = body;
this.dynamic = dynamic ? dynamic : false;
}
// Session
// output: user text output function
function Session( limit, output ) {
var self=this;
limit = limit === undefined || limit <= 0 ? 1000 : limit;
this.rules = {};
this.src_predicates = {};
this.rename = 0;
this.modules = [];
this.total_threads = 1;
this.renamed_variables = {};
this.public_predicates = {};
this.multifile_predicates = {};
this.limit = limit;
this.streams = {
"user_input": new Stream(
nodejs_flag ? nodejs_user_input : tau_user_input,
"read", "user_input", "text", false, "reset" ),
"user_output": new Stream(
nodejs_flag ? nodejs_user_output : output?{
put: function (text, arg) { output(text); return true; },
flush : function () { return true },
}: tau_user_output,
"write", "user_output", "text", false, "eof_code" )
};
this.file_system = nodejs_flag ? nodejs_file_system : tau_file_system;
this.standard_input = this.streams["user_input"];
this.standard_output = this.streams["user_output"];
this.current_input = this.streams["user_input"];
this.current_output = this.streams["user_output"];
this.working_directory = "/"; // only for browser
this.format_success = function( state ) { return state.substitution; };
this.format_error = function( state ) { return state.goal; };
this.flag = {
bounded: pl.flag.bounded.value,
max_integer: pl.flag.max_integer.value,
min_integer: pl.flag.min_integer.value,
integer_rounding_function: pl.flag.integer_rounding_function.value,
char_conversion: pl.flag.char_conversion.value,
debug: pl.flag.debug.value,
max_arity: pl.flag.max_arity.value,
unknown: pl.flag.unknown.value,
double_quotes: pl.flag.double_quotes.value,
occurs_check: pl.flag.occurs_check.value,
dialect: pl.flag.dialect.value,
version_data: pl.flag.version_data.value,
nodejs: pl.flag.nodejs.value,
argv: pl.flag.argv.value
};
this.__loaded_modules = [];
this.__char_conversion = {};
this.__operators = {
1200: { ":-": ["fx", "xfx"], "-->": ["xfx"], "?-": ["fx"] },
1100: { ";": ["xfy"] },
1050: { "->": ["xfy"] },
1000: { ",": ["xfy"] },
900: { "\\+": ["fy"] },
700: {
"=": ["xfx"], "\\=": ["xfx"], "==": ["xfx"], "\\==": ["xfx"],
"@<": ["xfx"], "@=<": ["xfx"], "@>": ["xfx"], "@>=": ["xfx"],
"=..": ["xfx"], "is": ["xfx"], "=:=": ["xfx"], "=\\=": ["xfx"],
"<": ["xfx"], "=<": ["xfx"], ">": ["xfx"], ">=": ["xfx"]
},
600: { ":": ["xfy"] },
500: { "+": ["yfx"], "-": ["yfx"], "/\\": ["yfx"], "\\/": ["yfx"] },
400: {
"*": ["yfx"], "/": ["yfx"], "//": ["yfx"], "rem": ["yfx"],
"mod": ["yfx"], "<<": ["yfx"], ">>": ["yfx"], "div": ["yfx"]
},
200: { "**": ["xfx"], "^": ["xfy"], "-": ["fy"], "+": ["fy"], "\\": ["fy"] }
};
this.thread = new Thread( this );
}
// Threads
function Thread( session ) {
this.epoch = Date.now();
this.session = session;
this.session.total_threads++;
this.format_success = session.format_success;
this.format_error = session.format_error;
this.total_steps = 0;
this.cpu_time = 0;
this.cpu_time_last = 0;
this.points = [];
this.debugger = false;
this.debugger_states = [];
this.level = "top_level/0";
this.current_limit = this.session.limit;
this.warnings = [];
this.__calls = [];
this.__goal_expansion = false;
}
// Modules
function Module( id, rules, exports ) {
this.id = id;
this.rules = rules;
this.exports = exports;
pl.module[id] = this;
}
Module.prototype.exports_predicate = function( indicator ) {
return this.exports.indexOf( indicator ) !== -1;
};
// UNIFY PROLOG OBJECTS
// Variables
Var.prototype.unify = function( obj, occurs_check ) {
if( occurs_check && indexOf( obj.variables(), this.id ) !== -1 && !pl.type.is_variable( obj ) ) {
return null;
}
var links = {};
links[this.id] = obj;
return new Substitution( links );
};
// Numbers
Num.prototype.unify = function( obj, _ ) {
if( pl.type.is_number( obj ) && this.value === obj.value && this.is_float === obj.is_float ) {
return new Substitution();
}
return null;
};
// Terms
Term.prototype.unify = function( obj, occurs_check ) {
if( pl.type.is_term( obj ) && this.indicator === obj.indicator ) {
var subs = new Substitution();
for( var i = 0; i < this.args.length; i++ ) {
var mgu = pl.unify( this.args[i].apply( subs ), obj.args[i].apply( subs ), occurs_check );
if( mgu === null )
return null;
for( var x in mgu.links )
subs.links[x] = mgu.links[x];
subs = subs.apply( mgu );
}
return subs;
}
return null;
};
// Streams
Stream.prototype.unify = function( obj, occurs_check ) {
if( pl.type.is_stream( obj ) && this.id === obj.id ) {
return new Substitution();
}
return null;
};
// PROLOG OBJECTS TO STRING
// Variables
Var.prototype.toString = function( _ ) {
return this.id;
};
// Numbers
Num.prototype.toString = function( _ ) {
var str = this.value.toString();
var e = str.indexOf("e");
if(e !== -1) {
if(str.indexOf(".") !== -1)
return str
else
return str.replace("e", ".0e");
}
return this.is_float && indexOf(str, ".") === -1 ? this.value + ".0" : str;
};
// Terms
Term.prototype.toString = function( options, priority, from ) {
options = !options ? {} : options;
options.quoted = options.quoted === undefined ? true: options.quoted;
options.ignore_ops = options.ignore_ops === undefined ? false : options.ignore_ops;
options.numbervars = options.numbervars === undefined ? false : options.numbervars;
priority = priority === undefined ? {priority: 999, class: "", indicator: ""} : priority;
from = from === undefined ? "" : from;
if( options.numbervars && this.indicator === "$VAR/1" && pl.type.is_integer( this.args[0] ) && this.args[0].value >= 0 ) {
var i = this.args[0].value;
var number = Math.floor( i/26 );
var letter = i % 26;
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[letter] + (number !== 0 ? number : "");
}
switch( this.indicator ){
case "[]/0":
case "{}/0":
case "!/0":
return this.id;
case "{}/1":
return "{" + this.args[0].toString( options ) + "}";
case "./2":
if( options.ignore_ops === false ) {
var list = "[" + this.args[0].toString( options );
var pointer = this.args[1];
while( pointer.indicator === "./2" ) {
list += ", " + pointer.args[0].toString( options );
pointer = pointer.args[1];
}
if( pointer.indicator !== "[]/0" ) {
list += "|" + pointer.toString( options );
}
list += "]";
return list;
}
default:
var id = this.id;
var operator = options.session ? options.session.lookup_operator( this.id, this.args.length ) : null;
if( options.session === undefined || options.ignore_ops || operator === null ) {
if( options.quoted && ! /^(!|;|[a-z][0-9a-zA-Z_]*|[#\$\&\*\+\-\.\/\:\<\=\>\?\@\^\~\\]+)$/.test( id ) && id !== "{}" && id !== "[]" )
id = "'" + redoEscape(id) + "'";
return id + (this.args.length ? "(" + map( this.args,
function(x) { return x.toString( options); }
).join(", ") + ")" : "");
} else {
var priority_op = parseInt(operator.priority);
var priority_arg = parseInt(priority.priority);
var cond = priority_op > priority_arg || priority_op === priority_arg && (
operator.class === "xfx" ||
operator.class === "xfy" && this.indicator !== priority.indicator ||
operator.class === "yfx" && this.indicator !== priority.indicator ||
this.indicator === priority.indicator && operator.class === "yfx" && from === "right" ||
this.indicator === priority.indicator && operator.class === "xfy" && from === "left");
operator.indicator = this.indicator;
var lpar = cond ? "(" : "";
var rpar = cond ? ")" : "";
var space = /^[a-z][0-9a-zA-Z_]*$/.test( id ) ? " " : "";
if( this.args.length === 0 ) {
return "(" + this.id + ")";
} else if( ["fy","fx"].indexOf( operator.class) !== -1 ) {
return lpar + id + space + this.args[0].toString( options, operator ) + rpar;
} else if( ["yf","xf"].indexOf( operator.class) !== -1 ) {
return lpar + this.args[0].toString( options, operator ) + space + id + rpar;
} else {
return lpar + this.args[0].toString( options, operator, "left" ) + space + this.id + space + this.args[1].toString( options, operator, "right" ) + rpar;
}
}
}
};
// Streams
Stream.prototype.toString = function( _ ) {
return "<stream>(" + this.id + ")";
};
// Substitutions
Substitution.prototype.toString = function( options ) {
var str = "{";
for( var link in this.links ) {
if(!this.links.hasOwnProperty(link)) continue;
if( str !== "{" ) {
str += ", ";
}
str += link + "/" + this.links[link].toString( options );
}
str += "}";
return str;
};
// States
State.prototype.toString = function( options ) {
if( this.goal === null ) {
return "<" + this.substitution.toString( options ) + ">";
} else {
return "<" + this.goal.toString( options ) + ", " + this.substitution.toString( options ) + ">";
}
};
// Rules
Rule.prototype.toString = function( options ) {
if( !this.body ) {
return this.head.toString( options ) + ".";
} else {
return this.head.toString( options, 1200, "left" ) + " :- " + this.body.toString( options, 1200, "right" ) + ".";
}
};
// Session
Session.prototype.toString = function( options ) {
var str = "";
for(var i = 0; i < this.modules.length; i++) {
str += ":- use_module(library(" + this.modules[i] + ")).\n";
}
str += "\n";
for(var key in this.rules) {
if(!this.rules.hasOwnProperty(key)) continue;
for(i = 0; i < this.rules[key].length; i++) {
str += this.rules[key][i].toString( options );
str += "\n";
}
}
return str;
};
// CLONE PROLOG OBJECTS
// Variables
Var.prototype.clone = function() {
return new Var( this.id );
};
// Numbers
Num.prototype.clone = function() {
return new Num( this.value, this.is_float );
};
// Terms
Term.prototype.clone = function() {
return new Term( this.id, map( this.args, function( arg ) {
return arg.clone();
} ) );
};
// Streams
Stream.prototype.clone = function() {
return new Stram( this.stream, this.mode, this.alias, this.type, this.reposition, this.eof_action );
};
// Substitutions
Substitution.prototype.clone = function() {
var links = {};
var attrs = {};
for( var link in this.links ) {
if(!this.links.hasOwnProperty(link)) continue;
links[link] = this.links[link].clone();
}
for( var attr in this.attrs ) {
if(!this.attrs.hasOwnProperty(attrs)) continue;
attrs[attr] = {};
for( var m in this.attrs[attr] ) {
if(!this.attrs[attr].hasOwnProperty(m)) continue;
attrs[attr][m] = this.attrs[attr][m].clone();
}
}
return new Substitution( links, attrs );
};
// States
State.prototype.clone = function() {
return new State( this.goal.clone(), this.substitution.clone(), this.parent );
};
// Rules
Rule.prototype.clone = function() {
return new Rule( this.head.clone(), this.body !== null ? this.body.clone() : null );
};
// COMPARE PROLOG OBJECTS
// Variables
Var.prototype.equals = function( obj ) {
return pl.type.is_variable( obj ) && this.id === obj.id;
};
// Numbers
Num.prototype.equals = function( obj ) {
return pl.type.is_number( obj ) && this.value === obj.value && this.is_float === obj.is_float;
};
// Terms
Term.prototype.equals = function( obj ) {
if( !pl.type.is_term( obj ) || this.indicator !== obj.indicator ) {
return false;
}
for( var i = 0; i < this.args.length; i++ ) {
if( !this.args[i].equals( obj.args[i] ) ) {
return false;
}
}
return true;
};
// Streams
Stream.prototype.equals = function( obj ) {
return pl.type.is_stream( obj ) && this.id === obj.id;
};
// Substitutions
Substitution.prototype.equals = function( obj ) {
var link;
if( !pl.type.is_substitution( obj ) ) {
return false;
}
for( link in this.links ) {
if(!this.links.hasOwnProperty(link)) continue;
if( !obj.links[link] || !this.links[link].equals( obj.links[link] ) ) {
return false;
}
}
for( link in obj.links ) {
if(!obj.links.hasOwnProperty(link)) continue;
if( !this.links[link] ) {
return false;
}
}
return true;
};
// States
State.prototype.equals = function( obj ) {
return pl.type.is_state( obj ) && this.goal.equals( obj.goal ) && this.substitution.equals( obj.substitution ) && this.parent === obj.parent;
};
// Rules
Rule.prototype.equals = function( obj ) {
return pl.type.is_rule( obj ) && this.head.equals( obj.head ) && (this.body === null && obj.body === null || this.body !== null && this.body.equals( obj.body ));
};
// RENAME VARIABLES OF PROLOG OBJECTS
// Variables
Var.prototype.rename = function( thread ) {
return thread.get_free_variable( this );
};
// Numbers
Num.prototype.rename = function( _ ) {
return this;
};
// Terms
Term.prototype.rename = function( thread ) {
// atom
/*if(this.args.length === 0)
return this;*/
// list
if( this.indicator === "./2" ) {
var arr = [], pointer = this;
var last_neq = -1, pointer_neq = null, i = 0;
while( pointer.indicator === "./2" ) {
var app = pointer.args[0].rename(thread);
var cmp = app == pointer.args[0];
arr.push(app);
pointer = pointer.args[1];
if(!cmp) {
last_neq = i;
pointer_neq = pointer;
}
i++;
}
var list = pointer.rename(thread);
var cmp = list == pointer;
if(last_neq === -1 && cmp)
return this;
var start = cmp ? last_neq : arr.length-1;
var list = cmp ? pointer_neq : list;
for(var i = start; i >= 0; i--) {
list = new Term( ".", [arr[i], list] );
}
return list;
}
// compound term
var eq = true;
var args = [];
for(var i = 0; i < this.args.length; i++) {
var app = this.args[i].rename(thread);
eq = eq && this.args[i] == app;
args.push(app);
}
/*if(eq)
return this;*/
return new Term(this.id, args);
};
// Streams
Stream.prototype.rename = function( thread ) {
return this;
};
// Rules
Rule.prototype.rename = function( thread ) {
return new Rule( this.head.rename( thread ), this.body !== null ? this.body.rename( thread ) : null );
};
// GET ID OF VARIABLES FROM PROLOG OBJECTS
// Variables
Var.prototype.variables = function() {
return [this.id];
};
// Numbers
Num.prototype.variables = function() {
return [];
};
// Terms
Term.prototype.variables = function() {
return [].concat.apply( [], map( this.args, function( arg ) {
return arg.variables();
} ) );
};
// Streams
Stream.prototype.variables = function() {
return [];
};
// Rules
Rule.prototype.variables = function() {
if( this.body === null ) {
return this.head.variables();
} else {
return this.head.variables().concat( this.body.variables() );
}
};
// APPLY SUBSTITUTIONS TO PROLOG OBJECTS
// Variables
Var.prototype.apply = function( subs ) {
if( subs.lookup( this.id ) ) {
return subs.lookup( this.id );
}
return this;
};
// Numbers
Num.prototype.apply = function( _ ) {
return this;
};
// Terms
Term.prototype.apply = function( subs ) {
// atom
if(this.args.length === 0)
return this;
// list
if( this.indicator === "./2" ) {
var arr = [], pointer = this;
var last_neq = -1, pointer_neq = null, i = 0;
while( pointer.indicator === "./2" ) {
var app = pointer.args[0].apply(subs);
var cmp = app == pointer.args[0];
arr.push(app);
pointer = pointer.args[1];
if(!cmp) {
last_neq = i;
pointer_neq = pointer;
}
i++;
}
var list = pointer.apply(subs);
var cmp = list == pointer;
if(last_neq === -1 && cmp)
return this;
var start = cmp ? last_neq : arr.length-1;
var list = cmp ? pointer_neq : list;
for(var i = start; i >= 0; i--) {
list = new Term( ".", [arr[i], list] );
}
return list;
}
// compound term
var eq = true;
var args = [];
for(var i = 0; i < this.args.length; i++) {
var app = this.args[i].apply(subs);
eq = eq && this.args[i] == app;
args.push(app);
}
if(eq)
return this;
return new Term(this.id, args, this.ref);
};
// Streams
Stream.prototype.apply = function( _ ) {
return this;
};
// Rules
Rule.prototype.apply = function( subs ) {
return new Rule( this.head.apply( subs ), this.body !== null ? this.body.apply( subs ) : null );
};
// Substitutions
Substitution.prototype.apply = function( subs ) {
var link, links = {}, attr, attrs = {}, m;
for( link in this.links ) {
if(!this.links.hasOwnProperty(link)) continue;
links[link] = this.links[link].apply(subs);
}
for( attr in this.attrs ) {
if(!this.attrs.hasOwnProperty(attr)) continue;
attrs[attr] = {};
for( m in this.attrs[attr] ) {
if(!this.attrs[attr].hasOwnProperty(m)) continue;
attrs[attr][m] = this.attrs[attr][m].apply(subs);
}
}
return new Substitution( links, attrs );
};
// SELECTION FUNCTION
// Select term
Term.prototype.select = function() {
var pointer = this;
while( pointer.indicator === ",/2" )
pointer = pointer.args[0];
return pointer;
};
// Replace term
Term.prototype.replace = function( expr ) {
if( this.indicator === ",/2" ) {
if( this.args[0].indicator === ",/2" ) {
return new Term( ",", [this.args[0].replace( expr ), this.args[1]] );
} else {
return expr === null ? this.args[1] : new Term( ",", [expr, this.args[1]] );
}
} else {
return expr;
}
};
// Search term
Term.prototype.search = function( expr ) {
if(this == expr || this.ref === expr.ref)
return true;
for( var i = 0; i < this.args.length; i++ )
if( pl.type.is_term( this.args[i] ) && this.args[i].search( expr ) )
return true;
return false;
};
// PROLOG SESSIONS AND THREADS
// Get current input
Session.prototype.get_current_input = function() {
return this.current_input;
};
Thread.prototype.get_current_input = function() {
return this.session.get_current_input();
};
// Get current output
Session.prototype.get_current_output = function() {
return this.current_output;
};
Thread.prototype.get_current_output = function() {
return this.session.get_current_output();
};
// Set current input
Session.prototype.set_current_input = function( input ) {
this.current_input = input;
};
Thread.prototype.set_current_input = function( input ) {
return this.session.set_current_input( input );
};
// Set current output
Session.prototype.set_current_output = function( output ) {
this.current_output = output;
};
Thread.prototype.set_current_output = function( output ) {
return this.session.set_current_output( output);
};
// Get stream by alias
Session.prototype.get_stream_by_alias = function( alias ) {
return this.streams[alias];
};
Thread.prototype.get_stream_by_alias = function( alias ) {
return this.session.get_stream_by_alias( alias );
};
// Open file
Session.prototype.file_system_open = function( path, type, mode ) {
if(this.get_flag("nodejs").indicator === "false/0")
path = cd(this.working_directory, path);
return this.file_system.open( path, type, mode );
};
Thread.prototype.file_system_open = function( path, type, mode ) {
return this.session.file_system_open( path, type, mode );
};
// Get conversion of the char
Session.prototype.get_char_conversion = function( char ) {
return this.__char_conversion[char] || char;
};
Thread.prototype.get_char_conversion = function( char ) {
return this.session.get_char_conversion( char );
};
// Parse an expression
Session.prototype.parse = function( string ) {
return this.thread.parse( string );
};
Thread.prototype.parse = function( string ) {
var tokenizer = new Tokenizer( this );
tokenizer.new_text( string );
var tokens = tokenizer.get_tokens();
if( tokens === null )
return false;
var expr = parseExpr(this, tokens, 0, this.__get_max_priority(), false);
if( expr.len !== tokens.length )
return false;
return { value: expr.value, expr: expr, tokens: tokens };
};
// Get flag value
Session.prototype.get_flag = function( flag ) {
return this.flag[flag];
};
Thread.prototype.get_flag = function( flag ) {
return this.session.get_flag( flag );
};
// Add a rule
Session.prototype.add_rule = function( rule, options ) {
options = options ? options : {};
options.from = options.from ? options.from : "$tau-js";
this.src_predicates[rule.head.indicator] = options.from;
if(!this.rules[rule.head.indicator]) {
this.rules[rule.head.indicator] = [];
}
this.rules[rule.head.indicator].push(rule);
if( !this.public_predicates.hasOwnProperty( rule.head.indicator ) )
this.public_predicates[rule.head.indicator] = false;
return true;
};
Thread.prototype.add_rule = function( rule, options ) {
return this.session.add_rule( rule, options );
};
// Run a directive
Session.prototype.run_directive = function( directive ) {
this.thread.run_directive( directive );
};
Thread.prototype.run_directive = function( directive ) {
if( pl.type.is_directive( directive ) ) {
if(pl.directive[directive.indicator])
pl.directive[directive.indicator]( this, directive );
else
pl.directive[directive.id + "/*"]( this, directive );
return true;
}
return false;
};
// Get maximum priority of the operators
Session.prototype.__get_max_priority = function() {
return "1200";
};
Thread.prototype.__get_max_priority = function() {
return this.session.__get_max_priority();
};
// Get next priority of the operators
Session.prototype.__get_next_priority = function( priority ) {
var max = 0;
priority = parseInt( priority );
for( var key in this.__operators ) {
if( !this.__operators.hasOwnProperty(key) ) continue;
var n = parseInt(key);
if( n > max && n < priority ) max = n;
}
return max.toString();
};
Thread.prototype.__get_next_priority = function( priority ) {
return this.session.__get_next_priority( priority );
};
// Get classes of an operator
Session.prototype.__lookup_operator_classes = function( priority, operator ) {
if( this.__operators.hasOwnProperty( priority ) && this.__operators[priority][operator] instanceof Array ) {
return this.__operators[priority][operator] || false;
}
return false;
};
Thread.prototype.__lookup_operator_classes = function( priority, operator ) {
return this.session.__lookup_operator_classes( priority, operator );
};
// Get operator
Session.prototype.lookup_operator = function( name, arity ) {
for(var p in this.__operators)
if(this.__operators[p][name])
for(var i = 0; i < this.__operators[p][name].length; i++)
if( arity === 0 || this.__operators[p][name][i].length === arity+1 )
return {priority: p, class: this.__operators[p][name][i]};
return null;
};
Thread.prototype.lookup_operator = function( name, arity ) {
return this.session.lookup_operator( name, arity );
};
// Throw a warning
Session.prototype.throw_warning = function( warning ) {
this.thread.throw_warning( warning );
};
Thread.prototype.throw_warning = function( warning ) {
this.warnings.push( warning );
};
// Get warnings
Session.prototype.get_warnings = function() {
return this.thread.get_warnings();
};
Thread.prototype.get_warnings = function() {
return this.warnings;
};
// Add a goal
Session.prototype.add_goal = function( goal, unique ) {
this.thread.add_goal( goal, unique );
};
Thread.prototype.add_goal = function( goal, unique, parent ) {
parent = parent ? parent : null;
if( unique === true )
this.points = [];
var vars = goal.variables();
var links = {};
for( var i = 0; i < vars.length; i++ )
links[vars[i]] = new Var(vars[i]);
this.points.push( new State( goal, new Substitution(links), parent ) );
};
// Consult a program from a string
Session.prototype.consult = function( program, options ) {
return this.thread.consult( program, options );
};
Thread.prototype.consult = function( program, options ) {
var string = "";
// string
if( typeof program === "string" ) {
string = program;
var len = string.length;
// script id
if( this.get_flag("nodejs").indicator === "false/0" && program != "" && document.getElementById( string ) ) {
var script = document.getElementById( string );
var type = script.getAttribute( "type" );
if( type !== null && type.replace( / /g, "" ).toLowerCase() === "text/prolog" ) {
string = script.text;
}
// file (node.js)
} else if( this.get_flag("nodejs").indicator === "true/0" ) {
var fs = require("fs");
const isFile = fs.existsSync(program);
if(isFile) string = fs.readFileSync( program ).toString();
else string = program;
// http request
} else if( program != "" && !(/\s/.test(program)) ) {
try {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if( this.readyState == 4 && this.status == 200 )
string = xhttp.responseText;
}
xhttp.open("GET", program, false);
xhttp.send();
} catch(ex) {}
}
// html
} else if( program.nodeName ) {
switch( program.nodeName.toLowerCase() ) {
case "input":
case "textarea":
string = program.value;
break;
default:
string = program.innerHTML;
break;
}
} else {
return false;
}
this.warnings = [];
return parseProgram( this, string, options );
};
// Query goal from a string (without ?-)
Session.prototype.query = function( string ) {
return this.thread.query( string );
};
Thread.prototype.query = function( string ) {
this.points = [];
this.debugger_states = [];
return parseQuery( this, string );
};
// Get first choice point
Session.prototype.head_point = function() {
return this.thread.head_point();
};
Thread.prototype.head_point = function() {
return this.points[this.points.length-1];
};
// Get free variable
Session.prototype.get_free_variable = function( variable ) {
return this.thread.get_free_variable( variable );
};
Thread.prototype.get_free_variable = function( variable ) {
var variables = [];
if( variable.id === "_" || this.session.renamed_variables[variable.id] === undefined ) {
this.session.rename++;
if( this.current_point )
variables = this.current_point.substitution.domain();
while( indexOf( variables, pl.format_variable( this.session.rename ) ) !== -1 ) {
this.session.rename++;
}
if( variable.id === "_" ) {
return new Var( pl.format_variable( this.session.rename ) );
} else {
this.session.renamed_variables[variable.id] = pl.format_variable( this.session.rename );
}
}
return new Var( this.session.renamed_variables[variable.id] );
};
// Get next free variable
Session.prototype.next_free_variable = function() {
return this.thread.next_free_variable();
};
Thread.prototype.next_free_variable = function() {
this.session.rename++;
var variables = [];
if( this.current_point )
variables = this.current_point.substitution.domain();
while( indexOf( variables, pl.format_variable( this.session.rename ) ) !== -1 ) {
this.session.rename++;
}
return new Var( pl.format_variable( this.session.rename ) );
};
// Check if a predicate is public
Session.prototype.is_public_predicate = function( indicator ) {
return !this.public_predicates.hasOwnProperty( indicator ) || this.public_predicates[indicator] === true;
};
Thread.prototype.is_public_predicate = function( indicator ) {
return this.session.is_public_predicate( indicator );
};
// Check if a predicate is multifile
Session.prototype.is_multifile_predicate = function( indicator ) {
return this.multifile_predicates.hasOwnProperty( indicator ) && this.multifile_predicates[indicator] === true;
};
Thread.prototype.is_multifile_predicate = function( indicator ) {
return this.session.is_multifile_predicate( indicator );
};
// Insert states at the beginning
Session.prototype.prepend = function( states ) {
return this.thread.prepend( states );
};
Thread.prototype.prepend = function( states ) {
for(var i = states.length-1; i >= 0; i--)
this.points.push( states[i] );
};
// Remove the selected term and prepend the current state
Session.prototype.success = function( point, parent ) {
return this.thread.success( point, parent );
}
Thread.prototype.success = function( point, parent ) {
var parent = typeof parent === "undefined" ? point : parent;
this.prepend( [new State( point.goal.replace( null ), point.substitution, parent ) ] );
};
// Throw error
Session.prototype.throw_error = function( error ) {
return this.thread.throw_error( error );
};
Thread.prototype.throw_error = function( error ) {
this.prepend( [new State( new Term( "throw", [error] ), new Substitution(), null, null )] );
};
// Selection rule
Session.prototype.step_rule = function( mod, atom ) {
return this.thread.step_rule( mod, atom );
}
Thread.prototype.step_rule = function( mod, atom ) {
var name = atom.indicator;
if( mod === "user" )
mod = null;
if( mod === null && this.session.rules.hasOwnProperty(name) )
return this.session.rules[name];
var modules = mod === null ? this.session.modules : (indexOf(this.session.modules, mod) === -1 ? [] : [mod]);
for( var i = 0; i < modules.length; i++ ) {
var module = pl.module[modules[i]];
if( module.rules.hasOwnProperty(name) && (module.rules.hasOwnProperty(this.level) || module.exports_predicate(name)) )
return pl.module[modules[i]].rules[name];
}
return null;
};
// Resolution step
Session.prototype.step = function() {
return this.thread.step();
}
Thread.prototype.step = function() {
if( this.points.length === 0 ) {
return;
}
var asyn = false;
var point = this.points.pop();
this.current_point = point;
if( this.debugger )
this.debugger_states.push( point );
if( pl.type.is_term( point.goal ) ) {
var atom = point.goal.select();
var mod = null;
var states = [];
if( atom !== null ) {
this.total_steps++;
var level = point;
while( level.parent !== null && level.parent.goal.search( atom ) )
level = level.parent;
this.level = level.parent === null ? "top_level/0" : level.parent.goal.select().indicator;
if( pl.type.is_term( atom ) && atom.indicator === ":/2" ) {
mod = atom.args[0].id;
atom = atom.args[1];
atom.from_module = mod;
}
if(
(mod === null || atom.indicator === "listing/0" || atom.indicator === "listing/1")
&& pl.type.is_builtin( atom )
) {
this.__call_indicator = atom.indicator;
asyn = pl.predicate[atom.indicator]( this, point, atom );
} else {
var srule = this.step_rule(mod, atom);
if( srule === null ) {
if( !this.session.rules.hasOwnProperty( atom.indicator ) ) {
if( this.get_flag( "unknown" ).id === "error" ) {
this.throw_error( pl.error.existence( "procedure", atom.indicator, this.level ) );
} else if( this.get_flag( "unknown" ).id === "warning" ) {
this.throw_warning( "unknown procedure " + atom.indicator + " (from " + this.level + ")" );
}
}
} else if( srule instanceof Function ) {
asyn = srule( this, point, atom );
} else {
// Goal expansion
if( this.__goal_expansion && atom.indicator === "goal_expansion/2" )
srule = srule.concat(pl.predicate["goal_expansion/2"]);
for( var _rule in srule ) {
if(!srule.hasOwnProperty(_rule)) continue;
var rule = srule[_rule];
this.session.renamed_variables = {};
rule = rule.rename( this );
var occurs_check = this.get_flag( "occurs_check" ).indicator === "true/0";
var state = new State();
var mgu = pl.unify( atom, rule.head, occurs_check );
if( mgu !== null ) {
state.goal = point.goal.replace( rule.body );
if( state.goal !== null ) {
state.goal = state.goal.apply( mgu );
}
state.substitution = point.substitution.apply( mgu );
state.parent = point;
states.push( state );
}
}
this.prepend( states );
}
}
}
} else if( pl.type.is_variable( point.goal ) ) {
this.throw_error( pl.error.instantiation( this.level ) );
} else {
this.throw_error( pl.error.type( "callable", point.goal, this.level ) );
}
return asyn;
};
// Find next computed answer
Session.prototype.answer = function( success ) {
return this.thread.answer( success );
}
Thread.prototype.answer = function( success ) {
success = success || function( _ ) { };
this.__calls.push( success );
if( this.__calls.length > 1 ) {
return;
}
this.again();
};
// Find all computed answers
Session.prototype.answers = function( callback, max, after ) {
return this.thread.answers( callback, max, after );
}
Thread.prototype.answers = function( callback, max, after ) {
var answers = max || 1000;
var thread = this;
if( max <= 0 ) {
if(after)
after();
return;
}
this.answer( function( answer ) {
callback( answer );
if( answer !== false ) {
setTimeout( function() {
thread.answers( callback, max-1, after );
}, 1 );
} else if(after) {
after();
}
} );
};
// Again finding next computed answer
Session.prototype.again = function( reset_limit ) {
return this.thread.again( reset_limit );
};
Thread.prototype.again = function( reset_limit ) {
var answer;
var t0 = Date.now();
while( this.__calls.length > 0 ) {
this.warnings = [];
if( reset_limit !== false )
this.current_limit = this.session.limit;
while( this.current_limit > 0 && this.points.length > 0 && this.head_point().goal !== null && !pl.type.is_error( this.head_point().goal ) ) {
this.current_limit--;
if( this.step() === true ) {
return;
}
}
var t1 = Date.now();
this.cpu_time_last = t1-t0;
this.cpu_time += this.cpu_time_last;
var success = this.__calls.shift();
if( this.current_limit <= 0 ) {
success( null );
} else if( this.points.length === 0 ) {
success( false );
} else if( pl.type.is_error( this.head_point().goal ) ) {
answer = this.format_error( this.points.pop() );
this.points = [];
success( answer );
} else {
if( this.debugger )
this.debugger_states.push( this.head_point() );
answer = this.format_success( this.points.pop() );
success( answer );
}
}
};
// Unfolding transformation
Session.prototype.unfold = function( rule ) {
if(rule.body === null)
return false;
var head = rule.head;
var body = rule.body;
var atom = body.select();
var thread = new Thread( this );
var unfolded = [];
thread.add_goal( atom );
thread.step();
for( var i = thread.points.length-1; i >= 0; i-- ) {
var point = thread.points[i];
var head2 = head.apply( point.substitution );
var body2 = body.replace( point.goal );
if( body2 !== null )
body2 = body2.apply( point.substitution );
unfolded.push( new Rule( head2, body2 ) );
}
var rules = this.rules[head.indicator];
var index = indexOf( rules, rule );
if( unfolded.length > 0 && index !== -1 ) {
rules.splice.apply( rules, [index, 1].concat(unfolded) );
return true;
}
return false;
};
Thread.prototype.unfold = function(rule) {
return this.session.unfold(rule);
};
// INTERPRET EXPRESSIONS
// Variables
Var.prototype.interpret = function( thread ) {
return pl.error.instantiation( thread.level );
};
// Numbers
Num.prototype.interpret = function( thread ) {
return this;
};
// Terms
Term.prototype.interpret = function( thread ) {
if( pl.type.is_unitary_list( this ) ) {
return this.args[0].interpret( thread );
} else {
return pl.operate( thread, this );
}
};
// COMPARE PROLOG OBJECTS
// Variables
Var.prototype.compare = function( obj ) {
if( this.id < obj.id ) {
return -1;
} else if( this.id > obj.id ) {
return 1;
} else {
return 0;
}
};
// Numbers
Num.prototype.compare = function( obj ) {
if( this.value === obj.value && this.is_float === obj.is_float ) {
return 0;
} else if( this.value < obj.value || this.value === obj.value && this.is_float && !obj.is_float ) {
return -1;
} else if( this.value > obj.value ) {
return 1;
}
};
// Terms
Term.prototype.compare = function( obj ) {
if( this.args.length < obj.args.length || this.args.length === obj.args.length && this.id < obj.id ) {
return -1;
} else if( this.args.length > obj.args.length || this.args.length === obj.args.length && this.id > obj.id ) {
return 1;
} else {
for( var i = 0; i < this.args.length; i++ ) {
var arg = pl.compare( this.args[i], obj.args[i] );
if( arg !== 0 ) {
return arg;
}
}
return 0;
}
};
// SUBSTITUTIONS
// Lookup variable
Substitution.prototype.lookup = function( variable ) {
if( this.links[variable] ) {
return this.links[variable];
} else {
return null;
}
};
// Filter variables
Substitution.prototype.filter = function( predicate ) {
var links = {};
for( var id in this.links ) {
if(!this.links.hasOwnProperty(id)) continue;
var value = this.links[id];
if( predicate( id, value ) ) {
links[id] = value;
}
}
return new Substitution( links, this.attrs );
};
// Exclude variables
Substitution.prototype.exclude = function( variables ) {
var links = {};
for( var variable in this.links ) {
if(!this.links.hasOwnProperty(variable)) continue;
if( indexOf( variables, variable ) === -1 ) {
links[variable] = this.links[variable];
}
}
return new Substitution( links, this.attrs );
};
// Add link
Substitution.prototype.add = function( variable, value ) {
this.links[variable] = value;
};
// Get domain
Substitution.prototype.domain = function( plain ) {
var f = plain === true ? function(x){return x;} : function(x){return new Var(x);};
var vars = [];
for( var x in this.links )
vars.push( f(x) );
return vars;
};
// Get an attribute
Substitution.prototype.get_attribute = function( variable, module ) {
if( this.attrs[variable] )
return this.attrs[variable][module];
}
// Set an attribute (in a new substitution)
Substitution.prototype.set_attribute = function( variable, module, value ) {
var subs = new Substitution( this.links );
for( var v in this.attrs ) {
if( v === variable ) {
subs.attrs[v] = {};
for( var m in this.attrs[v] ) {
subs.attrs[v][m] = this.attrs[v][m];
}
} else {
subs.attrs[v] = this.attrs[v];
}
}
if( !subs.attrs[variable] ) {
subs.attrs[variable] = {};
}
subs.attrs[variable][module] = value;
return subs;
}
// Check if a variables has attributes
Substitution.prototype.has_attributes = function( variable ) {
return this.attrs[variable] && this.attrs[variable] !== {};
}
// GENERATE JAVASCRIPT CODE FROM PROLOG OBJECTS
// Variables
Var.prototype.compile = function() {
return 'new pl.type.Var("' + this.id.toString() + '")';
};
// Numbers
Num.prototype.compile = function() {
return 'new pl.type.Num(' + this.value.toString() + ', ' + this.is_float.toString() + ')';
};
// Terms
Term.prototype.compile = function() {
return 'new pl.type.Term("' + this.id.replace(/"/g, '\\"') + '", [' + map( this.args, function( arg ) {
return arg.compile();
} ) + '])';
};
// Rules
Rule.prototype.compile = function() {
return 'new pl.type.Rule(' + this.head.compile() + ', ' + (this.body === null ? 'null' : this.body.compile()) + ')';
};
// Sessions
Session.prototype.compile = function() {
var str, obj = [], rules;
for( var _indicator in this.rules ) {
if(!this.rules.hasOwnProperty(_indicator)) continue;
var indicator = this.rules[_indicator];
rules = [];
str = "\"" + _indicator + "\": [";
for( var i = 0; i < indicator.length; i++ ) {
rules.push( indicator[i].compile() );
}
str += rules.join();
str += "]";
obj.push( str );
}
return "{" + obj.join() + "};";
};
// PROLOG TO JAVASCRIPT
Var.prototype.toJavaScript = function() {
return this.toString();
};
// Numbers
Num.prototype.toJavaScript = function() {
return this.value;
};
// Terms
Term.prototype.toJavaScript = function() {
// Atom => String
if( this.args.length === 0 && this.indicator !== "[]/0" ) {
return this.toString();
} else if( pl.type.is_list( this ) ) {
// List => Array
var all_obj = true;
var arr = [];
var obj = {};
var pointer = this;
var value;
while( pointer.indicator === "./2" ) {
value = pointer.args[0].toJavaScript();
arr.push( value );
all_obj = all_obj && pl.type.is_term(pointer.args[0]) && pointer.args[0].indicator === "-/2" && pl.type.is_atom(pointer.args[0].args[0]);
if(all_obj)
obj[pointer.args[0].args[0].id] = pointer.args[0].args[1].toJavaScript();
pointer = pointer.args[1];
}
if( pointer.indicator === "[]/0" )
return all_obj && arr.length > 0 ? obj : arr;
}
return this.toString();
};
// RULES
// Return singleton variables in the session
Rule.prototype.singleton_variables = function() {
var variables = this.head.variables();
var count = {};
var singleton = [];
if( this.body !== null )
variables = variables.concat( this.body.variables() );
for( var i = 0; i < variables.length; i++ ) {
if( count[variables[i]] === undefined )
count[variables[i]] = 0;
count[variables[i]]++;
}
for( var key in count )
if( key !== "_" && count[key] === 1 )
singleton.push( key );
return singleton;
};
// NODEJS
var nodejs_flag = typeof process !== 'undefined' && !process.browser
var nodejs_arguments = nodejs_flag ?
arrayToList( map(process.argv.slice(1), function(arg) { return new Term( arg ); })) :
new Term("[]", []);
// PROLOG
var pl = {
// Environment
__env: nodejs_flag ? global : window,
// Modules
module: {},
// Version
version: version,
// Parser
parser: {
tokenizer: Tokenizer,
expression: parseExpr
},
// Utils
utils: {
// String to indicator
str_indicator: str_indicator,
// Code point at
codePointAt: codePointAt,
// From code point
fromCodePoint: fromCodePoint,
// Current directory
cd: cd
},
// Statistics
statistics: {
// Number of created terms
getCountTerms: function() {
return term_ref;
}
},
// JavaScript to Prolog
fromJavaScript: {
// Type testing
test: {
// Boolean
boolean: function( obj, tobj ) {
return obj === true || obj === false;
},
// Number
number: function( obj, tobj ) {
return typeof obj === "number";
},
// String
string: function( obj, tobj ) {
return typeof obj === "string";
},
// List
list: function( obj, tobj ) {
return obj instanceof Array;
},
// Variable
variable: function( obj, tobj ) {
return obj === undefined;
},
// Object
object: function( obj, tobj ) {
tobj = tobj === undefined ? false : tobj;
return tobj && !(obj instanceof Array) && typeof obj === "object";
},
// Any
any: function( _, tobj ) {
return true;
}
},
// Function conversion
conversion: {
// Bolean
boolean: function( obj, tobj ) {
return new Term( obj ? "true" : "false", [] );
},
// Number
number: function( obj, tobj ) {
return new Num( obj, obj % 1 !== 0 );
},
// String
string: function( obj, tobj ) {
return new Term( obj, [] );
},
// List
list: function( obj, tobj ) {
tobj = tobj === undefined ? false : tobj;
var arr = [];
var elem;
for( var i = 0; i < obj.length; i++ ) {
elem = pl.fromJavaScript.apply( obj[i], tobj );
if( elem === undefined )
return undefined;
arr.push( elem );
}
return arrayToList( arr );
},
// Variable
variable: function( obj, tobj ) {
return new Var( "_" );
},
// Object
object: function( obj, tobj ) {
tobj = tobj === undefined ? false : tobj;
var list = new Term("[]", []);
var arr = [];
for(var prop in obj) {
if(!obj.hasOwnProperty(prop)) continue;
arr.push(new Term("-", [
pl.fromJavaScript.apply(prop, tobj),
pl.fromJavaScript.apply(obj[prop], tobj)
]));
}
return arrayToList(arr);
},
// Any
any: function( obj, tobj ) {
return undefined;
}
},
// Transform object
apply: function( obj, tobj ) {
tobj = tobj === undefined ? false : tobj;
for( var i in pl.fromJavaScript.test )
if( i !== "any" && pl.fromJavaScript.test[i]( obj, tobj ) )
return pl.fromJavaScript.conversion[i]( obj, tobj );
return pl.fromJavaScript.conversion.any( obj, tobj );
}
},
// Types
type: {
// Objects
Var: Var,
Num: Num,
Term: Term,
Rule: Rule,
State: State,
Stream: Stream,
Module: Module,
Thread: Thread,
Session: Session,
Substitution: Substitution,
File: TauFile,
Directory: TauDirectory,
// Order
order: [Var, Num, Term, Stream],
// Compare types
compare: function( x, y ) {
var ord_x = indexOf( pl.type.order, x.constructor );
var ord_y = indexOf( pl.type.order, y.constructor );
if( ord_x < ord_y ) {
return -1;
} else if( ord_x > ord_y ) {
return 1;
} else {
if( x.constructor === Num )
if( x.is_float && y.is_float )
return 0;
else if( x.is_float )
return -1;
else if( y.is_float )
return 1;
return 0;
}
},
// Is a substitution
is_substitution: function( obj ) {
return obj instanceof Substitution;
},
// Is a state
is_state: function( obj ) {
return obj instanceof State;
},
// Is a rule
is_rule: function( obj ) {
return obj instanceof Rule;
},
// Is a variable
is_variable: function( obj ) {
return obj instanceof Var;
},
// Is a stream
is_stream: function( obj ) {
return obj instanceof Stream;
},
// Is an anonymous variable
is_anonymous_var: function( obj ) {
return obj instanceof Var && obj.id === "_";
},
// Is a callable term
is_callable: function( obj ) {
return obj instanceof Term;
},
// Is a number
is_number: function( obj ) {
return obj instanceof Num;
},
// Is an integer
is_integer: function( obj ) {
return obj instanceof Num && !obj.is_float;
},
// Is a float
is_float: function( obj ) {
return obj instanceof Num && obj.is_float;
},
// Is a term
is_term: function( obj ) {
return obj instanceof Term;
},
// Is an atom
is_atom: function( obj ) {
return obj instanceof Term && obj.args.length === 0;
},
// Is a ground term
is_ground: function( obj ) {
if( obj instanceof Var ) return false;
if( obj instanceof Term )
for( var i = 0; i < obj.args.length; i++ )
if( !pl.type.is_ground( obj.args[i] ) )
return false;
return true;
},
// Is atomic
is_atomic: function( obj ) {
return obj instanceof Term && obj.args.length === 0 || obj instanceof Num;
},
// Is compound
is_compound: function( obj ) {
return obj instanceof Term && obj.args.length > 0;
},
// Is a list
is_list: function( obj ) {
return obj instanceof Term && (obj.indicator === "[]/0" || obj.indicator === "./2");
},
// Is an empty list
is_empty_list: function( obj ) {
return obj instanceof Term && obj.indicator === "[]/0";
},
// Is a non empty list
is_non_empty_list: function( obj ) {
return obj instanceof Term && obj.indicator === "./2";
},
// Is a fully list
is_fully_list: function( obj ) {
while( obj instanceof Term && obj.indicator === "./2" ) {
obj = obj.args[1];
}
return obj instanceof Var || obj instanceof Term && obj.indicator === "[]/0";
},
// Is a instantiated list
is_instantiated_list: function( obj ) {
while( obj instanceof Term && obj.indicator === "./2" ) {
obj = obj.args[1];
}
return obj instanceof Term && obj.indicator === "[]/0";
},
// Is an unitary list
is_unitary_list: function( obj ) {
return obj instanceof Term && obj.indicator === "./2" && obj.args[1] instanceof Term && obj.args[1].indicator === "[]/0";
},
// Is a character
is_character: function( obj ) {
return obj instanceof Term && (obj.id.length === 1 || obj.id.length > 0 && obj.id.length <= 2 && codePointAt( obj.id, 0 ) >= 65536);
},
// Is a character
is_character_code: function( obj ) {
return obj instanceof Num && !obj.is_float && obj.value >= 0 && obj.value <= 1114111;
},
// Is a byte
is_byte: function( obj ) {
return obj instanceof Num && !obj.is_float && obj.value >= 0 && obj.value <= 255;
},
// Is an operator
is_operator: function( obj ) {
return obj instanceof Term && pl.arithmetic.evaluation[obj.indicator];
},
// Is a directive
is_directive: function( obj ) {
return obj instanceof Term && (pl.directive[obj.indicator] !== undefined || pl.directive[obj.id + "/*"] !== undefined);
},
// Is a built-in predicate
is_builtin: function( obj ) {
return obj instanceof Term && pl.predicate[obj.indicator] !== undefined && obj.indicator !== "goal_expansion/2";
},
// Is an error
is_error: function( obj ) {
return obj instanceof Term && obj.indicator === "throw/1";
},
// Is a predicate indicator
is_predicate_indicator: function( obj ) {
return obj instanceof Term && obj.indicator === "//2" && obj.args[0] instanceof Term && obj.args[0].args.length === 0 && obj.args[1] instanceof Num && obj.args[1].is_float === false;
},
// Is a flag
is_flag: function( obj ) {
return obj instanceof Term && obj.args.length === 0 && pl.flag[obj.id] !== undefined;
},
// Is a valid value for a flag
is_value_flag: function( flag, obj ) {
if( !pl.type.is_flag( flag ) ) return false;
for( var value in pl.flag[flag.id].allowed ) {
if(!pl.flag[flag.id].allowed.hasOwnProperty(value)) continue;
if( pl.flag[flag.id].allowed[value].equals( obj ) ) return true;
}
return false;
},
// Is a io mode
is_io_mode: function( obj ) {
return pl.type.is_atom( obj ) && ["read","write","append"].indexOf( obj.id ) !== -1;
},
// Is a stream option
is_stream_option: function( obj ) {
return pl.type.is_term( obj ) && (
obj.indicator === "alias/1" && pl.type.is_atom(obj.args[0]) ||
obj.indicator === "reposition/1" && pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "true" || obj.args[0].id === "false") ||
obj.indicator === "type/1" && pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "text" || obj.args[0].id === "binary") ||
obj.indicator === "eof_action/1" && pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "error" || obj.args[0].id === "eof_code" || obj.args[0].id === "reset")
);
},
// Is a stream position
is_stream_position: function( obj ) {
return pl.type.is_integer( obj ) && obj.value >= 0 || pl.type.is_atom( obj ) && (obj.id === "end_of_stream" || obj.id === "past_end_of_stream");
},
// Is a stream property
is_stream_property: function( obj ) {
return pl.type.is_term( obj ) && (
obj.indicator === "input/0" ||
obj.indicator === "output/0" ||
obj.indicator === "alias/1" && (pl.type.is_variable( obj.args[0] ) || pl.type.is_atom( obj.args[0] )) ||
obj.indicator === "file_name/1" && (pl.type.is_variable( obj.args[0] ) || pl.type.is_atom( obj.args[0] )) ||
obj.indicator === "position/1" && (pl.type.is_variable( obj.args[0] ) || pl.type.is_stream_position( obj.args[0] )) ||
obj.indicator === "reposition/1" && (pl.type.is_variable( obj.args[0] ) || pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "true" || obj.args[0].id === "false")) ||
obj.indicator === "type/1" && (pl.type.is_variable( obj.args[0] ) || pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "text" || obj.args[0].id === "binary")) ||
obj.indicator === "mode/1" && (pl.type.is_variable( obj.args[0] ) || pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "read" || obj.args[0].id === "write" || obj.args[0].id === "append")) ||
obj.indicator === "eof_action/1" && (pl.type.is_variable( obj.args[0] ) || pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "error" || obj.args[0].id === "eof_code" || obj.args[0].id === "reset")) ||
obj.indicator === "end_of_stream/1" && (pl.type.is_variable( obj.args[0] ) || pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "at" || obj.args[0].id === "past" || obj.args[0].id === "not"))
);
},
// Is a streamable term
is_streamable: function( obj ) {
return obj.__proto__.stream !== undefined;
},
// Is a read option
is_read_option: function( obj ) {
return pl.type.is_term( obj ) && ["variables/1","variable_names/1","singletons/1"].indexOf( obj.indicator ) !== -1;
},
// Is a write option
is_write_option: function( obj ) {
return pl.type.is_term( obj ) && (
obj.indicator === "quoted/1" && pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "true" || obj.args[0].id === "false") ||
obj.indicator === "ignore_ops/1" && pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "true" || obj.args[0].id === "false") ||
obj.indicator === "numbervars/1" && pl.type.is_atom(obj.args[0]) && (obj.args[0].id === "true" || obj.args[0].id === "false")
);
},
// Is a close option
is_close_option: function( obj ) {
return pl.type.is_term( obj ) &&
obj.indicator === "force/1" &&
pl.type.is_atom(obj.args[0]) &&
(obj.args[0].id === "true" || obj.args[0].id === "false");
},
// Is a modifiable flag
is_modifiable_flag: function( obj ) {
return pl.type.is_flag( obj ) && pl.flag[obj.id].changeable;
},
// Is an existing module
is_module: function( obj ) {
return obj instanceof Term && obj.indicator === "library/1" && obj.args[0] instanceof Term && obj.args[0].args.length === 0 && pl.module[obj.args[0].id] !== undefined;
},
// Is a virtual file
is_file: function( obj ) {
return obj instanceof TauFile;
},
// Is a virtual directory
is_directory: function( obj ) {
return obj instanceof TauDirectory;
}
},
// Arithmetic functions
arithmetic: {
// Evaluation
evaluation: {
"e/0": {
type_args: null,
type_result: true,
fn: function( _ ) { return Math.E; }
},
"pi/0": {
type_args: null,
type_result: true,
fn: function( _ ) { return Math.PI; }
},
"tau/0": {
type_args: null,
type_result: true,
fn: function( _ ) { return 2*Math.PI; }
},
"epsilon/0": {
type_args: null,
type_result: true,
fn: function( _ ) { return Number.EPSILON; }
},
"+/1": {
type_args: null,
type_result: null,
fn: function( x, _ ) { return x; }
},
"-/1": {
type_args: null,
type_result: null,
fn: function( x, _ ) { return -x; }
},
"\\/1": {
type_args: false,
type_result: false,
fn: function( x, _ ) { return ~x; }
},
"abs/1": {
type_args: null,
type_result: null,
fn: function( x, _ ) { return Math.abs( x ); }
},
"sign/1": {
type_args: null,
type_result: null,
fn: function( x, _ ) { return Math.sign( x ); }
},
"float_integer_part/1": {
type_args: true,
type_result: false,
fn: function( x, _ ) { return parseInt( x ); }
},
"float_fractional_part/1": {
type_args: true,
type_result: true,
fn: function( x, _ ) { return x - parseInt( x ); }
},
"float/1": {
type_args: null,
type_result: true,
fn: function( x, _ ) { return parseFloat( x ); }
},
"floor/1": {
type_args: true,
type_result: false,
fn: function( x, _ ) { return Math.floor( x ); }
},
"truncate/1": {
type_args: true,
type_result: false,
fn: function( x, _ ) { return parseInt( x ); }
},
"round/1": {
type_args: true,
type_result: false,
fn: function( x, _ ) { return Math.round( x ); }
},
"ceiling/1": {
type_args: true,
type_result: false,
fn: function( x, _ ) { return Math.ceil( x ); }
},
"sin/1": {
type_args: null,
type_result: true,
fn: function( x, _ ) { return Math.sin( x ); }
},
"cos/1": {
type_args: null,
type_result: true,
fn: function( x, _ ) { return Math.cos( x ); }
},
"tan/1": {
type_args: null,
type_result: true,
fn: function( x, _ ) { return Math.tan( x ); }
},
"asin/1": {
type_args: null,
type_result: true,
fn: function( x, _ ) { return Math.asin( x ); }
},
"acos/1": {
type_args: null,
type_result: true,
fn: function( x, _ ) { return Math.acos( x ); }
},
"atan/1": {
type_args: null,
type_result: true,
fn: function( x, _ ) { return Math.atan( x ); }
},
"atan2/2": {
type_args: null,
type_result: true,
fn: function( x, y, _ ) { return Math.atan2( x, y ); }
},
"exp/1": {
type_args: null,
type_result: true,
fn: function( x, _ ) { return Math.exp( x ); }
},
"sqrt/1": {
type_args: null,
type_result: true,
fn: function( x, _ ) { return Math.sqrt( x ); }
},
"log/1": {
type_args: null,
type_result: true,
fn: function( x, thread ) { return x > 0 ? Math.log( x ) : pl.error.evaluation( "undefined", thread.__call_indicator ); }
},
"+/2": {
type_args: null,
type_result: null,
fn: function( x, y, _ ) { return x + y; }
},
"-/2": {
type_args: null,
type_result: null,
fn: function( x, y, _ ) { return x - y; }
},
"*/2": {
type_args: null,
type_result: null,
fn: function( x, y, _ ) { return x * y; }
},
"//2": {
type_args: null,
type_result: true,
fn: function( x, y, thread ) { return y ? x / y : pl.error.evaluation( "zero_division", thread.__call_indicator ); }
},
"///2": {
type_args: false,
type_result: false,
fn: function( x, y, thread ) { return y ? Math.trunc( x / y ) : pl.error.evaluation( "zero_division", thread.__call_indicator ); }
},
"div/2": {
type_args: false,
type_result: false,
fn: function( x, y, thread ) { return y ? Math.floor( x / y ) : pl.error.evaluation( "zero_division", thread.__call_indicator ); }
},
"**/2": {
type_args: null,
type_result: true,
fn: function( x, y, _ ) { return Math.pow(x, y); }
},
"^/2": {
type_args: null,
type_result: null,
fn: function( x, y, _ ) { return Math.pow(x, y); }
},
"<</2": {
type_args: false,
type_result: false,
fn: function( x, y, _ ) { return x << y; }
},
">>/2": {
type_args: false,
type_result: false,
fn: function( x, y, _ ) { return x >> y; }
},
"/\\/2": {
type_args: false,
type_result: false,
fn: function( x, y, _ ) { return x & y; }
},
"\\//2": {
type_args: false,
type_result: false,
fn: function( x, y, _ ) { return x | y; }
},
"xor/2": {
type_args: false,
type_result: false,
fn: function( x, y, _ ) { return x ^ y; }
},
"rem/2": {
type_args: false,
type_result: false,
fn: function( x, y, thread ) { return y ? x % y : pl.error.evaluation( "zero_division", thread.__call_indicator ); }
},
"mod/2": {
type_args: false,
type_result: false,
fn: function( x, y, thread ) { return y ? x - Math.floor( x / y ) * y : pl.error.evaluation( "zero_division", thread.__call_indicator ); }
},
"max/2": {
type_args: null,
type_result: null,
fn: function( x, y, _ ) { return Math.max( x, y ); }
},
"min/2": {
type_args: null,
type_result: null,
fn: function( x, y, _ ) { return Math.min( x, y ); }
}
}
},
// Directives
directive: {
// dynamic/1
"dynamic/1": function( thread, atom ) {
var indicators = atom.args[0];
if(!pl.type.is_list(indicators))
indicators = arrayToList([indicators]);
var pointer = indicators;
while(pl.type.is_term(pointer) && pointer.indicator === "./2") {
indicator = pointer.args[0];
if( pl.type.is_variable( indicator ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_compound( indicator ) || indicator.indicator !== "//2" ) {
thread.throw_error( pl.error.type( "predicate_indicator", indicator, atom.indicator ) );
} else if( pl.type.is_variable( indicator.args[0] ) || pl.type.is_variable( indicator.args[1] ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( indicator.args[0] ) ) {
thread.throw_error( pl.error.type( "atom", indicator.args[0], atom.indicator ) );
} else if( !pl.type.is_integer( indicator.args[1] ) ) {
thread.throw_error( pl.error.type( "integer", indicator.args[1], atom.indicator ) );
} else {
var key = indicator.args[0].id + "/" + indicator.args[1].value;
thread.session.public_predicates[key] = true;
if( !thread.session.rules[key] )
thread.session.rules[key] = [];
}
pointer = pointer.args[1];
}
if(pl.type.is_variable(pointer)) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if(!pl.type.is_term(pointer) || pointer.indicator !== "[]/0") {
thread.throw_error( pl.error.type( "predicate_indicator", indicator, atom.indicator ) );
}
},
// dynamic/[2..]
"dynamic/*": function( thread, atom ) {
for(var i = 0; i < atom.args.length; i++) {
pl.directive["dynamic/1"](thread, new Term("dynamic", [atom.args[i]]));
}
},
// multifile/1
"multifile/1": function( thread, atom ) {
var indicator = atom.args[0];
if( pl.type.is_variable( indicator ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_compound( indicator ) || indicator.indicator !== "//2" ) {
thread.throw_error( pl.error.type( "predicate_indicator", indicator, atom.indicator ) );
} else if( pl.type.is_variable( indicator.args[0] ) || pl.type.is_variable( indicator.args[1] ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( indicator.args[0] ) ) {
thread.throw_error( pl.error.type( "atom", indicator.args[0], atom.indicator ) );
} else if( !pl.type.is_integer( indicator.args[1] ) ) {
thread.throw_error( pl.error.type( "integer", indicator.args[1], atom.indicator ) );
} else {
thread.session.multifile_predicates[atom.args[0].args[0].id + "/" + atom.args[0].args[1].value] = true;
}
},
// set_prolog_flag
"set_prolog_flag/2": function( thread, atom ) {
var flag = atom.args[0], value = atom.args[1];
if( pl.type.is_variable( flag ) || pl.type.is_variable( value ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( flag ) ) {
thread.throw_error( pl.error.type( "atom", flag, atom.indicator ) );
} else if( !pl.type.is_flag( flag ) ) {
thread.throw_error( pl.error.domain( "prolog_flag", flag, atom.indicator ) );
} else if( !pl.type.is_value_flag( flag, value ) ) {
thread.throw_error( pl.error.domain( "flag_value", new Term( "+", [flag, value] ), atom.indicator ) );
} else if( !pl.type.is_modifiable_flag( flag ) ) {
thread.throw_error( pl.error.permission( "modify", "flag", flag, atom.indicator ) );
} else {
thread.session.flag[flag.id] = value;
}
},
// use_module/1
"use_module/1": function( thread, atom ) {
var module = atom.args[0];
if( pl.type.is_variable( module ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_term( module ) ) {
thread.throw_error( pl.error.type( "term", module, atom.indicator ) );
} else {
if( pl.type.is_module( module ) ) {
var name = module.args[0].id;
if( indexOf(thread.session.modules, name) === -1 )
thread.session.modules.push( name );
} else {
// TODO
// error no existe modulo
}
}
},
// char_conversion/2
"char_conversion/2": function( thread, atom ) {
var inchar = atom.args[0], outchar = atom.args[1];
if( pl.type.is_variable( inchar ) || pl.type.is_variable( outchar ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_character( inchar ) ) {
thread.throw_error( pl.error.type( "character", inchar, atom.indicator ) );
} else if( !pl.type.is_character( outchar ) ) {
thread.throw_error( pl.error.type( "character", outchar, atom.indicator ) );
} else {
if( inchar.id === outchar.id ) {
delete thread.session.__char_conversion[inchar.id];
} else {
thread.session.__char_conversion[inchar.id] = outchar.id;
}
}
},
// op/3
"op/3": function( thread, atom ) {
var priority = atom.args[0], type = atom.args[1], operator = atom.args[2];
if( pl.type.is_variable( priority ) || pl.type.is_variable( type ) || pl.type.is_variable( operator ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_integer( priority ) ) {
thread.throw_error( pl.error.type( "integer", priority, atom.indicator ) );
} else if( !pl.type.is_atom( type ) ) {
thread.throw_error( pl.error.type( "atom", type, atom.indicator ) );
} else if( !pl.type.is_atom( operator ) ) {
thread.throw_error( pl.error.type( "atom", operator, atom.indicator ) );
} else if( priority.value < 0 || priority.value > 1200 ) {
thread.throw_error( pl.error.domain( "operator_priority", priority, atom.indicator ) );
} else if( operator.id === "," ) {
thread.throw_error( pl.error.permission( "modify", "operator", operator, atom.indicator ) );
} else if( operator.id === "|" && (priority.value < 1001 || type.id.length !== 3 ) ) {
thread.throw_error( pl.error.permission( "modify", "operator", operator, atom.indicator ) );
} else if( ["fy", "fx", "yf", "xf", "xfx", "yfx", "xfy"].indexOf( type.id ) === -1 ) {
thread.throw_error( pl.error.domain( "operator_specifier", type, atom.indicator ) );
} else {
var fix = { prefix: null, infix: null, postfix: null };
for( var p in thread.session.__operators ) {
if(!thread.session.__operators.hasOwnProperty(p)) continue;
var classes = thread.session.__operators[p][operator.id];
if( classes ) {
if( indexOf( classes, "fx" ) !== -1 ) { fix.prefix = { priority: p, type: "fx" }; }
if( indexOf( classes, "fy" ) !== -1 ) { fix.prefix = { priority: p, type: "fy" }; }
if( indexOf( classes, "xf" ) !== -1 ) { fix.postfix = { priority: p, type: "xf" }; }
if( indexOf( classes, "yf" ) !== -1 ) { fix.postfix = { priority: p, type: "yf" }; }
if( indexOf( classes, "xfx" ) !== -1 ) { fix.infix = { priority: p, type: "xfx" }; }
if( indexOf( classes, "xfy" ) !== -1 ) { fix.infix = { priority: p, type: "xfy" }; }
if( indexOf( classes, "yfx" ) !== -1 ) { fix.infix = { priority: p, type: "yfx" }; }
}
}
var current_class;
switch( type.id ) {
case "fy": case "fx": current_class = "prefix"; break;
case "yf": case "xf": current_class = "postfix"; break;
default: current_class = "infix"; break;
}
if( ((fix.prefix && current_class === "prefix" || fix.postfix && current_class === "postfix" || fix.infix && current_class === "infix")
&& fix[current_class].type !== type.id || fix.infix && current_class === "postfix" || fix.postfix && current_class === "infix") && priority.value !== 0 ) {
thread.throw_error( pl.error.permission( "create", "operator", operator, atom.indicator ) );
} else {
if( fix[current_class] ) {
remove( thread.session.__operators[fix[current_class].priority][operator.id], type.id );
if( thread.session.__operators[fix[current_class].priority][operator.id].length === 0 ) {
delete thread.session.__operators[fix[current_class].priority][operator.id];
}
}
if( priority.value > 0 ) {
if( !thread.session.__operators[priority.value] ) thread.session.__operators[priority.value.toString()] = {};
if( !thread.session.__operators[priority.value][operator.id] ) thread.session.__operators[priority.value][operator.id] = [];
thread.session.__operators[priority.value][operator.id].push( type.id );
}
return true;
}
}
}
},
// Built-in predicates
predicate: {
// TERM AND GOAL EXPANSION
"goal_expansion/2": [
new Rule(new Term("goal_expansion", [new Term(",", [new Var("X"),new Var("Y")]),new Term(",", [new Var("X_"),new Var("Y_")])]), new Term(";", [new Term(",", [new Term("goal_expansion", [new Var("X"),new Var("X_")]),new Term(";", [new Term("goal_expansion", [new Var("Y"),new Var("Y_")]),new Term("=", [new Var("Y_"),new Var("Y")])])]),new Term(",", [new Term("=", [new Var("X"),new Var("X_")]),new Term("goal_expansion", [new Var("Y"),new Var("Y_")])])])),
new Rule(new Term("goal_expansion", [new Term(";", [new Var("X"),new Var("Y")]),new Term(";", [new Var("X_"),new Var("Y_")])]), new Term(";", [new Term(",", [new Term("goal_expansion", [new Var("X"),new Var("X_")]),new Term(";", [new Term("goal_expansion", [new Var("Y"),new Var("Y_")]),new Term("=", [new Var("Y_"),new Var("Y")])])]),new Term(",", [new Term("=", [new Var("X"),new Var("X_")]),new Term("goal_expansion", [new Var("Y"),new Var("Y_")])])])),
new Rule(new Term("goal_expansion", [new Term("->", [new Var("X"),new Var("Y")]),new Term("->", [new Var("X_"),new Var("Y_")])]), new Term(";", [new Term(",", [new Term("goal_expansion", [new Var("X"),new Var("X_")]),new Term(";", [new Term("goal_expansion", [new Var("Y"),new Var("Y_")]),new Term("=", [new Var("Y_"),new Var("Y")])])]),new Term(",", [new Term("=", [new Var("X"),new Var("X_")]),new Term("goal_expansion", [new Var("Y"),new Var("Y_")])])])),
new Rule(new Term("goal_expansion", [new Term("catch", [new Var("X"),new Var("Y"),new Var("Z")]),new Term("catch", [new Var("X_"),new Var("Y"),new Var("Z_")])]), new Term(";", [new Term(",", [new Term("goal_expansion", [new Var("X"),new Var("X_")]),new Term(";", [new Term("goal_expansion", [new Var("Z"),new Var("Z_")]),new Term("=", [new Var("Z_"),new Var("Z")])])]),new Term(",", [new Term("=", [new Var("X_"),new Var("X")]),new Term("goal_expansion", [new Var("Z"),new Var("Z_")])])])),
new Rule(new Term("goal_expansion", [new Term("\\+", [new Var("X")]),new Term("\\+", [new Var("X_")])]), new Term(",", [new Term("nonvar", [new Var("X")]),new Term("goal_expansion", [new Var("X"),new Var("X_")])])),
new Rule(new Term("goal_expansion", [new Term("once", [new Var("X")]),new Term("once", [new Var("X_")])]), new Term(",", [new Term("nonvar", [new Var("X")]),new Term("goal_expansion", [new Var("X"),new Var("X_")])])),
new Rule(new Term("goal_expansion", [new Term("findall", [new Var("X"),new Var("Y"),new Var("Z")]),new Term("findall", [new Var("X"),new Var("Y_"),new Var("Z")])]), new Term("goal_expansion", [new Var("Y"),new Var("Y_")])),
new Rule(new Term("goal_expansion", [new Term("setof", [new Var("X"),new Var("Y"),new Var("Z")]),new Term("findall", [new Var("X"),new Var("Y_"),new Var("Z")])]), new Term("goal_expansion", [new Var("Y"),new Var("Y_")])),
new Rule(new Term("goal_expansion", [new Term("bagof", [new Var("X"),new Var("Y"),new Var("Z")]),new Term("findall", [new Var("X"),new Var("Y_"),new Var("Z")])]), new Term("goal_expansion", [new Var("Y"),new Var("Y_")])),
new Rule(new Term("goal_expansion", [new Term("call", [new Var("X")]),new Term("call", [new Var("X_")])]), new Term(",", [new Term("nonvar", [new Var("X")]),new Term("goal_expansion", [new Var("X"),new Var("X_")])])),
new Rule(new Term("goal_expansion", [new Term("call", [new Var("X"),new Var("A1")]),new Term("call", [new Var("F_")])]), new Term(",", [new Term("=..", [new Var("F"),new Term(".", [new Var("X"),new Term(".", [new Var("A1"),new Term("[]", [])])])]),new Term("goal_expansion", [new Var("F"),new Var("F_")])])),
new Rule(new Term("goal_expansion", [new Term("call", [new Var("X"),new Var("A1"),new Var("A2")]),new Term("call", [new Var("F_")])]), new Term(",", [new Term("=..", [new Var("F"),new Term(".", [new Var("X"),new Term(".", [new Var("A1"),new Term(".", [new Var("A2"),new Term("[]", [])])])])]),new Term("goal_expansion", [new Var("F"),new Var("F_")])])),
new Rule(new Term("goal_expansion", [new Term("call", [new Var("X"),new Var("A1"),new Var("A2"),new Var("A3")]),new Term("call", [new Var("F_")])]), new Term(",", [new Term("=..", [new Var("F"),new Term(".", [new Var("X"),new Term(".", [new Var("A1"),new Term(".", [new Var("A2"),new Term(".", [new Var("A3"),new Term("[]", [])])])])])]),new Term("goal_expansion", [new Var("F"),new Var("F_")])])),
new Rule(new Term("goal_expansion", [new Term("call", [new Var("X"),new Var("A1"),new Var("A2"),new Var("A3"),new Var("A4")]),new Term("call", [new Var("F_")])]), new Term(",", [new Term("=..", [new Var("F"),new Term(".", [new Var("X"),new Term(".", [new Var("A1"),new Term(".", [new Var("A2"),new Term(".", [new Var("A3"),new Term(".", [new Var("A4"),new Term("[]", [])])])])])])]),new Term("goal_expansion", [new Var("F"),new Var("F_")])])),
new Rule(new Term("goal_expansion", [new Term("call", [new Var("X"),new Var("A1"),new Var("A2"),new Var("A3"),new Var("A4"),new Var("A5")]),new Term("call", [new Var("F_")])]), new Term(",", [new Term("=..", [new Var("F"),new Term(".", [new Var("X"),new Term(".", [new Var("A1"),new Term(".", [new Var("A2"),new Term(".", [new Var("A3"),new Term(".", [new Var("A4"),new Term(".", [new Var("A5"),new Term("[]", [])])])])])])])]),new Term("goal_expansion", [new Var("F"),new Var("F_")])])),
new Rule(new Term("goal_expansion", [new Term("call", [new Var("X"),new Var("A1"),new Var("A2"),new Var("A3"),new Var("A4"),new Var("A5"),new Var("A6")]),new Term("call", [new Var("F_")])]), new Term(",", [new Term("=..", [new Var("F"),new Term(".", [new Var("X"),new Term(".", [new Var("A1"),new Term(".", [new Var("A2"),new Term(".", [new Var("A3"),new Term(".", [new Var("A4"),new Term(".", [new Var("A5"),new Term(".", [new Var("A6"),new Term("[]", [])])])])])])])])]),new Term("goal_expansion", [new Var("F"),new Var("F_")])])),
new Rule(new Term("goal_expansion", [new Term("call", [new Var("X"),new Var("A1"),new Var("A2"),new Var("A3"),new Var("A4"),new Var("A5"),new Var("A6"),new Var("A7")]),new Term("call", [new Var("F_")])]), new Term(",", [new Term("=..", [new Var("F"),new Term(".", [new Var("X"),new Term(".", [new Var("A1"),new Term(".", [new Var("A2"),new Term(".", [new Var("A3"),new Term(".", [new Var("A4"),new Term(".", [new Var("A5"),new Term(".", [new Var("A6"),new Term(".", [new Var("A7"),new Term("[]", [])])])])])])])])])]),new Term("goal_expansion", [new Var("F"),new Var("F_")])]))
],
// ATTRIBUTED VARIABLES
//put_attr/3
"put_attr/3": function( thread, point, atom ) {
var variable = atom.args[0], module = atom.args[1], value = atom.args[2];
if( !pl.type.is_variable(variable) ) {
thread.throw_error( pl.error.type( "variable", variable, atom.indicator ) );
} else if( !pl.type.is_atom(module) ) {
thread.throw_error( pl.error.type( "atom", module, atom.indicator ) );
} else {
var subs = point.substitution.set_attribute( variable.id, module, value );
thread.prepend( [new State( point.goal.replace(null), subs, point )] );
}
},
// get_attr/3
"get_attr/3": function( thread, point, atom ) {
var variable = atom.args[0], module = atom.args[1], value = atom.args[2];
if( !pl.type.is_variable(variable) ) {
thread.throw_error( pl.error.type( "variable", variable, atom.indicator ) );
} else if( !pl.type.is_atom(module) ) {
thread.throw_error( pl.error.type( "atom", module, atom.indicator ) );
} else {
var attr = point.substitution.get_attribute( variable.id, module );
if( attr ) {
thread.prepend( [new State(
point.goal.replace( new Term("=", [value, attr]) ),
point.substitution,
point
)] );
}
}
},
// INPUT AND OUTPUT
// op/3
"op/3": function( thread, point, atom ) {
if( pl.directive["op/3"]( thread, atom ) )
thread.success( point );
},
// current_op/3
"current_op/3": function( thread, point, atom ) {
var priority = atom.args[0], specifier = atom.args[1], operator = atom.args[2];
var points = [];
for( var p in thread.session.__operators )
for( var o in thread.session.__operators[p] )
for( var i = 0; i < thread.session.__operators[p][o].length; i++ )
points.push( new State(
point.goal.replace(
new Term( ",", [
new Term( "=", [new Num( p, false ), priority] ),
new Term( ",", [
new Term( "=", [new Term( thread.session.__operators[p][o][i], [] ), specifier] ),
new Term( "=", [new Term( o, [] ), operator] )
] )
] )
),
point.substitution,
point
) );
thread.prepend( points );
},
// LOGIC AND CONTROL STRUCTURES
// ;/2 (disjunction)
";/2": function( thread, point, atom ) {
var left = atom.args[0], right = atom.args[1];
if( pl.type.is_term( left ) && left.indicator === "->/2" ) {
var cond = left.args[0], then = left.args[1], otherwise = right;
var goal_fst = point.goal.replace( new Term( ",", [cond, new Term( ",", [new Term( "!" ), then] )] ) );
var goal_snd = point.goal.replace( new Term( ",", [new Term( "!" ), otherwise] ) );
thread.prepend( [
new State( goal_fst, point.substitution, point ),
new State( goal_snd, point.substitution, point )
] );
} else {
thread.prepend([
new State( point.goal.replace( left ), point.substitution, point ),
new State( point.goal.replace( right ), point.substitution, point )
]);
}
},
// !/0 (cut)
"!/0": function( thread, point, atom ) {
var parent_cut, last_cut, states = [];
parent_cut = point;
last_cut = null;
while( parent_cut.parent !== null && parent_cut.parent.goal.search( atom ) ) {
last_cut = parent_cut;
parent_cut = parent_cut.parent;
if(parent_cut.goal !== null) {
var selected = parent_cut.goal.select();
if( selected && selected.id === "call" && selected.search(atom) ) {
parent_cut = last_cut;
break;
}
}
}
for( var i = thread.points.length-1; i >= 0; i-- ) {
var state = thread.points[i];
var node = state.parent;
while( node !== null && node !== parent_cut.parent ) {
node = node.parent;
}
if( node === null && node !== parent_cut.parent )
states.push( state );
}
thread.points = states.reverse();
thread.success( point );
},
// \+ (negation)
"\\+/1": function( thread, point, atom ) {
var goal = atom.args[0];
if( pl.type.is_variable( goal ) ) {
thread.throw_error( pl.error.instantiation( thread.level ) );
} else if( !pl.type.is_callable( goal ) ) {
thread.throw_error( pl.error.type( "callable", goal, thread.level ) );
} else {
// TRANSPARENT VERSION OF THE NEGATION
/*var neg_thread;
if(point.negation_thread) {
neg_thread = point.negation_thread;
} else {
neg_thread = new Thread( thread.session );
neg_thread.add_goal( goal );
point.negation_thread = neg_thread;
}
neg_thread.answer( function( answer ) {
if(answer === false) {
thread.success( point );
} else if(pl.type.is_error( answer )) {
thread.throw_error( answer.args[0] );
} else if(answer === null) {
thread.prepend( [point] );
thread.current_limit = 0;
}
thread.again( answer !== null );
} );
return true;*/
// '\+'(X) :- call(X), !, fail.
// '\+'(_).
thread.prepend( [
new State( point.goal.replace( new Term( ",", [new Term( ",", [ new Term( "call", [goal] ), new Term( "!", [] ) ] ), new Term( "fail", [] ) ] ) ), point.substitution, point ),
new State( point.goal.replace( null ), point.substitution, point )
] );
}
},
// ->/2 (implication)
"->/2": function( thread, point, atom ) {
var cond = atom.args[0], then = atom.args[1];
var goal = point.goal.replace( new Term( ",", [cond, new Term( ",", [new Term( "!" ), then] )] ) );
thread.prepend( [new State( goal, point.substitution, point )] );
},
// fail/0
"fail/0": function( _1, _2, _3 ) {},
// false/0
"false/0": function( _1, _2, _3 ) {},
// true/0
"true/0": function( thread, point, _ ) {
thread.success( point );
},
// call/1..8
"call/1": callN(1),
"call/2": callN(2),
"call/3": callN(3),
"call/4": callN(4),
"call/5": callN(5),
"call/6": callN(6),
"call/7": callN(7),
"call/8": callN(8),
// once/1
"once/1": function( thread, point, atom ) {
var goal = atom.args[0];
thread.prepend( [new State( point.goal.replace( new Term( ",", [new Term( "call", [goal] ), new Term( "!", [] )] ) ), point.substitution, point )] );
},
// forall/2
"forall/2": function( thread, point, atom ) {
var generate = atom.args[0], test = atom.args[1];
thread.prepend( [new State( point.goal.replace( new Term( "\\+", [new Term( ",", [new Term( "call", [generate] ), new Term( "\\+", [new Term( "call", [test] )] )] )] ) ), point.substitution, point )] );
},
// repeat/0
"repeat/0": function( thread, point, _ ) {
thread.prepend( [new State( point.goal.replace( null ), point.substitution, point ), point] );
},
// EXCEPTIONS
// throw/1
"throw/1": function( thread, point, atom ) {
if( pl.type.is_variable( atom.args[0] ) ) {
thread.throw_error( pl.error.instantiation( thread.level ) );
} else {
thread.throw_error( atom.args[0] );
}
},
// catch/3
"catch/3": function( thread, point, atom ) {
var points = thread.points;
thread.points = [];
thread.prepend( [new State( atom.args[0], point.substitution, point )] );
var format_success = thread.session.format_success;
var format_error = thread.session.format_error;
thread.session.format_success = function(x) { return x.substitution; };
thread.session.format_error = function(x) { return x.goal; };
var callback = function( answer ) {
var call_points = thread.points;
thread.points = points;
thread.session.format_success = format_success;
thread.session.format_error = format_error;
if( pl.type.is_error( answer ) ) {
var states = [];
for( var i = thread.points.length-1 ; i >= 0; i-- ) {
var state = thread.points[i];
var node = state.parent;
while( node !== null && node !== point.parent ) {
node = node.parent;
}
if( node === null && node !== point.parent )
states.push( state );
}
thread.points = states;
var occurs_check = thread.get_flag( "occurs_check" ).indicator === "true/0";
var state = new State();
var mgu = pl.unify( answer.args[0], atom.args[1], occurs_check );
if( mgu !== null ) {
state.substitution = point.substitution.apply( mgu );
state.goal = point.goal.replace( atom.args[2] ).apply( mgu );
state.parent = point;
thread.prepend( [state] );
} else {
thread.throw_error( answer.args[0] );
}
} else if( answer !== false ) {
var answer_state = answer === null ? [] : [new State(
point.goal.apply( answer ).replace( null ),
point.substitution.apply( answer ),
point
)];
var filter_points = [];
for( var i = call_points.length-1; i >= 0; i-- ) {
filter_points.push( call_points[i] );
var selected = call_points[i].goal !== null ? call_points[i].goal.select() : null;
if( pl.type.is_term( selected ) && selected.indicator === "!/0" )
break;
}
var catch_points = map( filter_points, function( state ) {
if( state.goal === null )
state.goal = new Term( "true", [] );
state = new State(
point.goal.replace( new Term( "catch", [state.goal, atom.args[1], atom.args[2]] ) ),
point.substitution.apply( state.substitution ),
state.parent
);
state.exclude = atom.args[0].variables();
return state;
} ).reverse();
thread.prepend( catch_points );
thread.prepend( answer_state );
if( answer === null ) {
this.current_limit = 0;
thread.__calls.shift()( null );
}
}
};
thread.__calls.unshift( callback );
},
// UNIFICATION
// =/2 (unification)
"=/2": function( thread, point, atom ) {
var occurs_check = thread.get_flag( "occurs_check" ).indicator === "true/0";
var state = new State();
var mgu = pl.unify( atom.args[0], atom.args[1], occurs_check );
if( mgu !== null ) {
state.goal = point.goal.apply( mgu ).replace( null );
state.substitution = point.substitution.apply( mgu );
state.parent = point;
thread.prepend( [state] );
}
},
// unify_with_occurs_check/2
"unify_with_occurs_check/2": function( thread, point, atom ) {
var state = new State();
var mgu = pl.unify( atom.args[0], atom.args[1], true );
if( mgu !== null ) {
state.goal = point.goal.apply( mgu ).replace( null );
state.substitution = point.substitution.apply( mgu );
state.parent = point;
thread.prepend( [state] );
}
},
// \=/2
"\\=/2": function( thread, point, atom ) {
var occurs_check = thread.get_flag( "occurs_check" ).indicator === "true/0";
var mgu = pl.unify( atom.args[0], atom.args[1], occurs_check );
if( mgu === null ) {
thread.success( point );
}
},
// subsumes_term/2
"subsumes_term/2": function( thread, point, atom ) {
var occurs_check = thread.get_flag( "occurs_check" ).indicator === "true/0";
var mgu = pl.unify( atom.args[1], atom.args[0], occurs_check );
if( mgu !== null && atom.args[1].apply( mgu ).equals( atom.args[1] ) ) {
thread.success( point );
}
},
// ALL SOLUTIONS
// findall/3
"findall/3": function( thread, point, atom ) {
var template = atom.args[0], goal = atom.args[1], instances = atom.args[2];
if( pl.type.is_variable( goal ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_callable( goal ) ) {
thread.throw_error( pl.error.type( "callable", goal, atom.indicator ) );
} else if( !pl.type.is_variable( instances ) && !pl.type.is_list( instances ) ) {
thread.throw_error( pl.error.type( "list", instances, atom.indicator ) );
} else {
var variable = thread.next_free_variable();
var newGoal = new Term( ",", [goal, new Term( "=", [variable, template] )] );
var nthread = new Thread(thread.session);
nthread.debugger = thread.debugger;
nthread.format_success = function(state) { return state.substitution; };
nthread.format_error = function(state) { return state.goal; };
nthread.add_goal( newGoal, true, point );
nthread.head_point().parent = point;
var answers = [];
var callback = function( answer ) {
if( answer !== false && answer !== null && !pl.type.is_error( answer ) ) {
answers.push( answer.links[variable.id] );
nthread.answer(callback);
} else {
var reset_limit = true;
if( pl.type.is_error( answer ) ) {
thread.throw_error( answer.args[0] );
} else if( nthread.current_limit > 0 ) {
var list = arrayToList(answers);
thread.prepend( [new State(
point.goal.replace( new Term( "=", [instances, list] ) ),
point.substitution,
point
)] );
} else {
thread.prepend( [point] );
thread.current_limit = 0;
reset_limit = false;
}
if(reset_limit && nthread.debugger)
thread.debugger_states = thread.debugger_states.concat(nthread.debugger_states);
thread.again(reset_limit);
}
};
nthread.answer(callback);
return true;
}
},
// bagof/3
"bagof/3": function( thread, point, atom ) {
var answer, template = atom.args[0], goal = atom.args[1], instances = atom.args[2];
if( pl.type.is_variable( goal ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_callable( goal ) ) {
thread.throw_error( pl.error.type( "callable", goal, atom.indicator ) );
} else if( !pl.type.is_variable( instances ) && !pl.type.is_list( instances ) ) {
thread.throw_error( pl.error.type( "list", instances, atom.indicator ) );
} else {
var variable = thread.next_free_variable();
var template_vars;
if( goal.indicator === "^/2" ) {
template_vars = goal.args[0].variables();
goal = goal.args[1];
} else {
template_vars = [];
}
template_vars = template_vars.concat( template.variables() );
var free_vars = goal.variables().filter( function( v ){
return indexOf( template_vars, v ) === -1;
} );
var list_vars = new Term( "[]" );
for( var i = free_vars.length - 1; i >= 0; i-- ) {
list_vars = new Term( ".", [ new Var( free_vars[i] ), list_vars ] );
}
var newGoal = new Term( ",", [goal, new Term( "=", [variable, new Term( ",", [list_vars, template] )] )] );
var nthread = new Thread(thread.session);
nthread.debugger = thread.debugger;
nthread.format_success = function(state) { return state.substitution; };
nthread.format_error = function(state) { return state.goal; };
nthread.add_goal( newGoal, true, point );
nthread.head_point().parent = point;
var answers = [];
var callback = function( answer ) {
if( answer !== false && answer !== null && !pl.type.is_error( answer ) ) {
var match = false;
var arg_vars = answer.links[variable.id].args[0];
var arg_template = answer.links[variable.id].args[1];
for( var _elem in answers ) {
if(!answers.hasOwnProperty(_elem)) continue;
var elem = answers[_elem];
if( elem.variables.equals( arg_vars ) ) {
elem.answers.push( arg_template );
match = true;
break;
}
}
if( !match )
answers.push( {variables: arg_vars, answers: [arg_template]} );
nthread.answer(callback);
} else {
reset_limit = true;
if( pl.type.is_error( answer ) ) {
thread.throw_error( answer.args[0] );
} else if( thread.current_limit > 0 ) {
var states = [];
for( var i = 0; i < answers.length; i++ ) {
answer = answers[i].answers;
var list = arrayToList(answer);
states.push( new State(
point.goal.replace( new Term( ",", [new Term( "=", [list_vars, answers[i].variables] ), new Term( "=", [instances, list] )] ) ),
point.substitution,
point
) );
}
thread.prepend( states );
} else {
thread.prepend( [point] );
thread.current_limit = 0;
reset_limit = false;
}
if(reset_limit && nthread.debugger)
thread.debugger_states = thread.debugger_states.concat(nthread.debugger_states);
thread.again(reset_limit);
}
};
nthread.answer(callback);
return true;
}
},
// setof/3
"setof/3": function( thread, point, atom ) {
var answer, template = atom.args[0], goal = atom.args[1], instances = atom.args[2];
if( pl.type.is_variable( goal ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_callable( goal ) ) {
thread.throw_error( pl.error.type( "callable", goal, atom.indicator ) );
} else if( !pl.type.is_variable( instances ) && !pl.type.is_list( instances ) ) {
thread.throw_error( pl.error.type( "list", instances, atom.indicator ) );
} else {
var variable = thread.next_free_variable();
var template_vars;
if( goal.indicator === "^/2" ) {
template_vars = goal.args[0].variables();
goal = goal.args[1];
} else {
template_vars = [];
}
template_vars = template_vars.concat( template.variables() );
var free_vars = goal.variables().filter( function( v ){
return indexOf( template_vars, v ) === -1;
} );
var list_vars = new Term( "[]" );
for( var i = free_vars.length - 1; i >= 0; i-- ) {
list_vars = new Term( ".", [ new Var( free_vars[i] ), list_vars ] );
}
var newGoal = new Term( ",", [goal, new Term( "=", [variable, new Term( ",", [list_vars, template] )] )] );
var nthread = new Thread(thread.session);
nthread.debugger = thread.debugger;
nthread.format_success = function(state) { return state.substitution; };
nthread.format_error = function(state) { return state.goal; };
nthread.add_goal( newGoal, true, point );
nthread.head_point().parent = point;
var answers = [];
var callback = function( answer ) {
if( answer !== false && answer !== null && !pl.type.is_error( answer ) ) {
var match = false;
var arg_vars = answer.links[variable.id].args[0];
var arg_template = answer.links[variable.id].args[1];
for( var _elem in answers ) {
if(!answers.hasOwnProperty(_elem)) continue;
var elem = answers[_elem];
if( elem.variables.equals( arg_vars ) ) {
elem.answers.push( arg_template );
match = true;
break;
}
}
if( !match )
answers.push( {variables: arg_vars, answers: [arg_template]} );
nthread.answer(callback);
} else {
reset_limit = true;
if( pl.type.is_error( answer ) ) {
thread.throw_error( answer.args[0] );
} else if( thread.current_limit > 0 ) {
var states = [];
for( var i = 0; i < answers.length; i++ ) {
answer = answers[i].answers.sort( pl.compare );
var list = arrayToList(answer);
states.push( new State(
point.goal.replace( new Term( ",", [new Term( "=", [list_vars, answers[i].variables] ), new Term( "=", [instances, list] )] ) ),
point.substitution,
point
) );
}
thread.prepend( states );
} else {
thread.prepend( [point] );
thread.current_limit = 0;
reset_limit = false;
}
if(reset_limit && nthread.debugger)
thread.debugger_states = thread.debugger_states.concat(nthread.debugger_states);
thread.again(reset_limit);
}
};
nthread.answer(callback);
return true;
}
},
// TERM CREATION AND DECOMPOSITION
// functor/3
"functor/3": function( thread, point, atom ) {
var subs;
var term = atom.args[0], name = atom.args[1], arity = atom.args[2];
if( pl.type.is_variable( term ) && (pl.type.is_variable( name ) || pl.type.is_variable( arity )) ) {
thread.throw_error( pl.error.instantiation( "functor/3" ) );
} else if( !pl.type.is_variable( arity ) && !pl.type.is_integer( arity ) ) {
thread.throw_error( pl.error.type( "integer", atom.args[2], "functor/3" ) );
} else if( !pl.type.is_variable( name ) && !pl.type.is_atomic( name ) ) {
thread.throw_error( pl.error.type( "atomic", atom.args[1], "functor/3" ) );
} else if( pl.type.is_integer( name ) && pl.type.is_integer( arity ) && arity.value !== 0 ) {
thread.throw_error( pl.error.type( "atom", atom.args[1], "functor/3" ) );
} else if( pl.type.is_variable( term ) ) {
if( atom.args[2].value >= 0 ) {
var args = [];
for( var i = 0; i < arity.value; i++ )
args.push( thread.next_free_variable() );
var functor = pl.type.is_integer( name ) ? name : new Term( name.id, args );
thread.prepend( [new State( point.goal.replace( new Term( "=", [term, functor] ) ), point.substitution, point )] );
}
} else {
var id = pl.type.is_integer( term ) ? term : new Term( term.id, [] );
var length = pl.type.is_integer( term ) ? new Num( 0, false ) : new Num( term.args.length, false );
var goal = new Term( ",", [new Term( "=", [id, name] ), new Term( "=", [length, arity] )] );
thread.prepend( [new State( point.goal.replace( goal ), point.substitution, point )] );
}
},
// arg/3
"arg/3": function( thread, point, atom ) {
if( pl.type.is_variable( atom.args[0] ) || pl.type.is_variable( atom.args[1] ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( atom.args[0].value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", atom.args[0], atom.indicator ) );
} else if( !pl.type.is_compound( atom.args[1] ) ) {
thread.throw_error( pl.error.type( "compound", atom.args[1], atom.indicator ) );
} else {
var n = atom.args[0].value;
if( n > 0 && n <= atom.args[1].args.length ) {
var goal = new Term( "=", [atom.args[1].args[n-1], atom.args[2]] );
thread.prepend( [new State( point.goal.replace( goal ), point.substitution, point )] );
}
}
},
// =../2 (univ)
"=../2": function( thread, point, atom ) {
var list;
if( pl.type.is_variable( atom.args[0] ) && (pl.type.is_variable( atom.args[1] )
|| pl.type.is_non_empty_list( atom.args[1] ) && pl.type.is_variable( atom.args[1].args[0] )) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_fully_list( atom.args[1] ) ) {
thread.throw_error( pl.error.type( "list", atom.args[1], atom.indicator ) );
} else if( !pl.type.is_variable( atom.args[0] ) ) {
if( pl.type.is_atomic( atom.args[0] ) ) {
list = new Term( ".", [atom.args[0], new Term( "[]" )] );
} else {
list = new Term( "[]" );
for( var i = atom.args[0].args.length - 1; i >= 0; i-- ) {
list = new Term( ".", [atom.args[0].args[i], list] );
}
list = new Term( ".", [new Term( atom.args[0].id ), list] );
}
thread.prepend( [new State( point.goal.replace( new Term( "=", [list, atom.args[1]] ) ), point.substitution, point )] );
} else if( !pl.type.is_variable( atom.args[1] ) ) {
var args = [];
list = atom.args[1].args[1];
while( list.indicator === "./2" ) {
args.push( list.args[0] );
list = list.args[1];
}
if( pl.type.is_variable( atom.args[0] ) && pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( args.length === 0 && pl.type.is_compound( atom.args[1].args[0] ) ) {
thread.throw_error( pl.error.type( "atomic", atom.args[1].args[0], atom.indicator ) );
} else if( args.length > 0 && (pl.type.is_compound( atom.args[1].args[0] ) || pl.type.is_number( atom.args[1].args[0] )) ) {
thread.throw_error( pl.error.type( "atom", atom.args[1].args[0], atom.indicator ) );
} else {
if( args.length === 0 ) {
thread.prepend( [new State( point.goal.replace( new Term( "=", [atom.args[1].args[0], atom.args[0]], point ) ), point.substitution, point )] );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [new Term( atom.args[1].args[0].id, args ), atom.args[0]] ) ), point.substitution, point )] );
}
}
}
},
// copy_term/2
"copy_term/2": function( thread, point, atom ) {
var renamed = atom.args[0].rename( thread );
thread.prepend( [new State( point.goal.replace( new Term( "=", [renamed, atom.args[1]] ) ), point.substitution, point.parent )] );
},
// term_variables/2
"term_variables/2": function( thread, point, atom ) {
var term = atom.args[0], vars = atom.args[1];
if( !pl.type.is_fully_list( vars ) ) {
thread.throw_error( pl.error.type( "list", vars, atom.indicator ) );
} else {
var list = arrayToList( map( nub( term.variables() ), function(v) {
return new Var(v);
} ) );
thread.prepend( [new State( point.goal.replace( new Term( "=", [vars, list] ) ), point.substitution, point )] );
}
},
// CLAUSE RETRIEVAL AND INFORMATION
// clause/2
"clause/2": function( thread, point, atom ) {
if( pl.type.is_variable( atom.args[0] ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_callable( atom.args[0] ) ) {
thread.throw_error( pl.error.type( "callable", atom.args[0], atom.indicator ) );
} else if( !pl.type.is_variable( atom.args[1] ) && !pl.type.is_callable( atom.args[1] ) ) {
thread.throw_error( pl.error.type( "callable", atom.args[1], atom.indicator ) );
} else if( thread.session.rules[atom.args[0].indicator] !== undefined ) {
if( thread.is_public_predicate( atom.args[0].indicator ) ) {
var states = [];
for( var _rule in thread.session.rules[atom.args[0].indicator] ) {
if(!thread.session.rules[atom.args[0].indicator].hasOwnProperty(_rule)) continue;
var rule = thread.session.rules[atom.args[0].indicator][_rule];
thread.session.renamed_variables = {};
rule = rule.rename( thread );
if( rule.body === null ) {
rule.body = new Term( "true" );
}
var goal = new Term( ",", [new Term( "=", [rule.head, atom.args[0]] ), new Term( "=", [rule.body, atom.args[1]] )] );
states.push( new State( point.goal.replace( goal ), point.substitution, point ) );
}
thread.prepend( states );
} else {
thread.throw_error( pl.error.permission( "access", "private_procedure", atom.args[0].indicator, atom.indicator ) );
}
}
},
// current_predicate/1
"current_predicate/1": function( thread, point, atom ) {
var indicator = atom.args[0];
if( !pl.type.is_variable( indicator ) && (!pl.type.is_compound( indicator ) || indicator.indicator !== "//2") ) {
thread.throw_error( pl.error.type( "predicate_indicator", indicator, atom.indicator ) );
} else if( !pl.type.is_variable( indicator ) && !pl.type.is_variable( indicator.args[0] ) && !pl.type.is_atom( indicator.args[0] ) ) {
thread.throw_error( pl.error.type( "atom", indicator.args[0], atom.indicator ) );
} else if( !pl.type.is_variable( indicator ) && !pl.type.is_variable( indicator.args[1] ) && !pl.type.is_integer( indicator.args[1] ) ) {
thread.throw_error( pl.error.type( "integer", indicator.args[1], atom.indicator ) );
} else {
var states = [];
for( var i in thread.session.rules ) {
if(!thread.session.rules.hasOwnProperty(i)) continue;
var index = i.lastIndexOf( "/" );
var name = i.substr( 0, index );
var arity = parseInt( i.substr( index+1, i.length-(index+1) ) );
var predicate = new Term( "/", [new Term( name ), new Num( arity, false )] );
var goal = new Term( "=", [predicate, indicator] );
states.push( new State( point.goal.replace( goal ), point.substitution, point ) );
}
thread.prepend( states );
}
},
// listing/0
"listing/0": function( thread, point, atom ) {
var from_module = atom.from_module ? atom.from_module : "user";
var rules;
if(from_module === "user") {
rules = thread.session.rules;
} else {
if(pl.module[from_module])
rules = pl.module[from_module].rules;
else
rules = {};
}
var str = "";
for(var indicator in rules) {
if(!rules.hasOwnProperty(indicator)) continue;
var predicate = rules[indicator];
str += "% " + indicator + "\n";
if(predicate instanceof Array) {
for(var i = 0; i < predicate.length; i++)
str += predicate[i].toString( {session: thread.session} ) + "\n";
} else {
str += "/*\n" + predicate.toString() + "\n*/";
}
str += "\n";
}
// @blab+
thread.session.result=str;
if (!thread.session.silent) thread.prepend( [new State(
point.goal.replace(new Term("write", [new Term(str, [])])),
point.substitution,
point
)] );
},
// listing/1
"listing/1": function( thread, point, atom ) {
var indicator = atom.args[0];
if(pl.type.is_variable(indicator)) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if(!pl.type.is_predicate_indicator(indicator)) {
thread.throw_error( pl.error.type( "predicate_indicator", indicator, atom.indicator ) );
} else {
var from_module = atom.from_module ? atom.from_module : "user";
var rules;
if(from_module === "user") {
rules = thread.session.rules;
} else {
if(pl.module[from_module])
rules = pl.module[from_module].rules;
else
rules = {};
}
var str = "";
var str_indicator = indicator.args[0].id + "/" + indicator.args[1].value;
if(rules.hasOwnProperty(str_indicator)) {
var predicate = rules[str_indicator];
if(predicate instanceof Array) {
for(var i = 0; i < predicate.length; i++)
str += predicate[i].toString( {session: thread.session} ) + "\n";
} else {
str += "/*\n" + predicate.toString() + "\n*/";
}
str += "\n";
}
// @blab+
thread.session.result=str;
if (!thread.session.silent) thread.prepend( [new State(
point.goal.replace(new Term("write", [new Term(str, [])])),
point.substitution,
point
)] );
}
},
// CLAUSE CREATION AND DESTRUCTION
// asserta/1
"asserta/1": function( thread, point, atom ) {
if( pl.type.is_variable( atom.args[0] ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_callable( atom.args[0] ) ) {
thread.throw_error( pl.error.type( "callable", atom.args[0], atom.indicator ) );
} else {
var head, body;
if( atom.args[0].indicator === ":-/2" ) {
head = atom.args[0].args[0];
body = body_conversion( atom.args[0].args[1] );
} else {
head = atom.args[0];
body = null;
}
if( !pl.type.is_callable( head ) ) {
thread.throw_error( pl.error.type( "callable", head, atom.indicator ) );
} else if( body !== null && !pl.type.is_callable( body ) ) {
thread.throw_error( pl.error.type( "callable", body, atom.indicator ) );
} else if( thread.is_public_predicate( head.indicator ) ) {
if( thread.session.rules[head.indicator] === undefined ) {
thread.session.rules[head.indicator] = [];
}
thread.session.public_predicates[head.indicator] = true;
thread.session.rules[head.indicator] = [new Rule( head, body, true )].concat( thread.session.rules[head.indicator] );
thread.success( point );
} else {
thread.throw_error( pl.error.permission( "modify", "static_procedure", head.indicator, atom.indicator ) );
}
}
},
// assertz/1
"assertz/1": function( thread, point, atom ) {
if( pl.type.is_variable( atom.args[0] ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_callable( atom.args[0] ) ) {
thread.throw_error( pl.error.type( "callable", atom.args[0], atom.indicator ) );
} else {
var head, body;
if( atom.args[0].indicator === ":-/2" ) {
head = atom.args[0].args[0];
body = body_conversion( atom.args[0].args[1] );
} else {
head = atom.args[0];
body = null;
}
if( !pl.type.is_callable( head ) ) {
thread.throw_error( pl.error.type( "callable", head, atom.indicator ) );
} else if( body !== null && !pl.type.is_callable( body ) ) {
thread.throw_error( pl.error.type( "callable", body, atom.indicator ) );
} else if( thread.is_public_predicate( head.indicator ) ) {
if( thread.session.rules[head.indicator] === undefined ) {
thread.session.rules[head.indicator] = [];
}
thread.session.public_predicates[head.indicator] = true;
thread.session.rules[head.indicator].push( new Rule( head, body, true ) );
thread.success( point );
} else {
thread.throw_error( pl.error.permission( "modify", "static_procedure", head.indicator, atom.indicator ) );
}
}
},
// retract/1
"retract/1": function( thread, point, atom ) {
if( pl.type.is_variable( atom.args[0] ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_callable( atom.args[0] ) ) {
thread.throw_error( pl.error.type( "callable", atom.args[0], atom.indicator ) );
} else {
var head, body;
if( atom.args[0].indicator === ":-/2" ) {
head = atom.args[0].args[0];
body = atom.args[0].args[1];
} else {
head = atom.args[0];
body = new Term( "true" );
}
if( typeof point.retract === "undefined" ) {
if( thread.is_public_predicate( head.indicator ) ) {
if( thread.session.rules[head.indicator] !== undefined ) {
var states = [];
for( var i = 0; i < thread.session.rules[head.indicator].length; i++ ) {
thread.session.renamed_variables = {};
var orule = thread.session.rules[head.indicator][i];
var rule = orule.rename( thread );
if( rule.body === null )
rule.body = new Term( "true", [] );
var occurs_check = thread.get_flag( "occurs_check" ).indicator === "true/0";
var mgu = pl.unify( new Term( ",", [head, body] ), new Term( ",", [rule.head, rule.body] ), occurs_check );
if( mgu !== null ) {
var state = new State( point.goal.replace( new Term(",", [
new Term( "retract", [ new Term( ":-", [head, body] ) ] ),
new Term( ",", [
new Term( "=", [head, rule.head] ),
new Term( "=", [body, rule.body] )
] )
] ) ), point.substitution, point );
state.retract = orule;
states.push( state );
}
}
thread.prepend( states );
}
} else {
thread.throw_error( pl.error.permission( "modify", "static_procedure", head.indicator, atom.indicator ) );
}
} else {
retract( thread, point, head.indicator, point.retract );
}
}
},
// retractall/1
"retractall/1": function( thread, point, atom ) {
var head = atom.args[0];
if( pl.type.is_variable( head ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_callable( head ) ) {
thread.throw_error( pl.error.type( "callable", head, atom.indicator ) );
} else {
thread.prepend( [
new State( point.goal.replace( new Term( ",", [
new Term( "retract", [new pl.type.Term( ":-", [head, new Var( "_" )] )] ),
new Term( "fail", [] )
] ) ), point.substitution, point ),
new State( point.goal.replace( null ), point.substitution, point )
] );
}
},
// abolish/1
"abolish/1": function( thread, point, atom ) {
if( pl.type.is_variable( atom.args[0] ) || pl.type.is_term( atom.args[0] ) && atom.args[0].indicator === "//2"
&& (pl.type.is_variable( atom.args[0].args[0] ) || pl.type.is_variable( atom.args[0].args[1] )) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_term( atom.args[0] ) || atom.args[0].indicator !== "//2" ) {
thread.throw_error( pl.error.type( "predicate_indicator", atom.args[0], atom.indicator ) );
} else if( !pl.type.is_atom( atom.args[0].args[0] ) ) {
thread.throw_error( pl.error.type( "atom", atom.args[0].args[0], atom.indicator ) );
} else if( !pl.type.is_integer( atom.args[0].args[1] ) ) {
thread.throw_error( pl.error.type( "integer", atom.args[0].args[1], atom.indicator ) );
} else if( atom.args[0].args[1].value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", atom.args[0].args[1], atom.indicator ) );
} else if( pl.type.is_number(thread.get_flag( "max_arity" )) && atom.args[0].args[1].value > thread.get_flag( "max_arity" ).value ) {
thread.throw_error( pl.error.representation( "max_arity", atom.indicator ) );
} else {
var indicator = atom.args[0].args[0].id + "/" + atom.args[0].args[1].value;
if( thread.is_public_predicate( indicator ) ) {
delete thread.session.rules[indicator];
thread.success( point );
} else {
thread.throw_error( pl.error.permission( "modify", "static_procedure", indicator, atom.indicator ) );
}
}
},
// ATOM PROCESSING
// atom_length/2
"atom_length/2": function( thread, point, atom ) {
if( pl.type.is_variable( atom.args[0] ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( atom.args[0] ) ) {
thread.throw_error( pl.error.type( "atom", atom.args[0], atom.indicator ) );
} else if( !pl.type.is_variable( atom.args[1] ) && !pl.type.is_integer( atom.args[1] ) ) {
thread.throw_error( pl.error.type( "integer", atom.args[1], atom.indicator ) );
} else if( pl.type.is_integer( atom.args[1] ) && atom.args[1].value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", atom.args[1], atom.indicator ) );
} else {
var length = new Num( stringLength(atom.args[0].id), false );
thread.prepend( [new State( point.goal.replace( new Term( "=", [length, atom.args[1]] ) ), point.substitution, point )] );
}
},
// atom_concat/3
"atom_concat/3": function( thread, point, atom ) {
var str, goal, start = atom.args[0], end = atom.args[1], whole = atom.args[2];
if( pl.type.is_variable( whole ) && (pl.type.is_variable( start ) || pl.type.is_variable( end )) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( start ) && !pl.type.is_atom( start ) ) {
thread.throw_error( pl.error.type( "atom", start, atom.indicator ) );
} else if( !pl.type.is_variable( end ) && !pl.type.is_atom( end ) ) {
thread.throw_error( pl.error.type( "atom", end, atom.indicator ) );
} else if( !pl.type.is_variable( whole ) && !pl.type.is_atom( whole ) ) {
thread.throw_error( pl.error.type( "atom", whole, atom.indicator ) );
} else {
var v1 = pl.type.is_variable( start );
var v2 = pl.type.is_variable( end );
//var v3 = pl.type.is_variable( whole );
if( !v1 && !v2 ) {
goal = new Term( "=", [whole, new Term( start.id + end.id )] );
thread.prepend( [new State( point.goal.replace( goal ), point.substitution, point )] );
} else if( v1 && !v2 ) {
str = whole.id.substr( 0, whole.id.length - end.id.length );
if( str + end.id === whole.id ) {
goal = new Term( "=", [start, new Term( str )] );
thread.prepend( [new State( point.goal.replace( goal ), point.substitution, point )] );
}
} else if( v2 && !v1 ) {
str = whole.id.substr( start.id.length );
if( start.id + str === whole.id ) {
goal = new Term( "=", [end, new Term( str )] );
thread.prepend( [new State( point.goal.replace( goal ), point.substitution, point )] );
}
} else {
var states = [];
for( var i = 0; i <= whole.id.length; i++ ) {
var atom1 = new Term( whole.id.substr( 0, i ) );
var atom2 = new Term( whole.id.substr( i ) );
goal = new Term( ",", [new Term( "=", [atom1, start] ), new Term( "=", [atom2, end] )] );
states.push( new State( point.goal.replace( goal ), point.substitution, point ) );
}
thread.prepend( states );
}
}
},
// sub_atom/5
"sub_atom/5": function( thread, point, atom ) {
var i, atom1 = atom.args[0], before = atom.args[1], length = atom.args[2], after = atom.args[3], subatom = atom.args[4];
if( pl.type.is_variable( atom1 ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( before ) && !pl.type.is_integer( before ) ) {
thread.throw_error( pl.error.type( "integer", before, atom.indicator ) );
} else if( !pl.type.is_variable( length ) && !pl.type.is_integer( length ) ) {
thread.throw_error( pl.error.type( "integer", length, atom.indicator ) );
} else if( !pl.type.is_variable( after ) && !pl.type.is_integer( after ) ) {
thread.throw_error( pl.error.type( "integer", after, atom.indicator ) );
} else if( pl.type.is_integer( before ) && before.value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", before, atom.indicator ) );
} else if( pl.type.is_integer( length ) && length.value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", length, atom.indicator ) );
} else if( pl.type.is_integer( after ) && after.value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", after, atom.indicator ) );
} else {
var bs = [], ls = [], as = [];
if( pl.type.is_variable( before ) ) {
for( i = 0; i <= atom1.id.length; i++ ) {
bs.push( i );
}
} else {
bs.push( before.value );
}
if( pl.type.is_variable( length ) ) {
for( i = 0; i <= atom1.id.length; i++ ) {
ls.push( i );
}
} else {
ls.push( length.value );
}
if( pl.type.is_variable( after ) ) {
for( i = 0; i <= atom1.id.length; i++ ) {
as.push( i );
}
} else {
as.push( after.value );
}
var states = [];
for( var _i in bs ) {
if(!bs.hasOwnProperty(_i)) continue;
i = bs[_i];
for( var _j in ls ) {
if(!ls.hasOwnProperty(_j)) continue;
var j = ls[_j];
var k = atom1.id.length - i - j;
if( indexOf( as, k ) !== -1 ) {
if( i+j+k === atom1.id.length ) {
var str = atom1.id.substr( i, j );
if( atom1.id === atom1.id.substr( 0, i ) + str + atom1.id.substr( i+j, k ) ) {
var pl1 = new Term( "=", [new Term( str ), subatom] );
var pl2 = new Term( "=", [before, new Num( i )] );
var pl3 = new Term( "=", [length, new Num( j )] );
var pl4 = new Term( "=", [after, new Num( k )] );
var goal = new Term( ",", [ new Term( ",", [ new Term( ",", [pl2, pl3] ), pl4] ), pl1] );
states.push( new State( point.goal.replace( goal ), point.substitution, point ) );
}
}
}
}
}
thread.prepend( states );
}
},
// atom_chars/2
"atom_chars/2": function( thread, point, atom ) {
var atom1 = atom.args[0], list = atom.args[1];
if( pl.type.is_variable( atom1 ) && pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( atom1 ) && !pl.type.is_atom( atom1 ) ) {
thread.throw_error( pl.error.type( "atom", atom1, atom.indicator ) );
} else {
if( !pl.type.is_variable( atom1 ) ) {
var list1 = new Term( "[]" );
var unilen = stringLength(atom1.id);
for( var i = unilen-1; i >= 0; i-- ) {
list1 = new Term( ".", [new Term( atom1.id.charAt( i ) ), list1] );
}
thread.prepend( [new State( point.goal.replace( new Term( "=", [list, list1] ) ), point.substitution, point )] );
} else {
var pointer = list;
var v = pl.type.is_variable( atom1 );
var str = "";
while( pointer.indicator === "./2" ) {
if( !pl.type.is_character( pointer.args[0] ) ) {
if( pl.type.is_variable( pointer.args[0] ) && v ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( !pl.type.is_variable( pointer.args[0] ) ) {
thread.throw_error( pl.error.type( "character", pointer.args[0], atom.indicator ) );
return;
}
} else {
str += pointer.args[0].id;
}
pointer = pointer.args[1];
}
if( pl.type.is_variable( pointer ) && v ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_empty_list( pointer ) && !pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [new Term( str ), atom1] ) ), point.substitution, point )] );
}
}
}
},
// atom_codes/2
"atom_codes/2": function( thread, point, atom ) {
var atom1 = atom.args[0], list = atom.args[1];
if( pl.type.is_variable( atom1 ) && pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( atom1 ) && !pl.type.is_atom( atom1 ) ) {
thread.throw_error( pl.error.type( "atom", atom1, atom.indicator ) );
} else {
if( !pl.type.is_variable( atom1 ) ) {
var list1 = new Term( "[]" );
var unilen = stringLength(atom1.id);
for( var i = unilen-1; i >= 0; i-- ) {
list1 = new Term( ".", [new Num( codePointAt(atom1.id,i), false ), list1] );
}
thread.prepend( [new State( point.goal.replace( new Term( "=", [list, list1] ) ), point.substitution, point )] );
} else {
var pointer = list;
var v = pl.type.is_variable( atom1 );
var str = "";
while( pointer.indicator === "./2" ) {
if( !pl.type.is_character_code( pointer.args[0] ) ) {
if( pl.type.is_variable( pointer.args[0] ) && v ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( !pl.type.is_variable( pointer.args[0] ) ) {
thread.throw_error( pl.error.representation( "character_code", atom.indicator ) );
return;
}
} else {
str += fromCodePoint( pointer.args[0].value );
}
pointer = pointer.args[1];
}
if( pl.type.is_variable( pointer ) && v ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_empty_list( pointer ) && !pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [new Term( str ), atom1] ) ), point.substitution, point )] );
}
}
}
},
// char_code/2
"char_code/2": function( thread, point, atom ) {
var char = atom.args[0], code = atom.args[1];
if( pl.type.is_variable( char ) && pl.type.is_variable( code ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( char ) && !pl.type.is_character( char ) ) {
thread.throw_error( pl.error.type( "character", char, atom.indicator ) );
} else if( !pl.type.is_variable( code ) && !pl.type.is_integer( code ) ) {
thread.throw_error( pl.error.type( "integer", code, atom.indicator ) );
} else if( !pl.type.is_variable( code ) && !pl.type.is_character_code( code ) ) {
thread.throw_error( pl.error.representation( "character_code", atom.indicator ) );
} else {
if( pl.type.is_variable( code ) ) {
var code1 = new Num( codePointAt(char.id,0 ), false );
thread.prepend( [new State( point.goal.replace( new Term( "=", [code1, code] ) ), point.substitution, point )] );
} else {
var char1 = new Term( fromCodePoint( code.value ) );
thread.prepend( [new State( point.goal.replace( new Term( "=", [char1, char] ) ), point.substitution, point )] );
}
}
},
// number_chars/2
"number_chars/2": function( thread, point, atom ) {
var str, num = atom.args[0], list = atom.args[1];
if( pl.type.is_variable( num ) && pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( num ) && !pl.type.is_number( num ) ) {
thread.throw_error( pl.error.type( "number", num, atom.indicator ) );
} else if( !pl.type.is_variable( list ) && !pl.type.is_list( list ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
var isvar = pl.type.is_variable( num );
if( !pl.type.is_variable( list ) ) {
var pointer = list;
var total = true;
str = "";
while( pointer.indicator === "./2" ) {
if( !pl.type.is_character( pointer.args[0] ) ) {
if( pl.type.is_variable( pointer.args[0] ) ) {
total = false;
} else if( !pl.type.is_variable( pointer.args[0] ) ) {
thread.throw_error( pl.error.type( "character", pointer.args[0], atom.indicator ) );
return;
}
} else {
str += pointer.args[0].id;
}
pointer = pointer.args[1];
}
total = total && pl.type.is_empty_list( pointer );
if( !pl.type.is_empty_list( pointer ) && !pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
return;
}
if( !total && isvar ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( total ) {
if( pl.type.is_variable( pointer ) && isvar ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else {
var expr = thread.parse( str );
var num2 = expr.value;
if( !pl.type.is_number( num2 ) || expr.tokens[expr.tokens.length-1].space ) {
thread.throw_error( pl.error.syntax_by_predicate( "parseable_number", atom.indicator ) );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [num, num2] ) ), point.substitution, point )] );
}
return;
}
}
}
if( !isvar ) {
str = num.toString();
var list2 = new Term( "[]" );
for( var i = str.length - 1; i >= 0; i-- ) {
list2 = new Term( ".", [ new Term( str.charAt( i ) ), list2 ] );
}
thread.prepend( [new State( point.goal.replace( new Term( "=", [list, list2] ) ), point.substitution, point )] );
}
}
},
// number_codes/2
"number_codes/2": function( thread, point, atom ) {
var str, num = atom.args[0], list = atom.args[1];
if( pl.type.is_variable( num ) && pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( num ) && !pl.type.is_number( num ) ) {
thread.throw_error( pl.error.type( "number", num, atom.indicator ) );
} else if( !pl.type.is_variable( list ) && !pl.type.is_list( list ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
var isvar = pl.type.is_variable( num );
if( !pl.type.is_variable( list ) ) {
var pointer = list;
var total = true;
str = "";
while( pointer.indicator === "./2" ) {
if( !pl.type.is_character_code( pointer.args[0] ) ) {
if( pl.type.is_variable( pointer.args[0] ) ) {
total = false;
} else if( !pl.type.is_variable( pointer.args[0] ) ) {
thread.throw_error( pl.error.type( "character_code", pointer.args[0], atom.indicator ) );
return;
}
} else {
str += fromCodePoint( pointer.args[0].value );
}
pointer = pointer.args[1];
}
total = total && pl.type.is_empty_list( pointer );
if( !pl.type.is_empty_list( pointer ) && !pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
return;
}
if( !total && isvar ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( total ) {
if( pl.type.is_variable( pointer ) && isvar ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else {
var expr = thread.parse( str );
var num2 = expr.value;
if( !pl.type.is_number( num2 ) || expr.tokens[expr.tokens.length-1].space ) {
thread.throw_error( pl.error.syntax_by_predicate( "parseable_number", atom.indicator ) );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [num, num2] ) ), point.substitution, point )] );
}
return;
}
}
}
if( !isvar ) {
str = num.toString();
var list2 = new Term( "[]" );
for( var i = str.length - 1; i >= 0; i-- ) {
list2 = new Term( ".", [ new Num( codePointAt(str,i), false ), list2 ] );
}
thread.prepend( [new State( point.goal.replace( new Term( "=", [list, list2] ) ), point.substitution, point )] );
}
}
},
// upcase_atom/2
"upcase_atom/2": function( thread, point, atom ) {
var original = atom.args[0], upcase = atom.args[1];
if( pl.type.is_variable( original ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( original ) ) {
thread.throw_error( pl.error.type( "atom", original, atom.indicator ) );
} else if( !pl.type.is_variable( upcase ) && !pl.type.is_atom( upcase ) ) {
thread.throw_error( pl.error.type( "atom", upcase, atom.indicator ) );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [upcase, new Term( original.id.toUpperCase(), [] )] ) ), point.substitution, point )] );
}
},
// downcase_atom/2
"downcase_atom/2": function( thread, point, atom ) {
var original = atom.args[0], downcase = atom.args[1];
if( pl.type.is_variable( original ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( original ) ) {
thread.throw_error( pl.error.type( "atom", original, atom.indicator ) );
} else if( !pl.type.is_variable( downcase ) && !pl.type.is_atom( downcase ) ) {
thread.throw_error( pl.error.type( "atom", downcase, atom.indicator ) );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [downcase, new Term( original.id.toLowerCase(), [] )] ) ), point.substitution, point )] );
}
},
// atomic_list_concat/2
"atomic_list_concat/2": function( thread, point, atom ) {
var list = atom.args[0], concat = atom.args[1];
thread.prepend( [new State( point.goal.replace( new Term( "atomic_list_concat", [list, new Term( "", [] ), concat] ) ), point.substitution, point )] );
},
// atomic_list_concat/3
"atomic_list_concat/3": function( thread, point, atom ) {
var list = atom.args[0], separator = atom.args[1], concat = atom.args[2];
if( pl.type.is_variable( separator ) || pl.type.is_variable( list ) && pl.type.is_variable( concat ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( list ) && !pl.type.is_list( list ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else if( !pl.type.is_variable( concat ) && !pl.type.is_atom( concat ) ) {
thread.throw_error( pl.error.type( "atom", concat, atom.indicator ) );
} else {
if( !pl.type.is_variable( concat ) ) {
var atomic = arrayToList( map(
concat.id.split( separator.id ),
function( id ) {
return new Term( id, [] );
}
) );
thread.prepend( [new State( point.goal.replace( new Term( "=", [atomic, list] ) ), point.substitution, point )] );
} else {
var id = "";
var pointer = list;
while( pl.type.is_term( pointer ) && pointer.indicator === "./2" ) {
if( !pl.type.is_atom( pointer.args[0] ) && !pl.type.is_number( pointer.args[0] ) ) {
thread.throw_error( pl.error.type( "atomic", pointer.args[0], atom.indicator ) );
return;
}
if( id !== "" )
id += separator.id;
if( pl.type.is_atom( pointer.args[0] ) )
id += pointer.args[0].id;
else
id += "" + pointer.args[0].value;
pointer = pointer.args[1];
}
id = new Term( id, [] );
if( pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_term( pointer ) || pointer.indicator !== "[]/0" ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [id, concat] ) ), point.substitution, point )] );
}
}
}
},
// TERM COMPARISON
"@=</2": function( thread, point, atom ) {
if( pl.compare( atom.args[0], atom.args[1] ) <= 0 ) {
thread.success( point );
}
},
"==/2": function( thread, point, atom ) {
if( pl.compare( atom.args[0], atom.args[1] ) === 0 ) {
thread.success( point );
}
},
"\\==/2": function( thread, point, atom ) {
if( pl.compare( atom.args[0], atom.args[1] ) !== 0 ) {
thread.success( point );
}
},
"@</2": function( thread, point, atom ) {
if( pl.compare( atom.args[0], atom.args[1] ) < 0 ) {
thread.success( point );
}
},
"@>/2": function( thread, point, atom ) {
if( pl.compare( atom.args[0], atom.args[1] ) > 0 ) {
thread.success( point );
}
},
"@>=/2": function( thread, point, atom ) {
if( pl.compare( atom.args[0], atom.args[1] ) >= 0 ) {
thread.success( point );
}
},
"compare/3": function( thread, point, atom ) {
var order = atom.args[0], left = atom.args[1], right = atom.args[2];
if( !pl.type.is_variable( order ) && !pl.type.is_atom( order ) ) {
thread.throw_error( pl.error.type( "atom", order, atom.indicator ) );
} else if( pl.type.is_atom( order ) && ["<", ">", "="].indexOf( order.id ) === -1 ) {
thread.throw_error( pl.type.domain( "order", order, atom.indicator ) );
} else {
var compare = pl.compare( left, right );
compare = compare === 0 ? "=" : (compare === -1 ? "<" : ">");
thread.prepend( [new State( point.goal.replace( new Term( "=", [order, new Term( compare, [] )] ) ), point.substitution, point )] );
}
},
// EVALUATION
// is/2
"is/2": function( thread, point, atom ) {
var op = atom.args[1].interpret( thread );
if( !pl.type.is_number( op ) ) {
thread.throw_error( op );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [atom.args[0], op], thread.level ) ), point.substitution, point )] );
}
},
// between/3
"between/3": function( thread, point, atom ) {
var lower = atom.args[0], upper = atom.args[1], bet = atom.args[2];
if( pl.type.is_variable( lower ) || pl.type.is_variable( upper ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_integer( lower ) ) {
thread.throw_error( pl.error.type( "integer", lower, atom.indicator ) );
} else if( !pl.type.is_integer( upper ) ) {
thread.throw_error( pl.error.type( "integer", upper, atom.indicator ) );
} else if( !pl.type.is_variable( bet ) && !pl.type.is_integer( bet ) ) {
thread.throw_error( pl.error.type( "integer", bet, atom.indicator ) );
} else {
if( pl.type.is_variable( bet ) ) {
var states = [new State( point.goal.replace( new Term( "=", [bet, lower] ) ), point.substitution, point )];
if( lower.value < upper.value )
states.push( new State( point.goal.replace( new Term( "between", [new Num( lower.value+1, false ), upper, bet] ) ), point.substitution, point ) );
thread.prepend( states );
} else if( lower.value <= bet.value && upper.value >= bet.value ) {
thread.success( point );
}
}
},
// succ/2
"succ/2": function( thread, point, atom ) {
var n = atom.args[0], m = atom.args[1];
if( pl.type.is_variable( n ) && pl.type.is_variable( m ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( n ) && !pl.type.is_integer( n ) ) {
thread.throw_error( pl.error.type( "integer", n, atom.indicator ) );
} else if( !pl.type.is_variable( m ) && !pl.type.is_integer( m ) ) {
thread.throw_error( pl.error.type( "integer", m, atom.indicator ) );
} else if( !pl.type.is_variable( n ) && n.value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", n, atom.indicator ) );
} else if( !pl.type.is_variable( m ) && m.value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", m, atom.indicator ) );
} else {
if( pl.type.is_variable( m ) || m.value > 0 ) {
if( pl.type.is_variable( n ) ) {
thread.prepend( [new State( point.goal.replace( new Term( "=", [n, new Num( m.value-1, false )] ) ), point.substitution, point )] );
} else {
thread.prepend( [new State( point.goal.replace( new Term( "=", [m, new Num( n.value+1, false )] ) ), point.substitution, point )] );
}
}
}
},
// =:=/2
"=:=/2": function( thread, point, atom ) {
var cmp = pl.arithmetic_compare( thread, atom.args[0], atom.args[1] );
if( pl.type.is_term( cmp ) ) {
thread.throw_error( cmp );
} else if( cmp === 0 ) {
thread.success( point );
}
},
// =\=/2
"=\\=/2": function( thread, point, atom ) {
var cmp = pl.arithmetic_compare( thread, atom.args[0], atom.args[1] );
if( pl.type.is_term( cmp ) ) {
thread.throw_error( cmp );
} else if( cmp !== 0 ) {
thread.success( point );
}
},
// </2
"</2": function( thread, point, atom ) {
var cmp = pl.arithmetic_compare( thread, atom.args[0], atom.args[1] );
if( pl.type.is_term( cmp ) ) {
thread.throw_error( cmp );
} else if( cmp < 0 ) {
thread.success( point );
}
},
// =</2
"=</2": function( thread, point, atom ) {
var cmp = pl.arithmetic_compare( thread, atom.args[0], atom.args[1] );
if( pl.type.is_term( cmp ) ) {
thread.throw_error( cmp );
} else if( cmp <= 0 ) {
thread.success( point );
}
},
// >/2
">/2": function( thread, point, atom ) {
var cmp = pl.arithmetic_compare( thread, atom.args[0], atom.args[1] );
if( pl.type.is_term( cmp ) ) {
thread.throw_error( cmp );
} else if( cmp > 0 ) {
thread.success( point );
}
},
// >=/2
">=/2": function( thread, point, atom ) {
var cmp = pl.arithmetic_compare( thread, atom.args[0], atom.args[1] );
if( pl.type.is_term( cmp ) ) {
thread.throw_error( cmp );
} else if( cmp >= 0 ) {
thread.success( point );
}
},
// TYPE TEST
// var/1
"var/1": function( thread, point, atom ) {
if( pl.type.is_variable( atom.args[0] ) ) {
thread.success( point );
}
},
// atom/1
"atom/1": function( thread, point, atom ) {
if( pl.type.is_atom( atom.args[0] ) ) {
thread.success( point );
}
},
// atomic/1
"atomic/1": function( thread, point, atom ) {
if( pl.type.is_atomic( atom.args[0] ) ) {
thread.success( point );
}
},
// compound/1
"compound/1": function( thread, point, atom ) {
if( pl.type.is_compound( atom.args[0] ) ) {
thread.success( point );
}
},
// integer/1
"integer/1": function( thread, point, atom ) {
if( pl.type.is_integer( atom.args[0] ) ) {
thread.success( point );
}
},
// float/1
"float/1": function( thread, point, atom ) {
if( pl.type.is_float( atom.args[0] ) ) {
thread.success( point );
}
},
// number/1
"number/1": function( thread, point, atom ) {
if( pl.type.is_number( atom.args[0] ) ) {
thread.success( point );
}
},
// nonvar/1
"nonvar/1": function( thread, point, atom ) {
if( !pl.type.is_variable( atom.args[0] ) ) {
thread.success( point );
}
},
// ground/1
"ground/1": function( thread, point, atom ) {
if( atom.variables().length === 0 ) {
thread.success( point );
}
},
// acyclic_term/1
"acyclic_term/1": function( thread, point, atom ) {
var test = point.substitution.apply( point.substitution );
var variables = atom.args[0].variables();
for( var i = 0; i < variables.length; i++ )
if( point.substitution.links[variables[i]] !== undefined && !point.substitution.links[variables[i]].equals( test.links[variables[i]] ) )
return;
thread.success( point );
},
// callable/1
"callable/1": function( thread, point, atom ) {
if( pl.type.is_callable( atom.args[0] ) ) {
thread.success( point );
}
},
// is_list/1
"is_list/1": function( thread, point, atom ) {
var list = atom.args[0];
while( pl.type.is_term( list ) && list.indicator === "./2" )
list = list.args[1];
if( pl.type.is_term( list ) && list.indicator === "[]/0" )
thread.success( point );
},
// STREAM SELECTION AND CONTROL
// current_input/1
"current_input/1": function( thread, point, atom ) {
var stream = atom.args[0];
if( !pl.type.is_variable( stream ) && !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain("stream", stream, atom.indicator) );
} else {
if( pl.type.is_atom( stream ) && thread.get_stream_by_alias( stream.id ) )
stream = thread.get_stream_by_alias( stream.id );
thread.prepend( [new State(
point.goal.replace(new Term("=", [stream, thread.get_current_input()])),
point.substitution,
point)
] );
}
},
// current_output/1
"current_output/1": function( thread, point, atom ) {
var stream = atom.args[0];
if( !pl.type.is_variable( stream ) && !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain("stream_or_alias", stream, atom.indicator) );
} else {
if( pl.type.is_atom( stream ) && thread.get_stream_by_alias( stream.id ) )
stream = thread.get_stream_by_alias( stream.id );
thread.prepend( [new State(
point.goal.replace(new Term("=", [stream, thread.get_current_output()])),
point.substitution,
point)
] );
}
},
// set_input/1
"set_input/1": function( thread, point, atom ) {
var input = atom.args[0];
var stream = pl.type.is_stream( input ) ? input : thread.get_stream_by_alias( input.id );
if( pl.type.is_variable( input ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( input ) && !pl.type.is_stream( input ) && !pl.type.is_atom( input ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", input, atom.indicator ) );
} else if( !pl.type.is_stream( stream ) ) {
thread.throw_error( pl.error.existence( "stream", input, atom.indicator ) );
} else if( stream.output === true ) {
thread.throw_error( pl.error.permission( "input", "stream", input, atom.indicator ) );
} else {
thread.set_current_input( stream );
thread.success( point );
}
},
// set_output/1
"set_output/1": function( thread, point, atom ) {
var output = atom.args[0];
var stream = pl.type.is_stream( output ) ? output : thread.get_stream_by_alias( output.id );
if( pl.type.is_variable( output ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( output ) && !pl.type.is_stream( output ) && !pl.type.is_atom( output ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", output, atom.indicator ) );
} else if( !pl.type.is_stream( stream ) ) {
thread.throw_error( pl.error.existence( "stream", output, atom.indicator ) );
} else if( stream.input === true ) {
thread.throw_error( pl.error.permission( "output", "stream", output, atom.indicator ) );
} else {
thread.set_current_output( stream );
thread.success( point );
}
},
// open/3
"open/3": function( thread, point, atom ) {
var dest = atom.args[0], mode = atom.args[1], stream = atom.args[2];
thread.prepend( [new State(
point.goal.replace(new Term("open", [dest, mode, stream, new Term("[]", [])])),
point.substitution,
point
)] );
},
// open/4
"open/4": function( thread, point, atom ) {
var dest = atom.args[0], mode = atom.args[1], stream = atom.args[2], options = atom.args[3];
if( pl.type.is_variable( dest ) || pl.type.is_variable( mode ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( mode ) && !pl.type.is_atom( mode ) ) {
thread.throw_error( pl.error.type( "atom", mode, atom.indicator ) );
} else if( !pl.type.is_list( options ) ) {
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
} else if( !pl.type.is_variable( stream ) ) {
thread.throw_error( pl.error.type( "variable", stream, atom.indicator ) );
} else if( !pl.type.is_atom( dest ) && !pl.type.is_streamable( dest ) ) {
thread.throw_error( pl.error.domain( "source_sink", dest, atom.indicator ) );
} else if( !pl.type.is_io_mode( mode ) ) {
thread.throw_error( pl.error.domain( "io_mode", mode, atom.indicator ) );
} else {
var obj_options = {};
var pointer = options;
var property;
while( pl.type.is_term(pointer) && pointer.indicator === "./2" ) {
property = pointer.args[0];
if( pl.type.is_variable( property ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( !pl.type.is_stream_option( property ) ) {
thread.throw_error( pl.error.domain( "stream_option", property, atom.indicator ) );
return;
}
obj_options[property.id] = property.args[0].id;
pointer = pointer.args[1];
}
if( pointer.indicator !== "[]/0" ) {
if( pl.type.is_variable( pointer ) )
thread.throw_error( pl.error.instantiation( atom.indicator ) );
else
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
return;
} else {
var alias = obj_options["alias"];
if( alias && thread.get_stream_by_alias(alias) ) {
thread.throw_error( pl.error.permission( "open", "source_sink", new Term("alias", [new Term(alias, [])]), atom.indicator ) );
return;
}
if( !obj_options["type"] )
obj_options["type"] = "text";
var file;
if( pl.type.is_atom( dest ) )
file = thread.file_system_open( dest.id, obj_options["type"], mode.id );
else
file = dest.stream( obj_options["type"], mode.id );
if( file === false ) {
thread.throw_error( pl.error.permission( "open", "source_sink", dest, atom.indicator ) );
return;
} else if( file === null ) {
thread.throw_error( pl.error.existence( "source_sink", dest, atom.indicator ) );
return;
}
var newstream = new Stream(
file, mode.id,
obj_options["alias"],
obj_options["type"],
obj_options["reposition"] === "true",
obj_options["eof_action"] );
if( alias )
thread.session.streams[alias] = newstream;
else
thread.session.streams[newstream.id] = newstream;
thread.prepend( [new State(
point.goal.replace( new Term( "=", [stream, newstream] ) ),
point.substitution,
point
)] );
}
}
},
// close/1
"close/1": function( thread, point, atom ) {
var stream = atom.args[0];
thread.prepend( [new State(
point.goal.replace(new Term("close", [stream, new Term("[]", [])])),
point.substitution,
point
)] );
},
// close/2
"close/2": function( thread, point, atom ) {
var stream = atom.args[0], options = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) || pl.type.is_variable( options ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_list( options ) ) {
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
} else if( !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else {
// Get options
var obj_options = {};
var pointer = options;
var property;
while( pl.type.is_term(pointer) && pointer.indicator === "./2" ) {
property = pointer.args[0];
if( pl.type.is_variable( property ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( !pl.type.is_close_option( property ) ) {
thread.throw_error( pl.error.domain( "close_option", property, atom.indicator ) );
return;
}
obj_options[property.id] = property.args[0].id === "true";
pointer = pointer.args[1];
}
if( pointer.indicator !== "[]/0" ) {
if( pl.type.is_variable( pointer ) )
thread.throw_error( pl.error.instantiation( atom.indicator ) );
else
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
return;
} else {
if( stream2 === thread.session.standard_input || stream2 === thread.session.standard_output ) {
thread.success( point );
return;
} else if( stream2 === thread.session.current_input ) {
thread.session.current_input = thread.session.standard_input;
} else if( stream2 === thread.session.current_output ) {
thread.session.current_output = thread.session.current_output;
}
if( stream2.alias !== null )
delete thread.session.streams[stream2.alias];
else
delete thread.session.streams[stream2.id];
if( stream2.output )
stream2.stream.flush();
var closed = stream2.stream.close();
stream2.stream = null;
if( obj_options.force === true || closed === true ) {
thread.success( point );
}
}
}
},
// flush_output/0
"flush_output/0": function( thread, point, atom ) {
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_output", [new Var("S")]),new Term("flush_output", [new Var("S")])]) ),
point.substitution,
point
)] );
},
// flush_output/1
"flush_output/1": function( thread, point, atom ) {
var stream = atom.args[0];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream.input === true ) {
thread.throw_error( pl.error.permission( "output", "stream", output, atom.indicator ) );
} else {
stream2.stream.flush();
thread.success( point );
}
},
// stream_property/2
"stream_property/2": function( thread, point, atom ) {
var stream = atom.args[0], property = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( !pl.type.is_variable( stream ) && !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_variable( stream ) && (!pl.type.is_stream( stream2 ) || stream2.stream === null) ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( !pl.type.is_variable( property ) && !pl.type.is_stream_property( property ) ) {
thread.throw_error( pl.error.domain( "stream_property", property, atom.indicator ) );
} else {
var streams = [];
var states = [];
if( !pl.type.is_variable( stream ) )
streams.push( stream2 );
else
for( var key in thread.session.streams )
streams.push( thread.session.streams[key] );
for( var i = 0; i < streams.length; i++ ) {
var properties = [];
if( streams[i].filename )
properties.push( new Term( "file_name", [new Term(streams[i].file_name, [])] ) );
properties.push( new Term( "mode", [new Term(streams[i].mode, [])] ) );
properties.push( new Term( streams[i].input ? "input" : "output", [] ) );
if( streams[i].alias )
properties.push( new Term( "alias", [new Term(streams[i].alias, [])] ) );
properties.push( new Term( "position", [
typeof streams[i].position === "number" ?
new Num( streams[i].position, false ) :
new Term( streams[i].position, [] )
] ) );
properties.push( new Term( "end_of_stream", [new Term(
streams[i].position === "end_of_stream" ? "at" :
streams[i].position === "past_end_of_stream" ? "past" :
"not", [])] ) );
properties.push( new Term( "eof_action", [new Term(streams[i].eof_action, [])] ) );
properties.push( new Term( "reposition", [new Term(streams[i].reposition ? "true" : "false", [])] ) );
properties.push( new Term( "type", [new Term(streams[i].type, [])] ) );
for( var j = 0; j < properties.length; j++ ) {
states.push( new State(
point.goal.replace( new Term( ",", [
new Term("=", [pl.type.is_variable( stream ) ? stream : stream2, streams[i]]),
new Term("=", [property, properties[j]])]) ),
point.substitution,
point
) );
}
}
thread.prepend( states );
}
},
// at_end_of_stream/0
"at_end_of_stream/0": function( thread, point, atom ) {
thread.prepend( [new State(
point.goal.replace(
new Term(",", [new Term("current_input", [new Var("S")]),new Term(",", [
new Term("stream_property", [new Var("S"),new Term("end_of_stream", [new Var("E")])]),
new Term(",", [new Term("!", []),new Term(";", [new Term("=", [new Var("E"),
new Term("at", [])]),new Term("=", [new Var("E"),new Term("past", [])])])])])])
),
point.substitution,
point
)] );
},
// at_end_of_stream/1
"at_end_of_stream/1": function( thread, point, atom ) {
var stream = atom.args[0];
thread.prepend( [new State(
point.goal.replace(
new Term(",", [new Term("stream_property", [stream,new Term("end_of_stream", [new Var("E")])]),
new Term(",", [new Term("!", []),new Term(";", [new Term("=", [new Var("E"),new Term("at", [])]),
new Term("=", [new Var("E"),new Term("past", [])])])])])
),
point.substitution,
point
)] );
},
// set_stream_position/2
"set_stream_position/2": function( thread, point, atom ) {
var stream = atom.args[0], position = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) || pl.type.is_variable( position ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( !pl.type.is_stream_position( position ) ) {
thread.throw_error( pl.error.domain( "stream_position", position, atom.indicator ) );
} else if( stream2.reposition === false ) {
thread.throw_error( pl.error.permission( "reposition", "stream", stream, atom.indicator ) );
} else {
if( pl.type.is_integer( position ) )
stream2.position = position.value;
else
stream2.position = position.id;
thread.success( point );
}
},
// CHARACTER INPUT OUTPUT
// get_char/1
"get_char/1": function( thread, point, atom ) {
var char = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_input", [new Var("S")]),new Term("get_char", [new Var("S"),char])]) ),
point.substitution,
point
)] );
},
// get_char/2
"get_char/2": function( thread, point, atom ) {
var stream = atom.args[0], char = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( char ) && !pl.type.is_character( char ) ) {
thread.throw_error( pl.error.type( "in_character", char, atom.indicator ) );
} else if( !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.output ) {
thread.throw_error( pl.error.permission( "input", "stream", stream, atom.indicator ) );
} else if( stream2.type === "binary" ) {
thread.throw_error( pl.error.permission( "input", "binary_stream", stream, atom.indicator ) );
} else if( stream2.position === "past_end_of_stream" && stream2.eof_action === "error" ) {
thread.throw_error( pl.error.permission( "input", "past_end_of_stream", stream, atom.indicator ) );
} else {
var stream_char;
if( stream2.position === "end_of_stream" ) {
stream_char = "end_of_file";
stream2.position = "past_end_of_stream";
} else {
stream_char = stream2.stream.get( 1, stream2.position );
if( stream_char === null ) {
thread.throw_error( pl.error.representation( "character", atom.indicator ) );
return;
}
stream2.position++;
}
thread.prepend( [new State(
point.goal.replace( new Term( "=", [new Term(stream_char,[]), char] ) ),
point.substitution,
point
)] );
}
},
// get_code/1
"get_code/1": function( thread, point, atom ) {
var code = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_input", [new Var("S")]),new Term("get_code", [new Var("S"),code])]) ),
point.substitution,
point
)] );
},
// get_code/2
"get_code/2": function( thread, point, atom ) {
var stream = atom.args[0], code = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( code ) && !pl.type.is_integer( code ) ) {
thread.throw_error( pl.error.type( "integer", char, atom.indicator ) );
} else if( !pl.type.is_variable( stream ) && !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.output ) {
thread.throw_error( pl.error.permission( "input", "stream", stream, atom.indicator ) );
} else if( stream2.type === "binary" ) {
thread.throw_error( pl.error.permission( "input", "binary_stream", stream, atom.indicator ) );
} else if( stream2.position === "past_end_of_stream" && stream2.eof_action === "error" ) {
thread.throw_error( pl.error.permission( "input", "past_end_of_stream", stream, atom.indicator ) );
} else {
var stream_code;
if( stream2.position === "end_of_stream" ) {
stream_code = -1;
stream2.position = "past_end_of_stream";
} else {
stream_code = stream2.stream.get( 1, stream2.position );
if( stream_code === null ) {
thread.throw_error( pl.error.representation( "character", atom.indicator ) );
return;
}
stream_code = codePointAt( stream_code, 0 );
stream2.position++;
}
thread.prepend( [new State(
point.goal.replace( new Term( "=", [new Num(stream_code, false), code] ) ),
point.substitution,
point
)] );
}
},
// peek_char/1
"peek_char/1": function( thread, point, atom ) {
var char = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_input", [new Var("S")]),new Term("peek_char", [new Var("S"),char])]) ),
point.substitution,
point
)] );
},
// peek_char/2
"peek_char/2": function( thread, point, atom ) {
var stream = atom.args[0], char = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( char ) && !pl.type.is_character( char ) ) {
thread.throw_error( pl.error.type( "in_character", char, atom.indicator ) );
} else if( !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.output ) {
thread.throw_error( pl.error.permission( "input", "stream", stream, atom.indicator ) );
} else if( stream2.type === "binary" ) {
thread.throw_error( pl.error.permission( "input", "binary_stream", stream, atom.indicator ) );
} else if( stream2.position === "past_end_of_stream" && stream2.eof_action === "error" ) {
thread.throw_error( pl.error.permission( "input", "past_end_of_stream", stream, atom.indicator ) );
} else {
var stream_char;
if( stream2.position === "end_of_stream" ) {
stream_char = "end_of_file";
stream2.position = "past_end_of_stream";
} else {
stream_char = stream2.stream.get( 1, stream2.position );
if( stream_char === null ) {
thread.throw_error( pl.error.representation( "character", atom.indicator ) );
return;
}
}
thread.prepend( [new State(
point.goal.replace( new Term( "=", [new Term(stream_char,[]), char] ) ),
point.substitution,
point
)] );
}
},
// peek_code/1
"peek_code/1": function( thread, point, atom ) {
var code = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_input", [new Var("S")]),new Term("peek_code", [new Var("S"),code])]) ),
point.substitution,
point
)] );
},
// peek_code/2
"peek_code/2": function( thread, point, atom ) {
var stream = atom.args[0], code = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( code ) && !pl.type.is_integer( code ) ) {
thread.throw_error( pl.error.type( "integer", char, atom.indicator ) );
} else if( !pl.type.is_variable( stream ) && !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.output ) {
thread.throw_error( pl.error.permission( "input", "stream", stream, atom.indicator ) );
} else if( stream2.type === "binary" ) {
thread.throw_error( pl.error.permission( "input", "binary_stream", stream, atom.indicator ) );
} else if( stream2.position === "past_end_of_stream" && stream2.eof_action === "error" ) {
thread.throw_error( pl.error.permission( "input", "past_end_of_stream", stream, atom.indicator ) );
} else {
var stream_code;
if( stream2.position === "end_of_stream" ) {
stream_code = -1;
stream2.position = "past_end_of_stream";
} else {
stream_code = stream2.stream.get( 1, stream2.position );
if( stream_code === null ) {
thread.throw_error( pl.error.representation( "character", atom.indicator ) );
return;
}
stream_code = codePointAt( stream_code, 0 );
}
thread.prepend( [new State(
point.goal.replace( new Term( "=", [new Num(stream_code, false), code] ) ),
point.substitution,
point
)] );
}
},
// put_char/1
"put_char/1": function( thread, point, atom ) {
var char = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_output", [new Var("S")]),new Term("put_char", [new Var("S"),char])]) ),
point.substitution,
point
)] );
},
// put_char/2
"put_char/2": function( thread, point, atom ) {
var stream = atom.args[0], char = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) || pl.type.is_variable( char ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_character( char ) ) {
thread.throw_error( pl.error.type( "character", char, atom.indicator ) );
} else if( !pl.type.is_variable( stream ) && !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.input ) {
thread.throw_error( pl.error.permission( "output", "stream", stream, atom.indicator ) );
} else if( stream2.type === "binary" ) {
thread.throw_error( pl.error.permission( "output", "binary_stream", stream, atom.indicator ) );
} else {
if( stream2.stream.put( char.id, stream2.position ) ) {
if(typeof stream2.position === "number")
stream2.position++;
thread.success( point );
}
}
},
// put_code/1
"put_code/1": function( thread, point, atom ) {
var code = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_output", [new Var("S")]),new Term("put_code", [new Var("S"),code])]) ),
point.substitution,
point
)] );
},
// put_code/2
"put_code/2": function( thread, point, atom ) {
var stream = atom.args[0], code = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) || pl.type.is_variable( code ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_integer( code ) ) {
thread.throw_error( pl.error.type( "integer", code, atom.indicator ) );
} else if( !pl.type.is_character_code( code ) ) {
thread.throw_error( pl.error.representation( "character_code", atom.indicator ) );
} else if( !pl.type.is_variable( stream ) && !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.input ) {
thread.throw_error( pl.error.permission( "output", "stream", stream, atom.indicator ) );
} else if( stream2.type === "binary" ) {
thread.throw_error( pl.error.permission( "output", "binary_stream", stream, atom.indicator ) );
} else {
if( stream2.stream.put_char( fromCodePoint( code.value ), stream2.position ) ) {
if(typeof stream2.position === "number")
stream2.position++;
thread.success( point );
}
}
},
// nl/0
"nl/0": function( thread, point, atom ) {
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_output", [new Var("S")]),new Term("put_char", [new Var("S"), new Term("\n", [])])]) ),
point.substitution,
point
)] );
},
// nl/1
"nl/1": function( thread, point, atom ) {
var stream = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term("put_char", [stream, new Term("\n", [])]) ),
point.substitution,
point
)] );
},
// BYTE INPUT/OUTPUT
// get_byte/1
"get_byte/1": function( thread, point, atom ) {
var byte = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_input", [new Var("S")]),new Term("get_byte", [new Var("S"),byte])]) ),
point.substitution,
point
)] );
},
// get_byte/2
"get_byte/2": function( thread, point, atom ) {
var stream = atom.args[0], byte = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( byte ) && !pl.type.is_byte( byte ) ) {
thread.throw_error( pl.error.type( "in_byte", char, atom.indicator ) );
} else if( !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.output ) {
thread.throw_error( pl.error.permission( "input", "stream", stream, atom.indicator ) );
} else if( stream2.type === "text" ) {
thread.throw_error( pl.error.permission( "input", "text_stream", stream, atom.indicator ) );
} else if( stream2.position === "past_end_of_stream" && stream2.eof_action === "error" ) {
thread.throw_error( pl.error.permission( "input", "past_end_of_stream", stream, atom.indicator ) );
} else {
var stream_byte;
if( stream2.position === "end_of_stream" ) {
stream_byte = "end_of_file";
stream2.position = "past_end_of_stream";
} else {
stream_byte = stream2.stream.get_byte( stream2.position );
if( stream_byte === null ) {
thread.throw_error( pl.error.representation( "byte", atom.indicator ) );
return;
}
stream2.position++;
}
thread.prepend( [new State(
point.goal.replace( new Term( "=", [new Num(stream_byte,false), byte] ) ),
point.substitution,
point
)] );
}
},
// peek_byte/1
"peek_byte/1": function( thread, point, atom ) {
var byte = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_input", [new Var("S")]),new Term("peek_byte", [new Var("S"),byte])]) ),
point.substitution,
point
)] );
},
// peek_byte/2
"peek_byte/2": function( thread, point, atom ) {
var stream = atom.args[0], byte = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( byte ) && !pl.type.is_byte( byte ) ) {
thread.throw_error( pl.error.type( "in_byte", char, atom.indicator ) );
} else if( !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.output ) {
thread.throw_error( pl.error.permission( "input", "stream", stream, atom.indicator ) );
} else if( stream2.type === "text" ) {
thread.throw_error( pl.error.permission( "input", "text_stream", stream, atom.indicator ) );
} else if( stream2.position === "past_end_of_stream" && stream2.eof_action === "error" ) {
thread.throw_error( pl.error.permission( "input", "past_end_of_stream", stream, atom.indicator ) );
} else {
var stream_byte;
if( stream2.position === "end_of_stream" ) {
stream_byte = "end_of_file";
stream2.position = "past_end_of_stream";
} else {
stream_byte = stream2.stream.get_byte( stream2.position );
if( stream_byte === null ) {
thread.throw_error( pl.error.representation( "byte", atom.indicator ) );
return;
}
}
thread.prepend( [new State(
point.goal.replace( new Term( "=", [new Num(stream_byte,false), byte] ) ),
point.substitution,
point
)] );
}
},
// put_byte/1
"put_byte/1": function( thread, point, atom ) {
var byte = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_output", [new Var("S")]),new Term("put_byte", [new Var("S"),byte])]) ),
point.substitution,
point
)] );
},
// put_byte/2
"put_byte/2": function( thread, point, atom ) {
var stream = atom.args[0], byte = atom.args[1];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) || pl.type.is_variable( byte ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_byte( byte ) ) {
thread.throw_error( pl.error.type( "byte", byte, atom.indicator ) );
} else if( !pl.type.is_variable( stream ) && !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.input ) {
thread.throw_error( pl.error.permission( "output", "stream", stream, atom.indicator ) );
} else if( stream2.type === "text" ) {
thread.throw_error( pl.error.permission( "output", "text_stream", stream, atom.indicator ) );
} else {
if( stream2.stream.put_byte( byte.value, stream2.position ) ) {
if(typeof stream2.position === "number")
stream2.position++;
thread.success( point );
}
}
},
// TERM INPUT/OUTPUT
// read/1
"read/1": function( thread, point, atom ) {
var term = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_input", [new Var("S")]),new Term("read_term", [new Var("S"),term,new Term("[]",[])])]) ),
point.substitution,
point
)] );
},
// read/2
"read/2": function( thread, point, atom ) {
var stream = atom.args[0], term = atom.args[1];
thread.prepend( [new State(
point.goal.replace( new Term("read_term", [stream,term,new Term("[]",[])]) ),
point.substitution,
point
)] );
},
// read_term/2
"read_term/2": function( thread, point, atom ) {
var term = atom.args[0], options = atom.args[1];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_input", [new Var("S")]),new Term("read_term", [new Var("S"),term,options])]) ),
point.substitution,
point
)] );
},
// read_term/3
"read_term/3": function( thread, point, atom ) {
var stream = atom.args[0], term = atom.args[1], options = atom.args[2];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) || pl.type.is_variable( options ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_list( options ) ) {
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
} else if( !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.output ) {
thread.throw_error( pl.error.permission( "input", "stream", stream, atom.indicator ) );
} else if( stream2.type === "binary" ) {
thread.throw_error( pl.error.permission( "input", "binary_stream", stream, atom.indicator ) );
} else if( stream2.position === "past_end_of_stream" && stream2.eof_action === "error" ) {
thread.throw_error( pl.error.permission( "input", "past_end_of_stream", stream, atom.indicator ) );
} else {
// Get options
var obj_options = {};
var pointer = options;
var property;
while( pl.type.is_term(pointer) && pointer.indicator === "./2" ) {
property = pointer.args[0];
if( pl.type.is_variable( property ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( !pl.type.is_read_option( property ) ) {
thread.throw_error( pl.error.domain( "read_option", property, atom.indicator ) );
return;
}
obj_options[property.id] = property.args[0];
pointer = pointer.args[1];
}
if( pointer.indicator !== "[]/0" ) {
if( pl.type.is_variable( pointer ) )
thread.throw_error( pl.error.instantiation( atom.indicator ) );
else
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
return;
} else {
var char, tokenizer, expr;
var text = "";
var tokens = [];
var last_token = null;
// Get term
while( last_token === null || last_token.name !== "atom" || last_token.value !== "." ||
(expr.type === ERROR && pl.flatten_error(new Term("throw",[expr.value])).found === "token_not_found") ) {
char = stream2.stream.get( 1, stream2.position );
if( char === null ) {
thread.throw_error( pl.error.representation( "character", atom.indicator ) );
return;
}
if( char === "end_of_file" || char === "past_end_of_file" ) {
if( expr )
thread.throw_error( pl.error.syntax( tokens[expr.len-1], ". or expression expected", false ) );
else
thread.throw_error( pl.error.syntax( null, "token not found", true ) );
return;
}
stream2.position++;
text += char;
tokenizer = new Tokenizer( thread );
tokenizer.new_text( text );
tokens = tokenizer.get_tokens();
last_token = tokens !== null && tokens.length > 0 ? tokens[tokens.length-1] : null;
if( tokens === null )
continue;
expr = parseExpr(thread, tokens, 0, thread.__get_max_priority(), false);
}
// Succeed analyzing term
if( expr.type === SUCCESS && expr.len === tokens.length-1 && last_token.value === "." ) {
expr = expr.value.rename( thread );
var eq = new Term( "=", [term, expr] );
// Variables
if( obj_options.variables ) {
var vars = arrayToList( map( nub( expr.variables() ), function(v) { return new Var(v); } ) );
eq = new Term( ",", [eq, new Term( "=", [obj_options.variables, vars] )] )
}
// Variable names
if( obj_options.variable_names ) {
var vars = arrayToList( map( nub( expr.variables() ), function(v) {
var prop;
for( prop in thread.session.renamed_variables ) {
if( thread.session.renamed_variables.hasOwnProperty( prop ) ) {
if( thread.session.renamed_variables[ prop ] === v )
break;
}
}
return new Term( "=", [new Term( prop, []), new Var(v)] );
} ) );
eq = new Term( ",", [eq, new Term( "=", [obj_options.variable_names, vars] )] )
}
// Singletons
if( obj_options.singletons ) {
var vars = arrayToList( map( new Rule( expr, null ).singleton_variables(), function(v) {
var prop;
for( prop in thread.session.renamed_variables ) {
if( thread.session.renamed_variables.hasOwnProperty( prop ) ) {
if( thread.session.renamed_variables[ prop ] === v )
break;
}
}
return new Term( "=", [new Term( prop, []), new Var(v)] );
} ) );
eq = new Term( ",", [eq, new Term( "=", [obj_options.singletons, vars] )] )
}
thread.prepend( [new State( point.goal.replace( eq ), point.substitution, point )] );
// Failed analyzing term
} else {
if( expr.type === SUCCESS )
thread.throw_error( pl.error.syntax( tokens[expr.len], "unexpected token", false ) );
else
thread.throw_error( expr.value );
}
}
}
},
// write/1
"write/1": function( thread, point, atom ) {
var term = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_output", [new Var("S")]),new Term("write", [new Var("S"),term])]) ),
point.substitution,
point
)] );
},
// write/2
"write/2": function( thread, point, atom ) {
var stream = atom.args[0], term = atom.args[1];
thread.prepend( [new State(
point.goal.replace( new Term("write_term", [stream, term,
new Term(".", [new Term("quoted", [new Term("false", [])]),
new Term(".", [new Term("ignore_ops", [new Term("false")]),
new Term(".", [new Term("numbervars", [new Term("true")]), new Term("[]",[])])])])]) ),
point.substitution,
point
)] );
},
// writeq/1
"writeq/1": function( thread, point, atom ) {
var term = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_output", [new Var("S")]),new Term("writeq", [new Var("S"),term])]) ),
point.substitution,
point
)] );
},
// writeq/2
"writeq/2": function( thread, point, atom ) {
var stream = atom.args[0], term = atom.args[1];
thread.prepend( [new State(
point.goal.replace( new Term("write_term", [stream, term,
new Term(".", [new Term("quoted", [new Term("true", [])]),
new Term(".", [new Term("ignore_ops", [new Term("false")]),
new Term(".", [new Term("numbervars", [new Term("true")]), new Term("[]",[])])])])]) ),
point.substitution,
point
)] );
},
// write_canonical/1
"write_canonical/1": function( thread, point, atom ) {
var term = atom.args[0];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_output", [new Var("S")]),new Term("write_canonical", [new Var("S"),term])]) ),
point.substitution,
point
)] );
},
// write_canonical/2
"write_canonical/2": function( thread, point, atom ) {
var stream = atom.args[0], term = atom.args[1];
thread.prepend( [new State(
point.goal.replace( new Term("write_term", [stream, term,
new Term(".", [new Term("quoted", [new Term("true", [])]),
new Term(".", [new Term("ignore_ops", [new Term("true")]),
new Term(".", [new Term("numbervars", [new Term("false")]), new Term("[]",[])])])])]) ),
point.substitution,
point
)] );
},
// write_term/2
"write_term/2": function( thread, point, atom ) {
var term = atom.args[0], options = atom.args[1];
thread.prepend( [new State(
point.goal.replace( new Term(",", [new Term("current_output", [new Var("S")]),new Term("write_term", [new Var("S"),term,options])]) ),
point.substitution,
point
)] );
},
// write_term/3
"write_term/3": function( thread, point, atom ) {
var stream = atom.args[0], term = atom.args[1], options = atom.args[2];
var stream2 = pl.type.is_stream( stream ) ? stream : thread.get_stream_by_alias( stream.id );
if( pl.type.is_variable( stream ) || pl.type.is_variable( options ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_list( options ) ) {
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
} else if( !pl.type.is_stream( stream ) && !pl.type.is_atom( stream ) ) {
thread.throw_error( pl.error.domain( "stream_or_alias", stream, atom.indicator ) );
} else if( !pl.type.is_stream( stream2 ) || stream2.stream === null ) {
thread.throw_error( pl.error.existence( "stream", stream, atom.indicator ) );
} else if( stream2.input ) {
thread.throw_error( pl.error.permission( "output", "stream", stream, atom.indicator ) );
} else if( stream2.type === "binary" ) {
thread.throw_error( pl.error.permission( "output", "binary_stream", stream, atom.indicator ) );
} else if( stream2.position === "past_end_of_stream" && stream2.eof_action === "error" ) {
thread.throw_error( pl.error.permission( "output", "past_end_of_stream", stream, atom.indicator ) );
} else {
// Get options
var obj_options = {};
var pointer = options;
var property;
while( pl.type.is_term(pointer) && pointer.indicator === "./2" ) {
property = pointer.args[0];
if( pl.type.is_variable( property ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( !pl.type.is_write_option( property ) ) {
thread.throw_error( pl.error.domain( "write_option", property, atom.indicator ) );
return;
}
obj_options[property.id] = property.args[0].id === "true";
pointer = pointer.args[1];
}
if( pointer.indicator !== "[]/0" ) {
if( pl.type.is_variable( pointer ) )
thread.throw_error( pl.error.instantiation( atom.indicator ) );
else
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
return;
} else {
obj_options.session = thread.session;
var text = term.toString( obj_options );
stream2.stream.put( text, stream2.position );
if( typeof stream2.position === "number" )
stream2.position += text.length;
thread.success( point );
}
}
},
// IMPLEMENTATION DEFINED HOOKS
// halt/0
"halt/0": function( thread, point, _ ) {
if( thread.get_flag("nodejs").indicator === "true/0" )
process.exit();
thread.points = [];
},
// halt/1
"halt/1": function( thread, point, atom ) {
var int = atom.args[0];
if( pl.type.is_variable( int ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_integer( int ) ) {
thread.throw_error( pl.error.type( "integer", int, atom.indicator ) );
} else {
if( thread.get_flag("nodejs").indicator === "true/0" )
process.exit(int.value);
thread.points = [];
}
},
// current_prolog_flag/2
"current_prolog_flag/2": function( thread, point, atom ) {
var flag = atom.args[0], value = atom.args[1];
if( !pl.type.is_variable( flag ) && !pl.type.is_atom( flag ) ) {
thread.throw_error( pl.error.type( "atom", flag, atom.indicator ) );
} else if( !pl.type.is_variable( flag ) && !pl.type.is_flag( flag ) ) {
thread.throw_error( pl.error.domain( "prolog_flag", flag, atom.indicator ) );
} else {
var states = [];
for( var name in pl.flag ) {
if(!pl.flag.hasOwnProperty(name)) continue;
var goal = new Term( ",", [new Term( "=", [new Term( name ), flag] ), new Term( "=", [thread.get_flag(name), value] )] );
states.push( new State( point.goal.replace( goal ), point.substitution, point ) );
}
thread.prepend( states );
}
},
// set_prolog_flag/2
"set_prolog_flag/2": function( thread, point, atom ) {
var flag = atom.args[0], value = atom.args[1];
if( pl.type.is_variable( flag ) || pl.type.is_variable( value ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( flag ) ) {
thread.throw_error( pl.error.type( "atom", flag, atom.indicator ) );
} else if( !pl.type.is_flag( flag ) ) {
thread.throw_error( pl.error.domain( "prolog_flag", flag, atom.indicator ) );
} else if( !pl.type.is_value_flag( flag, value ) ) {
thread.throw_error( pl.error.domain( "flag_value", new Term( "+", [flag, value] ), atom.indicator ) );
} else if( !pl.type.is_modifiable_flag( flag ) ) {
thread.throw_error( pl.error.permission( "modify", "flag", flag ) );
} else {
thread.session.flag[flag.id] = value;
thread.success( point );
}
},
// LOAD PROLOG SOURCE FILES
// consult/1
"consult/1": function( thread, point, atom ) {
var src = atom.args[0];
if(pl.type.is_variable(src)) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if(!pl.type.is_atom(src)) {
thread.throw_error( pl.error.type( "atom", src, atom.indicator ) );
} else {
if(thread.consult( src.id ))
thread.success(point);
}
},
// TIME AND DATES
// get_time/1
"get_time/1": function( thread, point, atom ) {
var time = atom.args[0];
if(!pl.type.is_variable(time) && !pl.type.is_number(time)) {
thread.throw_error( pl.error.type( "number", time, atom.indicator ) );
} else {
var current = new Num(Date.now(), true);
thread.prepend( [new State(
point.goal.replace( new Term( "=", [time, current] ) ),
point.substitution,
point
)] );
}
},
// GRAMMARS
// phrase/3
"phrase/3": function( thread, point, atom ) {
var grbody = atom.args[0], s0 = atom.args[1], s = atom.args[2];
if( pl.type.is_variable( grbody ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_callable( grbody ) ) {
thread.throw_error( pl.error.type( "callable", grbody, atom.indicator ) );
} else {
var goal = body_to_dcg( grbody.clone(), s0, thread );
if(goal !== null) {
thread.prepend( [new State(
point.goal.replace( new Term( ",", [goal.value, new Term("=", [goal.variable, s])] ) ),
point.substitution,
point
)] );
}
}
},
// phrase/2
"phrase/2": function( thread, point, atom ) {
var grbody = atom.args[0], s0 = atom.args[1];
thread.prepend( [new State(
point.goal.replace( new Term( "phrase", [grbody, s0, new Term("[]", [])] ) ),
point.substitution,
point
)] );
},
// TAU PROLOG INFORMATION
// version/0
"version/0": function( thread, point, atom ) {
var msg = "Welcome to Tau Prolog version " + version.major + "." + version.minor + "." + version.patch + "\n";
msg += "Tau Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.\n";
msg += "Please run ?- license. for legal details.\n";
msg += "For online help and background, visit http:/tau-prolog.org";
thread.prepend( [new State(
point.goal.replace( new Term( "write", [new Term( msg, [] )] ) ),
point.substitution,
point
)] );
},
// license/0
"license/0": function( thread, point, atom ) {
var msg = "Tau Prolog. A Prolog interpreter in JavaScript.\n";
msg += "Copyright (C) 2017 - 2020 José Antonio Riaza Valverde\n\n";
msg += "Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n";
msg += "1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n";
msg += "2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n";
msg += "3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n\n";
msg += "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n";
msg += "You should have received a copy of the BSD 3-Clause License along with this program. If not, see https://opensource.org/licenses/BSD-3-Clause";
thread.prepend( [new State(
point.goal.replace( new Term( "write", [new Term( msg, [] )] ) ),
point.substitution,
point
)] );
}
},
// Flags
flag: {
// Bounded numbers
bounded: {
allowed: [new Term( "true" ), new Term( "false" )],
value: new Term( "true" ),
changeable: false
},
// Maximum integer
max_integer: {
allowed: [new Num( Number.MAX_SAFE_INTEGER )],
value: new Num( Number.MAX_SAFE_INTEGER ),
changeable: false
},
// Minimum integer
min_integer: {
allowed: [new Num( Number.MIN_SAFE_INTEGER )],
value: new Num( Number.MIN_SAFE_INTEGER ),
changeable: false
},
// Rounding function
integer_rounding_function: {
allowed: [new Term( "down" ), new Term( "toward_zero" )],
value: new Term( "toward_zero" ),
changeable: false
},
// Character conversion
char_conversion: {
allowed: [new Term( "on" ), new Term( "off" )],
value: new Term( "on" ),
changeable: true
},
// Debugger
debug: {
allowed: [new Term( "on" ), new Term( "off" )],
value: new Term( "off" ),
changeable: true
},
// Maximum arity of predicates
max_arity: {
allowed: [new Term( "unbounded" )],
value: new Term( "unbounded" ),
changeable: false
},
// Unkwnow predicates behavior
unknown: {
allowed: [new Term( "error" ), new Term( "fail" ), new Term( "warning" )],
value: new Term( "error" ),
changeable: true
},
// Double quotes behavior
double_quotes: {
allowed: [new Term( "chars" ), new Term( "codes" ), new Term( "atom" )],
value: new Term( "codes" ),
changeable: true
},
// Occurs check behavior
occurs_check: {
allowed: [new Term( "false" ), new Term( "true" )],
value: new Term( "false" ),
changeable: true
},
// Dialect
dialect: {
allowed: [new Term( "tau" )],
value: new Term( "tau" ),
changeable: false
},
// Version
version_data: {
allowed: [new Term( "tau", [new Num(version.major,false), new Num(version.minor,false), new Num(version.patch,false), new Term(version.status)] )],
value: new Term( "tau", [new Num(version.major,false), new Num(version.minor,false), new Num(version.patch,false), new Term(version.status)] ),
changeable: false
},
// NodeJS
nodejs: {
allowed: [new Term( "true" ), new Term( "false" )],
value: new Term( nodejs_flag ? "true" : "false" ),
changeable: false
},
// Arguments
argv: {
allowed: [nodejs_arguments],
value: nodejs_arguments,
changeble: false
}
},
// Unify
unify: function( s, t, occurs_check ) {
occurs_check = occurs_check === undefined ? false : occurs_check;
var G = [{left: s, right: t}], links = {};
while( G.length !== 0 ) {
var eq = G.pop();
s = eq.left;
t = eq.right;
if(s == t)
continue;
if( pl.type.is_term(s) && pl.type.is_term(t) ) {
if( s.indicator !== t.indicator )
return null;
// list
if(s.indicator === "./2") {
var pointer_s = s, pointer_t = t;
while(pointer_s.indicator === "./2" && pointer_t.indicator === "./2") {
G.push( {left: pointer_s.args[0], right: pointer_t.args[0]} );
pointer_s = pointer_s.args[1];
pointer_t = pointer_t.args[1];
}
G.push( {left: pointer_s, right: pointer_t} );
// compound term
} else {
for( var i = 0; i < s.args.length; i++ )
G.push( {left: s.args[i], right: t.args[i]} );
}
} else if( pl.type.is_number(s) && pl.type.is_number(t) ) {
if( s.value !== t.value || s.is_float !== t.is_float )
return null;
} else if( pl.type.is_variable(s) ) {
// X = X
if( pl.type.is_variable(t) && s.id === t.id )
continue;
// Occurs check
if( occurs_check === true && indexOf( t.variables(), s.id ) !== -1 )
return null;
if( s.id !== "_" ) {
var subs = new Substitution();
subs.add( s.id, t );
for( var i = 0; i < G.length; i++ ) {
G[i].left = G[i].left.apply( subs );
G[i].right = G[i].right.apply( subs );
}
for( var i in links )
links[i] = links[i].apply( subs );
links[s.id] = t;
}
} else if( pl.type.is_variable(t) ) {
G.push( {left: t, right: s} );
} else if( s.unify !== undefined ) {
if( !s.unify(t) )
return null;
} else {
return null;
}
}
return new Substitution( links );
},
// Compare
compare: function( obj1, obj2 ) {
var type = pl.type.compare( obj1, obj2 );
return type !== 0 ? type : obj1.compare( obj2 );
},
// Arithmetic comparison
arithmetic_compare: function( thread, obj1, obj2 ) {
var expr1 = obj1.interpret( thread );
if( !pl.type.is_number( expr1 ) ) {
return expr1;
} else {
var expr2 = obj2.interpret( thread );
if( !pl.type.is_number( expr2 ) ) {
return expr2;
} else {
return expr1.value < expr2.value ? -1 : (expr1.value > expr2.value ? 1 : 0);
}
}
},
// Operate
operate: function( thread, obj ) {
if( pl.type.is_operator( obj ) ) {
var op = pl.type.is_operator( obj );
var args = [], value;
var type = false;
for( var i = 0; i < obj.args.length; i++ ) {
value = obj.args[i].interpret( thread );
if( !pl.type.is_number( value ) ) {
return value;
} else if( op.type_args !== null && value.is_float !== op.type_args ) {
return pl.error.type( op.type_args ? "float" : "integer", value, thread.__call_indicator );
} else {
args.push( value.value );
}
type = type || value.is_float;
}
args.push( thread );
value = pl.arithmetic.evaluation[obj.indicator].fn.apply( this, args );
type = op.type_result === null ? type : op.type_result;
if( pl.type.is_term( value ) ) {
return value;
} else if( value === Number.POSITIVE_INFINITY || value === Number.NEGATIVE_INFINITY ) {
return pl.error.evaluation( "overflow", thread.__call_indicator );
} else if( type === false && thread.get_flag( "bounded" ).id === "true" && (value > thread.get_flag( "max_integer" ).value || value < thread.get_flag( "min_integer" ).value) ) {
return pl.error.evaluation( "int_overflow", thread.__call_indicator );
} else {
return new Num( value, type );
}
} else {
return pl.error.type( "evaluable", obj.indicator, thread.__call_indicator );
}
},
// Errors
error: {
// Existence error
existence: function( type, object, indicator ) {
if( typeof object === "string" )
object = str_indicator( object );
return new Term( "error", [new Term( "existence_error", [new Term( type ), object] ), str_indicator( indicator )] );
},
// Type error
type: function( expected, found, indicator ) {
return new Term( "error", [new Term( "type_error", [new Term( expected ), found] ), str_indicator( indicator )] );
},
// Instantation error
instantiation: function( indicator ) {
return new Term( "error", [new Term( "instantiation_error" ), str_indicator( indicator )] );
},
// Domain error
domain: function( expected, found, indicator ) {
return new Term( "error", [new Term( "domain_error", [new Term( expected ), found]), str_indicator( indicator )] );
},
// Representation error
representation: function( flag, indicator ) {
return new Term( "error", [new Term( "representation_error", [new Term( flag )] ), str_indicator( indicator )] );
},
// Permission error
permission: function( operation, type, found, indicator ) {
return new Term( "error", [new Term( "permission_error", [new Term( operation ), new Term( type ), found] ), str_indicator( indicator )] );
},
// Evaluation error
evaluation: function( error, indicator ) {
return new Term( "error", [new Term( "evaluation_error", [new Term( error )] ), str_indicator( indicator )] );
},
// Syntax error
syntax: function( token, expected, last ) {
token = token || {value: "", line: 0, column: 0, matches: [""], start: 0};
var position = last && token.matches.length > 0 ? token.start + token.matches[0].length : token.start;
var found = last ? new Term("token_not_found") : new Term("found", [new Term(token.value.toString())]);
var info = new Term( ".", [new Term( "line", [new Num(token.line+1)] ), new Term( ".", [new Term( "column", [new Num(position+1)] ), new Term( ".", [found, new Term( "[]", [] )] )] )] );
return new Term( "error", [new Term( "syntax_error", [new Term( expected )] ), info] );
},
// Syntax error by predicate
syntax_by_predicate: function( expected, indicator ) {
return new Term( "error", [new Term( "syntax_error", [new Term( expected ) ] ), str_indicator( indicator )] );
}
},
// Warnings
warning: {
// Singleton variables
singleton: function( variables, rule, line ) {
var list = new Term( "[]" );
for( var i = variables.length-1; i >= 0; i-- )
list = new Term( ".", [new Var(variables[i]), list] );
return new Term( "warning", [new Term( "singleton_variables", [list, str_indicator(rule)]), new Term(".",[new Term( "line", [ new Num( line, false ) ]), new Term("[]")])] );
},
// Failed goal
failed_goal: function( goal, line ) {
return new Term( "warning", [new Term( "failed_goal", [goal]), new Term(".",[new Term( "line", [ new Num( line, false ) ]), new Term("[]")])] );
}
},
// Format of renamed variables
format_variable: function( variable ) {
return "_" + variable;
},
// Format of computed answers
format_answer: function( answer, thread, options ) {
if( thread instanceof Session )
thread = thread.thread;
var options = options ? options : {};
options.session = thread ? thread.session : undefined;
if( pl.type.is_error( answer ) ) {
return "uncaught exception: " + answer.args[0].toString();
} else if( answer === false ) {
return "false.";
} else if( answer === null ) {
return "limit exceeded ;";
} else {
var i = 0;
var str = "";
if( pl.type.is_substitution( answer ) ) {
var dom = answer.domain( true );
for( var link in answer.links ){
if( !answer.links.hasOwnProperty(link) ) continue;
if( pl.type.is_variable(answer.links[link]) ) {
var links = {};
links[answer.links[link].id] = new Var(link);
answer = answer.apply( new Substitution(links) );
}
}
answer = answer.filter( function( id, value ) {
return !pl.type.is_variable( value ) ||
pl.type.is_variable( value ) && answer.has_attributes( id ) ||
indexOf( dom, value.id ) !== -1 && id !== value.id;
} );
}
for( var link in answer.links ) {
if(!answer.links.hasOwnProperty(link))
continue;
if( pl.type.is_variable( answer.links[link] ) && link === answer.links[link].id ) {
var attrs = answer.attrs[link];
for( var module in attrs ) {
if(!attrs.hasOwnProperty(module))
continue;
i++;
if( str !== "" )
str += ", ";
str += "put_attr(" + link + ", " + module + ", " + attrs[module].toString(options) + ")";
}
} else {
i++;
if( str !== "" )
str += ", ";
str += link.toString( options ) + " = " +
answer.links[link].toString( options, {priority: "700", class: "xfx", indicator: "=/2"}, "right" );
}
}
var delimiter = typeof thread === "undefined" || thread.points.length > 0 ? " ;" : ".";
if( i === 0 ) {
return "true" + delimiter;
} else {
return str + delimiter;
}
}
},
// Flatten default errors
flatten_error: function( error ) {
if( !pl.type.is_error( error ) ) return null;
error = error.args[0];
var obj = {};
obj.type = error.args[0].id;
obj.thrown = obj.type === "syntax_error" ? null : error.args[1].id;
obj.expected = null;
obj.found = null;
obj.representation = null;
obj.existence = null;
obj.existence_type = null;
obj.line = null;
obj.column = null;
obj.permission_operation = null;
obj.permission_type = null;
obj.evaluation_type = null;
if( obj.type === "type_error" || obj.type === "domain_error" ) {
obj.expected = error.args[0].args[0].id;
obj.found = error.args[0].args[1].toString();
} else if( obj.type === "syntax_error" ) {
if( error.args[1].indicator === "./2" ) {
obj.expected = error.args[0].args[0].id;
obj.found = error.args[1].args[1].args[1].args[0];
obj.found = obj.found.id === "token_not_found" ? obj.found.id : obj.found.args[0].id;
obj.line = error.args[1].args[0].args[0].value;
obj.column = error.args[1].args[1].args[0].args[0].value;
} else {
obj.thrown = error.args[1].id;
}
} else if( obj.type === "permission_error" ) {
obj.found = error.args[0].args[2].toString();
obj.permission_operation = error.args[0].args[0].id;
obj.permission_type = error.args[0].args[1].id;
} else if( obj.type === "evaluation_error" ) {
obj.evaluation_type = error.args[0].args[0].id;
} else if( obj.type === "representation_error" ) {
obj.representation = error.args[0].args[0].id;
} else if( obj.type === "existence_error" ) {
obj.existence = error.args[0].args[1].toString();
obj.existence_type = error.args[0].args[0].id;
}
return obj;
},
// Create new session
create: function( limit , output ) {
return new pl.type.Session( limit, output );
}
};
if( typeof module !== 'undefined' ) {
prolog = pl;
} else {
window.prolog = pl;
}
})();
(function( pl ) {
var predicates = function() {
return {
// append/2
"append/2": [
new pl.type.Rule(new pl.type.Term("append", [new pl.type.Var("X"),new pl.type.Var("L")]), new pl.type.Term("foldl", [new pl.type.Term("append", []),new pl.type.Var("X"),new pl.type.Term("[]", []),new pl.type.Var("L")]))
],
// append/3
"append/3": [
new pl.type.Rule(new pl.type.Term("append", [new pl.type.Term("[]", []),new pl.type.Var("X"),new pl.type.Var("X")]), null),
new pl.type.Rule(new pl.type.Term("append", [new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Var("T")]),new pl.type.Var("X"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Var("S")])]), new pl.type.Term("append", [new pl.type.Var("T"),new pl.type.Var("X"),new pl.type.Var("S")]))
],
// member/2
"member/2": [
new pl.type.Rule(new pl.type.Term("member", [new pl.type.Var("X"),new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("_")])]), null),
new pl.type.Rule(new pl.type.Term("member", [new pl.type.Var("X"),new pl.type.Term(".", [new pl.type.Var("_"),new pl.type.Var("Xs")])]), new pl.type.Term("member", [new pl.type.Var("X"),new pl.type.Var("Xs")]))
],
// permutation/2
"permutation/2": [
new pl.type.Rule(new pl.type.Term("permutation", [new pl.type.Term("[]", []),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("permutation", [new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Var("T")]),new pl.type.Var("S")]), new pl.type.Term(",", [new pl.type.Term("permutation", [new pl.type.Var("T"),new pl.type.Var("P")]),new pl.type.Term(",", [new pl.type.Term("append", [new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("P")]),new pl.type.Term("append", [new pl.type.Var("X"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Var("Y")]),new pl.type.Var("S")])])]))
],
// maplist/2
"maplist/2": [
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("_"),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Xs")])]), new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("P"),new pl.type.Var("X")]),new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Var("Xs")])]))
],
// maplist/3
"maplist/3": [
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("_"),new pl.type.Term("[]", []),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("A"),new pl.type.Var("As")]),new pl.type.Term(".", [new pl.type.Var("B"),new pl.type.Var("Bs")])]), new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("P"),new pl.type.Var("A"),new pl.type.Var("B")]),new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Var("As"),new pl.type.Var("Bs")])]))
],
// maplist/4
"maplist/4": [
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("_"),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("A"),new pl.type.Var("As")]),new pl.type.Term(".", [new pl.type.Var("B"),new pl.type.Var("Bs")]),new pl.type.Term(".", [new pl.type.Var("C"),new pl.type.Var("Cs")])]), new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("P"),new pl.type.Var("A"),new pl.type.Var("B"),new pl.type.Var("C")]),new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Var("As"),new pl.type.Var("Bs"),new pl.type.Var("Cs")])]))
],
// maplist/5
"maplist/5": [
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("_"),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("A"),new pl.type.Var("As")]),new pl.type.Term(".", [new pl.type.Var("B"),new pl.type.Var("Bs")]),new pl.type.Term(".", [new pl.type.Var("C"),new pl.type.Var("Cs")]),new pl.type.Term(".", [new pl.type.Var("D"),new pl.type.Var("Ds")])]), new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("P"),new pl.type.Var("A"),new pl.type.Var("B"),new pl.type.Var("C"),new pl.type.Var("D")]),new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Var("As"),new pl.type.Var("Bs"),new pl.type.Var("Cs"),new pl.type.Var("Ds")])]))
],
// maplist/6
"maplist/6": [
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("_"),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("A"),new pl.type.Var("As")]),new pl.type.Term(".", [new pl.type.Var("B"),new pl.type.Var("Bs")]),new pl.type.Term(".", [new pl.type.Var("C"),new pl.type.Var("Cs")]),new pl.type.Term(".", [new pl.type.Var("D"),new pl.type.Var("Ds")]),new pl.type.Term(".", [new pl.type.Var("E"),new pl.type.Var("Es")])]), new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("P"),new pl.type.Var("A"),new pl.type.Var("B"),new pl.type.Var("C"),new pl.type.Var("D"),new pl.type.Var("E")]),new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Var("As"),new pl.type.Var("Bs"),new pl.type.Var("Cs"),new pl.type.Var("Ds"),new pl.type.Var("Es")])]))
],
// maplist/7
"maplist/7": [
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("_"),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("A"),new pl.type.Var("As")]),new pl.type.Term(".", [new pl.type.Var("B"),new pl.type.Var("Bs")]),new pl.type.Term(".", [new pl.type.Var("C"),new pl.type.Var("Cs")]),new pl.type.Term(".", [new pl.type.Var("D"),new pl.type.Var("Ds")]),new pl.type.Term(".", [new pl.type.Var("E"),new pl.type.Var("Es")]),new pl.type.Term(".", [new pl.type.Var("F"),new pl.type.Var("Fs")])]), new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("P"),new pl.type.Var("A"),new pl.type.Var("B"),new pl.type.Var("C"),new pl.type.Var("D"),new pl.type.Var("E"),new pl.type.Var("F")]),new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Var("As"),new pl.type.Var("Bs"),new pl.type.Var("Cs"),new pl.type.Var("Ds"),new pl.type.Var("Es"),new pl.type.Var("Fs")])]))
],
// maplist/8
"maplist/8": [
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("_"),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", []),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("A"),new pl.type.Var("As")]),new pl.type.Term(".", [new pl.type.Var("B"),new pl.type.Var("Bs")]),new pl.type.Term(".", [new pl.type.Var("C"),new pl.type.Var("Cs")]),new pl.type.Term(".", [new pl.type.Var("D"),new pl.type.Var("Ds")]),new pl.type.Term(".", [new pl.type.Var("E"),new pl.type.Var("Es")]),new pl.type.Term(".", [new pl.type.Var("F"),new pl.type.Var("Fs")]),new pl.type.Term(".", [new pl.type.Var("G"),new pl.type.Var("Gs")])]), new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("P"),new pl.type.Var("A"),new pl.type.Var("B"),new pl.type.Var("C"),new pl.type.Var("D"),new pl.type.Var("E"),new pl.type.Var("F"),new pl.type.Var("G")]),new pl.type.Term("maplist", [new pl.type.Var("P"),new pl.type.Var("As"),new pl.type.Var("Bs"),new pl.type.Var("Cs"),new pl.type.Var("Ds"),new pl.type.Var("Es"),new pl.type.Var("Fs"),new pl.type.Var("Gs")])]))
],
// include/3
"include/3": [
new pl.type.Rule(new pl.type.Term("include", [new pl.type.Var("_"),new pl.type.Term("[]", []),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("include", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Var("T")]),new pl.type.Var("L")]), new pl.type.Term(",", [new pl.type.Term("=..", [new pl.type.Var("P"),new pl.type.Var("A")]),new pl.type.Term(",", [new pl.type.Term("append", [new pl.type.Var("A"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Term("[]", [])]),new pl.type.Var("B")]),new pl.type.Term(",", [new pl.type.Term("=..", [new pl.type.Var("F"),new pl.type.Var("B")]),new pl.type.Term(",", [new pl.type.Term(";", [new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("F")]),new pl.type.Term(",", [new pl.type.Term("=", [new pl.type.Var("L"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Var("S")])]),new pl.type.Term("!", [])])]),new pl.type.Term("=", [new pl.type.Var("L"),new pl.type.Var("S")])]),new pl.type.Term("include", [new pl.type.Var("P"),new pl.type.Var("T"),new pl.type.Var("S")])])])])]))
],
// exclude/3
"exclude/3": [
new pl.type.Rule(new pl.type.Term("exclude", [new pl.type.Var("_"),new pl.type.Term("[]", []),new pl.type.Term("[]", [])]), null),
new pl.type.Rule(new pl.type.Term("exclude", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Var("T")]),new pl.type.Var("S")]), new pl.type.Term(",", [new pl.type.Term("exclude", [new pl.type.Var("P"),new pl.type.Var("T"),new pl.type.Var("E")]),new pl.type.Term(",", [new pl.type.Term("=..", [new pl.type.Var("P"),new pl.type.Var("L")]),new pl.type.Term(",", [new pl.type.Term("append", [new pl.type.Var("L"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Term("[]", [])]),new pl.type.Var("Q")]),new pl.type.Term(",", [new pl.type.Term("=..", [new pl.type.Var("R"),new pl.type.Var("Q")]),new pl.type.Term(";", [new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("R")]),new pl.type.Term(",", [new pl.type.Term("!", []),new pl.type.Term("=", [new pl.type.Var("S"),new pl.type.Var("E")])])]),new pl.type.Term("=", [new pl.type.Var("S"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Var("E")])])])])])])]))
],
// foldl/4
"foldl/4": [
new pl.type.Rule(new pl.type.Term("foldl", [new pl.type.Var("_"),new pl.type.Term("[]", []),new pl.type.Var("I"),new pl.type.Var("I")]), null),
new pl.type.Rule(new pl.type.Term("foldl", [new pl.type.Var("P"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Var("T")]),new pl.type.Var("I"),new pl.type.Var("R")]), new pl.type.Term(",", [new pl.type.Term("=..", [new pl.type.Var("P"),new pl.type.Var("L")]),new pl.type.Term(",", [new pl.type.Term("append", [new pl.type.Var("L"),new pl.type.Term(".", [new pl.type.Var("I"),new pl.type.Term(".", [new pl.type.Var("H"),new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Term("[]", [])])])]),new pl.type.Var("L2")]),new pl.type.Term(",", [new pl.type.Term("=..", [new pl.type.Var("P2"),new pl.type.Var("L2")]),new pl.type.Term(",", [new pl.type.Term("call", [new pl.type.Var("P2")]),new pl.type.Term("foldl", [new pl.type.Var("P"),new pl.type.Var("T"),new pl.type.Var("X"),new pl.type.Var("R")])])])])]))
],
// select/3
"select/3": [
new pl.type.Rule(new pl.type.Term("select", [new pl.type.Var("E"),new pl.type.Term(".", [new pl.type.Var("E"),new pl.type.Var("Xs")]),new pl.type.Var("Xs")]), null),
new pl.type.Rule(new pl.type.Term("select", [new pl.type.Var("E"),new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Xs")]),new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Ys")])]), new pl.type.Term("select", [new pl.type.Var("E"),new pl.type.Var("Xs"),new pl.type.Var("Ys")]))
],
// sum_list/2
"sum_list/2": [
new pl.type.Rule(new pl.type.Term("sum_list", [new pl.type.Term("[]", []),new pl.type.Num(0, false)]), null),
new pl.type.Rule(new pl.type.Term("sum_list", [new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Xs")]),new pl.type.Var("S")]), new pl.type.Term(",", [new pl.type.Term("sum_list", [new pl.type.Var("Xs"),new pl.type.Var("Y")]),new pl.type.Term("is", [new pl.type.Var("S"),new pl.type.Term("+", [new pl.type.Var("X"),new pl.type.Var("Y")])])]))
],
// max_list/2
"max_list/2": [
new pl.type.Rule(new pl.type.Term("max_list", [new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Term("[]", [])]),new pl.type.Var("X")]), null),
new pl.type.Rule(new pl.type.Term("max_list", [new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Xs")]),new pl.type.Var("S")]), new pl.type.Term(",", [new pl.type.Term("max_list", [new pl.type.Var("Xs"),new pl.type.Var("Y")]),new pl.type.Term(";", [new pl.type.Term(",", [new pl.type.Term(">=", [new pl.type.Var("X"),new pl.type.Var("Y")]),new pl.type.Term(",", [new pl.type.Term("=", [new pl.type.Var("S"),new pl.type.Var("X")]),new pl.type.Term("!", [])])]),new pl.type.Term("=", [new pl.type.Var("S"),new pl.type.Var("Y")])])]))
],
// min_list/2
"min_list/2": [
new pl.type.Rule(new pl.type.Term("min_list", [new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Term("[]", [])]),new pl.type.Var("X")]), null),
new pl.type.Rule(new pl.type.Term("min_list", [new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Xs")]),new pl.type.Var("S")]), new pl.type.Term(",", [new pl.type.Term("min_list", [new pl.type.Var("Xs"),new pl.type.Var("Y")]),new pl.type.Term(";", [new pl.type.Term(",", [new pl.type.Term("=<", [new pl.type.Var("X"),new pl.type.Var("Y")]),new pl.type.Term(",", [new pl.type.Term("=", [new pl.type.Var("S"),new pl.type.Var("X")]),new pl.type.Term("!", [])])]),new pl.type.Term("=", [new pl.type.Var("S"),new pl.type.Var("Y")])])]))
],
// prod_list/2
"prod_list/2": [
new pl.type.Rule(new pl.type.Term("prod_list", [new pl.type.Term("[]", []),new pl.type.Num(1, false)]), null),
new pl.type.Rule(new pl.type.Term("prod_list", [new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Xs")]),new pl.type.Var("S")]), new pl.type.Term(",", [new pl.type.Term("prod_list", [new pl.type.Var("Xs"),new pl.type.Var("Y")]),new pl.type.Term("is", [new pl.type.Var("S"),new pl.type.Term("*", [new pl.type.Var("X"),new pl.type.Var("Y")])])]))
],
// last/2
"last/2": [
new pl.type.Rule(new pl.type.Term("last", [new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Term("[]", [])]),new pl.type.Var("X")]), null),
new pl.type.Rule(new pl.type.Term("last", [new pl.type.Term(".", [new pl.type.Var("_"),new pl.type.Var("Xs")]),new pl.type.Var("X")]), new pl.type.Term("last", [new pl.type.Var("Xs"),new pl.type.Var("X")]))
],
// prefix/2
"prefix/2": [
new pl.type.Rule(new pl.type.Term("prefix", [new pl.type.Var("Part"),new pl.type.Var("Whole")]), new pl.type.Term("append", [new pl.type.Var("Part"),new pl.type.Var("_"),new pl.type.Var("Whole")]))
],
// nth0/3
"nth0/3": [
new pl.type.Rule(new pl.type.Term("nth0", [new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z")]), new pl.type.Term(";", [new pl.type.Term("->", [new pl.type.Term("var", [new pl.type.Var("X")]),new pl.type.Term("nth", [new pl.type.Num(0, false),new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("_")])]),new pl.type.Term(",", [new pl.type.Term(">=", [new pl.type.Var("X"),new pl.type.Num(0, false)]),new pl.type.Term(",", [new pl.type.Term("nth", [new pl.type.Num(0, false),new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("_")]),new pl.type.Term("!", [])])])]))
],
// nth1/3
"nth1/3": [
new pl.type.Rule(new pl.type.Term("nth1", [new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z")]), new pl.type.Term(";", [new pl.type.Term("->", [new pl.type.Term("var", [new pl.type.Var("X")]),new pl.type.Term("nth", [new pl.type.Num(1, false),new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("_")])]),new pl.type.Term(",", [new pl.type.Term(">", [new pl.type.Var("X"),new pl.type.Num(0, false)]),new pl.type.Term(",", [new pl.type.Term("nth", [new pl.type.Num(1, false),new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("_")]),new pl.type.Term("!", [])])])]))
],
// nth0/4
"nth0/4": [
new pl.type.Rule(new pl.type.Term("nth0", [new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("W")]), new pl.type.Term(";", [new pl.type.Term("->", [new pl.type.Term("var", [new pl.type.Var("X")]),new pl.type.Term("nth", [new pl.type.Num(0, false),new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("W")])]),new pl.type.Term(",", [new pl.type.Term(">=", [new pl.type.Var("X"),new pl.type.Num(0, false)]),new pl.type.Term(",", [new pl.type.Term("nth", [new pl.type.Num(0, false),new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("W")]),new pl.type.Term("!", [])])])]))
],
// nth1/4
"nth1/4": [
new pl.type.Rule(new pl.type.Term("nth1", [new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("W")]), new pl.type.Term(";", [new pl.type.Term("->", [new pl.type.Term("var", [new pl.type.Var("X")]),new pl.type.Term("nth", [new pl.type.Num(1, false),new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("W")])]),new pl.type.Term(",", [new pl.type.Term(">", [new pl.type.Var("X"),new pl.type.Num(0, false)]),new pl.type.Term(",", [new pl.type.Term("nth", [new pl.type.Num(1, false),new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z"),new pl.type.Var("W")]),new pl.type.Term("!", [])])])]))
],
// nth/5
// DO NOT EXPORT
"nth/5": [
new pl.type.Rule(new pl.type.Term("nth", [new pl.type.Var("N"),new pl.type.Var("N"),new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Xs")]),new pl.type.Var("X"),new pl.type.Var("Xs")]), null),
new pl.type.Rule(new pl.type.Term("nth", [new pl.type.Var("N"),new pl.type.Var("O"),new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Xs")]),new pl.type.Var("Y"),new pl.type.Term(".", [new pl.type.Var("X"),new pl.type.Var("Ys")])]), new pl.type.Term(",", [new pl.type.Term("is", [new pl.type.Var("M"),new pl.type.Term("+", [new pl.type.Var("N"),new pl.type.Num(1, false)])]),new pl.type.Term("nth", [new pl.type.Var("M"),new pl.type.Var("O"),new pl.type.Var("Xs"),new pl.type.Var("Y"),new pl.type.Var("Ys")])]))
],
// length/2
"length/2": function( thread, point, atom ) {
var list = atom.args[0], length = atom.args[1];
if( !pl.type.is_variable( length ) && !pl.type.is_integer( length ) ) {
thread.throw_error( pl.error.type( "integer", length, atom.indicator ) );
} else if( pl.type.is_integer( length ) && length.value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", length, atom.indicator ) );
} else {
var newgoal = new pl.type.Term("length", [list, new pl.type.Num(0, false), length]);
if( pl.type.is_integer( length ) )
newgoal = new pl.type.Term( ",", [newgoal, new pl.type.Term( "!", [] )] );
thread.prepend( [new pl.type.State(point.goal.replace(newgoal), point.substitution, point)] );
}
},
// length/3
// DO NOT EXPORT
"length/3": [
new pl.type.Rule(new pl.type.Term("length", [new pl.type.Term("[]", []),new pl.type.Var("N"),new pl.type.Var("N")]), null),
new pl.type.Rule(new pl.type.Term("length", [new pl.type.Term(".", [new pl.type.Var("_"),new pl.type.Var("X")]),new pl.type.Var("A"),new pl.type.Var("N")]), new pl.type.Term(",", [new pl.type.Term("succ", [new pl.type.Var("A"),new pl.type.Var("B")]),new pl.type.Term("length", [new pl.type.Var("X"),new pl.type.Var("B"),new pl.type.Var("N")])]))
],
// replicate/3
"replicate/3": function( thread, point, atom ) {
var elem = atom.args[0], times = atom.args[1], list = atom.args[2];
if( pl.type.is_variable( times ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_integer( times ) ) {
thread.throw_error( pl.error.type( "integer", times, atom.indicator ) );
} else if( times.value < 0 ) {
thread.throw_error( pl.error.domain( "not_less_than_zero", times, atom.indicator ) );
} else if( !pl.type.is_variable( list ) && !pl.type.is_list( list ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
var replicate = new pl.type.Term( "[]" );
for( var i = 0; i < times.value; i++ ) {
replicate = new pl.type.Term( ".", [elem, replicate] );
}
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [replicate, list] ) ), point.substitution, point )] );
}
},
// sort/2
"sort/2": function( thread, point, atom ) {
var list = atom.args[0], expected = atom.args[1];
if( pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( expected ) && !pl.type.is_fully_list( expected ) ) {
thread.throw_error( pl.error.type( "list", expected, atom.indicator ) );
} else {
var arr = [];
var pointer = list;
while( pointer.indicator === "./2" ) {
arr.push( pointer.args[0] );
pointer = pointer.args[1];
}
if( pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_empty_list( pointer ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
var sorted_arr = arr.sort( pl.compare );
for( var i = sorted_arr.length-1; i > 0; i-- ) {
if( sorted_arr[i].equals(sorted_arr[i-1]) )
sorted_arr.splice(i,1);
}
var sorted_list = new pl.type.Term( "[]" );
for( var i = sorted_arr.length-1; i >= 0; i-- ) {
sorted_list = new pl.type.Term( ".", [sorted_arr[i], sorted_list] );
}
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [sorted_list, expected] ) ), point.substitution, point )] );
}
}
},
// msort/2
"msort/2": function( thread, point, atom ) {
var list = atom.args[0], expected = atom.args[1];
if( pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( expected ) && !pl.type.is_fully_list( expected ) ) {
thread.throw_error( pl.error.type( "list", expected, atom.indicator ) );
} else {
var arr = [];
var pointer = list;
while( pointer.indicator === "./2" ) {
arr.push( pointer.args[0] );
pointer = pointer.args[1];
}
if( pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_empty_list( pointer ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
var sorted_arr = arr.sort( pl.compare );
var sorted_list = new pl.type.Term( "[]" );
for( var i = sorted_arr.length - 1; i >= 0; i-- ) {
sorted_list = new pl.type.Term( ".", [sorted_arr[i], sorted_list] );
}
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [sorted_list, expected] ) ), point.substitution, point )] );
}
}
},
// keysort/2
"keysort/2": function( thread, point, atom ) {
var list = atom.args[0], expected = atom.args[1];
if( pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( expected ) && !pl.type.is_fully_list( expected ) ) {
thread.throw_error( pl.error.type( "list", expected, atom.indicator ) );
} else {
var arr = [];
var elem;
var pointer = list;
while( pointer.indicator === "./2" ) {
elem = pointer.args[0];
if( pl.type.is_variable( elem ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( !pl.type.is_term( elem ) || elem.indicator !== "-/2" ) {
thread.throw_error( pl.error.type( "pair", elem, atom.indicator ) );
return;
}
elem.args[0].pair = elem.args[1];
arr.push( elem.args[0] );
pointer = pointer.args[1];
}
if( pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_empty_list( pointer ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
var sorted_arr = arr.sort( pl.compare );
var sorted_list = new pl.type.Term( "[]" );
for( var i = sorted_arr.length - 1; i >= 0; i-- ) {
sorted_list = new pl.type.Term( ".", [new pl.type.Term( "-", [sorted_arr[i], sorted_arr[i].pair] ), sorted_list] );
delete sorted_arr[i].pair;
}
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [sorted_list, expected] ) ), point.substitution, point )] );
}
}
},
// take/3
"take/3": function( thread, point, atom ) {
var number = atom.args[0], list = atom.args[1], take = atom.args[2];
if( pl.type.is_variable( list ) || pl.type.is_variable( number ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_list( list ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else if( !pl.type.is_integer( number ) ) {
thread.throw_error( pl.error.type( "integer", number, atom.indicator ) );
} else if( !pl.type.is_variable( take ) && !pl.type.is_list( take ) ) {
thread.throw_error( pl.error.type( "list", take, atom.indicator ) );
} else {
var i = number.value;
var arr = [];
var pointer = list;
while( i > 0 && pointer.indicator === "./2" ) {
arr.push( pointer.args[0] );
pointer = pointer.args[1];
i--;
}
if( i === 0 ) {
var new_list = new pl.type.Term( "[]" );
for( var i = arr.length - 1; i >= 0; i-- ) {
new_list = new pl.type.Term( ".", [arr[i], new_list] );
}
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [new_list, take] ) ), point.substitution, point )] );
}
}
},
// drop/3
"drop/3": function( thread, point, atom ) {
var number = atom.args[0], list = atom.args[1], drop = atom.args[2];
if( pl.type.is_variable( list ) || pl.type.is_variable( number ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_list( list ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else if( !pl.type.is_integer( number ) ) {
thread.throw_error( pl.error.type( "integer", number, atom.indicator ) );
} else if( !pl.type.is_variable( drop ) && !pl.type.is_list( drop ) ) {
thread.throw_error( pl.error.type( "list", drop, atom.indicator ) );
} else {
var i = number.value;
var arr = [];
var pointer = list;
while( i > 0 && pointer.indicator === "./2" ) {
arr.push( pointer.args[0] );
pointer = pointer.args[1];
i--;
}
if( i === 0 )
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [pointer, drop] ) ), point.substitution, point )] );
}
},
// reverse/2
"reverse/2": function( thread, point, atom ) {
var list = atom.args[0], reversed = atom.args[1];
var ins_list = pl.type.is_instantiated_list( list );
var ins_reversed = pl.type.is_instantiated_list( reversed );
if( pl.type.is_variable( list ) && pl.type.is_variable( reversed ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( list ) && !pl.type.is_fully_list( list ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else if( !pl.type.is_variable( reversed ) && !pl.type.is_fully_list( reversed ) ) {
thread.throw_error( pl.error.type( "list", reversed, atom.indicator ) );
} else if( !ins_list && !ins_reversed ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else {
var pointer = ins_list ? list : reversed;
var new_reversed = new pl.type.Term( "[]", [] );
while( pointer.indicator === "./2" ) {
new_reversed = new pl.type.Term( ".", [pointer.args[0], new_reversed] );
pointer = pointer.args[1];
}
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [new_reversed, ins_list ? reversed : list] ) ), point.substitution, point )] );
}
},
// list_to_set/2
"list_to_set/2": function( thread, point, atom ) {
var list = atom.args[0], lset = atom.args[1];
if( pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else {
var pointer = list;
var elems = [];
while( pointer.indicator === "./2" ) {
elems.push( pointer.args[0] );
pointer = pointer.args[1];
}
if( pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_term( pointer ) || pointer.indicator !== "[]/0" ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else {
var arr = [], nub = new pl.type.Term( "[]", [] );
var match;
for( var i = 0; i < elems.length; i++ ) {
match = false
for( var j = 0; j < arr.length && !match; j++ ) {
match = pl.compare( elems[i], arr[j] ) === 0;
}
if( !match )
arr.push( elems[i] );
}
for( i = arr.length - 1; i >= 0; i-- )
nub = new pl.type.Term( ".", [arr[i],nub] );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [lset,nub] ) ), point.substitution, point )] );
}
}
}
};
};
var exports = ["append/2", "append/3", "member/2", "permutation/2", "maplist/2", "maplist/3", "maplist/4", "maplist/5", "maplist/6", "maplist/7", "maplist/8", "include/3", "exclude/3", "foldl/4", "sum_list/2", "max_list/2", "min_list/2", "prod_list/2", "last/2", "prefix/2", "nth0/3", "nth1/3", "nth0/4", "nth1/4", "length/2", "replicate/3", "select/3", "sort/2", "msort/2", "keysort/2", "take/3", "drop/3", "reverse/2", "list_to_set/2"];
new pl.type.Module( "lists", predicates(), exports );
})( prolog );
(function( pl ) {
var predicates = function() {
return {
// maybe/0
"maybe/0": function( thread, point, _ ) {
if( Math.random() < 0.5 ) {
thread.success( point );
}
},
// maybe/1
"maybe/1": function( thread, point, atom ) {
var num = atom.args[0];
if( Math.random() < num.value ) {
thread.success( point );
}
},
// random/1
"random/1": function( thread, point, atom ) {
var rand = atom.args[0];
if( !pl.type.is_variable( rand ) && !pl.type.is_number( rand ) ) {
thread.throw_error( pl.error.type( "number", rand, atom.indicator ) );
} else {
var gen = Math.random();
thread.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term( "=", [rand, new pl.type.Num( gen, true )] ) ),
point.substitution, point
)] );
}
},
// random/3
"random/3": function( thread, point, atom ) {
var lower = atom.args[0], upper = atom.args[1], rand = atom.args[2];
if( pl.type.is_variable( lower ) || pl.type.is_variable( upper ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_number( lower ) ) {
thread.throw_error( pl.error.type( "number", lower, atom.indicator ) );
} else if( !pl.type.is_number( upper ) ) {
thread.throw_error( pl.error.type( "number", upper, atom.indicator ) );
} else if( !pl.type.is_variable( rand ) && !pl.type.is_number( rand ) ) {
thread.throw_error( pl.error.type( "number", rand, atom.indicator ) );
} else {
if( lower.value < upper.value ) {
var float = lower.is_float || upper.is_float;
var gen = lower.value + Math.random() * (upper.value - lower.value);
if( !float )
gen = Math.floor( gen );
thread.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term( "=", [rand, new pl.type.Num( gen, float )] ) ),
point.substitution, point
)] );
}
}
},
// random_between/3
"random_between/3": function( thread, point, atom ) {
var lower = atom.args[0], upper = atom.args[1], rand = atom.args[2];
if( pl.type.is_variable( lower ) || pl.type.is_variable( upper ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_integer( lower ) ) {
thread.throw_error( pl.error.type( "integer", lower, atom.indicator ) );
} else if( !pl.type.is_integer( upper ) ) {
thread.throw_error( pl.error.type( "integer", upper, atom.indicator ) );
} else if( !pl.type.is_variable( rand ) && !pl.type.is_integer( rand ) ) {
thread.throw_error( pl.error.type( "integer", rand, atom.indicator ) );
} else {
if( lower.value < upper.value ) {
var gen = Math.floor(lower.value + Math.random() * (upper.value - lower.value + 1));
thread.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term( "=", [rand, new pl.type.Num( gen, false )] ) ),
point.substitution, point
)] );
}
}
},
// random_member/2
"random_member/2": function( thread, point, atom ) {
var member = atom.args[0], list = atom.args[1];
if( pl.type.is_variable( list ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else {
var array = [];
var pointer = list;
while( pointer.indicator === "./2" ) {
array.push(pointer.args[0]);
pointer = pointer.args[1];
}
if( array.length > 0 ) {
var gen = Math.floor(Math.random() * array.length);
thread.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term( "=", [member, array[gen]] ) ),
point.substitution, point
)] );
}
}
},
// random_permutation/2
"random_permutation/2": function( thread, point, atom ) {
var i;
var list = atom.args[0], permutation = atom.args[1];
var ins_list = pl.type.is_instantiated_list( list );
var ins_permutation = pl.type.is_instantiated_list( permutation );
if( pl.type.is_variable( list ) && pl.type.is_variable( permutation ) || !ins_list && !ins_permutation ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( list ) && !pl.type.is_fully_list( list ) ) {
thread.throw_error( pl.error.type( "list", list, atom.indicator ) );
} else if( !pl.type.is_variable( permutation ) && !pl.type.is_fully_list( permutation ) ) {
thread.throw_error( pl.error.type( "list", permutation, atom.indicator ) );
} else {
var pointer = ins_list ? list : permutation;
var array = [];
while( pointer.indicator === "./2" ) {
array.push( pointer.args[0] );
pointer = pointer.args[1];
}
for( i = 0; i < array.length; i++ ) {
var rand = Math.floor( Math.random() * array.length );
var tmp = array[i];
array[i] = array[rand];
array[rand] = tmp;
}
var new_list = new pl.type.Term( "[]", [] );
for( i = array.length-1; i >= 0; i-- )
new_list = new pl.type.Term( ".", [array[i], new_list] );
thread.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term( "=", [new_list, ins_list ? permutation : list] ) ),
point.substitution, point
)] );
}
}
};
};
var exports = ["maybe/0", "maybe/1", "random/1", "random/3", "random_between/3", "random_member/2", "random_permutation/2"];
new pl.type.Module( "random", predicates(), exports );
})( prolog );
(function( pl ) {
var predicates = function() {
return {
// time/1
"time/1": function( thread, point, atom ) {
var goal = atom.args[0];
if( pl.type.is_variable( goal ) ) {
thread.throw_error( pl.error.instantiation( thread.level ) );
} else if( !pl.type.is_callable( goal ) ) {
thread.throw_error( pl.error.type( "callable", goal, thread.level ) );
} else {
var points = thread.points;
thread.points = [new pl.type.State( goal, point.substitution, point )];
var t0 = Date.now();
var c0 = pl.statistics.getCountTerms();
var i0 = thread.total_steps;
var format_success = thread.session.format_success;
var format_error = thread.session.format_error;
thread.session.format_success = function(x) { return x.substitution; };
thread.session.format_error = function(x) { return x.goal; };
var callback = function( answer ) {
var t1 = Date.now();
var c1 = pl.statistics.getCountTerms();
var i1 = thread.total_steps;
var newpoints = thread.points;
thread.points = points;
thread.session.format_success = format_success;
thread.session.format_error = format_error;
if( pl.type.is_error( answer ) ) {
thread.throw_error( answer.args[0] );
} else if( answer === null ) {
thread.points = points;
thread.prepend( [point] );
thread.__calls.shift()( null );
} else {
console.log( "% Tau Prolog: executed in " + (t1-t0) + " milliseconds, " + (c1-c0) + " atoms created, " + (i1-i0) + " resolution steps performed.");
if( answer !== false ) {
for( var i = 0; i < newpoints.length; i++ ) {
if( newpoints[i].goal === null )
newpoints[i].goal = new pl.type.Term( "true", [] );
newpoints[i].goal = point.goal.replace( new pl.type.Term( "time", [newpoints[i].goal] ) );
}
thread.points = points;
thread.prepend( newpoints );
thread.prepend( [ new pl.type.State( point.goal.apply(answer).replace(null), answer, point ) ] );
}
}
};
thread.__calls.unshift( callback );
}
},
// statistics/0
"statistics/0": function( thread, point, atom ) {
var stats = "% Tau Prolog statistics";
for(var x in statistics)
stats += "\n%%% " + x + ": " + statistics[x](thread).toString();
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("write", [new pl.type.Term(stats)])),
point.substitution,
point
)]);
},
// statistics/2
"statistics/2": function( thread, point, atom ) {
var key = atom.args[0], value = atom.args[1];
if( !pl.type.is_variable( key ) && !pl.type.is_atom( key ) ) {
thread.throw_error( pl.error.type( "atom", key, atom.indicator ) );
} else if( !pl.type.is_variable( key ) && statistics[key.id] === undefined ) {
thread.throw_error( pl.error.domain( "statistics_key", key, atom.indicator ) );
} else {
if( !pl.type.is_variable( key ) ) {
var value_ = statistics[key.id]( thread );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [value, value_] ) ), point.substitution, point )] );
} else {
var states = [];
for( var x in statistics ) {
var value_ = statistics[x]( thread );
states.push( new pl.type.State( point.goal.replace(
new pl.type.Term( ",", [
new pl.type.Term( "=", [key, new pl.type.Term( x, [] )] ),
new pl.type.Term( "=", [value, value_] )
] )
), point.substitution, point ) );
}
thread.prepend( states );
}
}
}
};
};
var exports = ["time/1", "statistics/0", "statistics/2"];
var statistics = {
// Total number of defined atoms
atoms: function( thread ) {
return new pl.type.Num( pl.statistics.getCountTerms(), false );
},
// Total number of clauses
clauses: function( thread ) {
var total = 0;
for( var x in thread.session.rules )
if( thread.session.rules[x] instanceof Array )
total += thread.session.rules[x].length;
for( var i = 0; i < thread.session.modules.length; i++ ) {
var module = pl.module[thread.session.modules[i]];
for( var j = 0; j < module.exports.length; j++ ) {
var predicate = module.rules[module.exports[j]];
if( predicate instanceof Array )
total += predicate.length;
}
}
return new pl.type.Num( total, false );
},
// Total cpu time
cputime: function( thread ) {
return new pl.type.Num( thread.cpu_time , false );
},
// Time stamp when thread was started
epoch: function( thread ) {
return new pl.type.Num( thread.epoch, false );
},
// Total number of resolution steps
inferences: function( thread ) {
return new pl.type.Num( thread.total_steps, false );
},
// Total number of defined modules
modules: function( thread ) {
return new pl.type.Num( thread.session.modules.length, false );
},
// Total number of predicates
predicates: function( thread ) {
var total = Object.keys( thread.session.rules ).length;
for( var i = 0; i < thread.session.modules.length; i++ ) {
var module = pl.module[thread.session.modules[i]];
total += module.exports.length;
}
return new pl.type.Num( total, false );
},
// [CPU time, CPU time since last]
runtime: function( thread ) {
return new pl.type.Term( ".", [new pl.type.Num( thread.cpu_time, false ), new pl.type.Term( ".", [new pl.type.Num( thread.cpu_time_last, false ), new pl.type.Term( "[]", [] )] )] );
},
// Total number of threads in current session
threads: function( thread ) {
return new pl.type.Num( thread.session.total_threads, false );
}
};
new pl.type.Module( "statistics", predicates(), exports );
})( prolog );
(function( pl ) {
var predicates = function() {
return {
// EVENTS
// bind/4
"bind/4": function( thread, point, atom ) {
var elem = atom.args[0], type = atom.args[1], event = atom.args[2], goal = atom.args[3];
if( pl.type.is_variable( elem ) || pl.type.is_variable( type ) && pl.type.is_variable( goal ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( elem ) ) {
thread.throw_error( pl.error.type( "HTMLObject", elem, atom.indicator ) );
} else if( !pl.type.is_atom( type ) ) {
thread.throw_error( pl.error.type( "atom", type, atom.indicator ) );
} else if( !pl.type.is_variable( event ) && !pl.type.is_dom_event_object( event ) ) {
thread.throw_error( pl.error.type( "DOMEventObject", type, atom.indicator ) );
} else if( !pl.type.is_variable( goal ) ) {
var thread_ = new pl.type.Thread( thread.session );
var eventObject = new pl.type.DOMEvent( type.id );
var links = {};
if( pl.type.is_variable( event ) )
links[event.id] = eventObject;
var subs = new pl.type.Substitution( links );
var handler = function( e ) {
eventObject.event = e;
thread_.add_goal( goal.apply( subs ) );
thread_.answer( thread.__calls[0] );
};
events.add( elem.object, type.id, handler );
elem.object.tau_events = elem.object.tau_events === undefined ? {} : elem.object.tau_events;
if( elem.object.tau_events[type.id] === undefined )
elem.object.tau_events[type.id] = [];
elem.object.tau_events[type.id].push( {goal: goal, fn: handler} );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [eventObject, event] ) ), point.substitution, point )] );
} else {
var event = elem.object.tau_events ? elem.object.tau_events[type.id] : undefined;
if( event !== undefined ) {
var states = [];
for( var i = 0; i < event.length; i++ )
states.push( new pl.type.State( point.goal.replace( new pl.type.Term( "=", [goal, event[i].goal.rename(thread)] ) ), point.substitution, point ) );
thread.prepend( states );
}
}
},
// unbind/2
"unbind/2": function( thread, point, atom ) {
var elem = atom.args[0], type = atom.args[1];
if( pl.type.is_variable( elem ) || pl.type.is_variable( type ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( elem ) ) {
thread.throw_error( pl.error.type( "HTMLObject", elem, atom.indicator ) );
} else if( !pl.type.is_atom( type ) ) {
thread.throw_error( pl.error.type( "atom", type, atom.indicator ) );
} else if( !pl.type.is_variable( goal ) ) {
if( elem.object.tau_events && elem.object.tau_events[type.id] ) {
var event = elem.object.tau_events[type.id];
for( var i = 0; i < event.length; i++ ) {
events.remove( elem.object, type.id, event[i].fn );
}
delete elem.object.tau_events[type.id];
}
thread.success( point );
}
},
// unbind/3
"unbind/3": function( thread, point, atom ) {
var elem = atom.args[0], type = atom.args[1], goal = atom.args[2];
if( pl.type.is_variable( elem ) || pl.type.is_variable( type ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( elem ) ) {
thread.throw_error( pl.error.type( "HTMLObject", elem, atom.indicator ) );
} else if( !pl.type.is_atom( type ) ) {
thread.throw_error( pl.error.type( "atom", type, atom.indicator ) );
} else if( !pl.type.is_variable( goal ) && !pl.type.is_term( goal ) ) {
thread.throw_error( pl.error.type( "term", goal, atom.indicator ) );
} else if( !pl.type.is_variable( goal ) ) {
if( elem.object.tau_events && elem.object.tau_events[type.id] ) {
var event = elem.object.tau_events[type.id];
var newevents = [];
for( var i = 0; i < event.length; i++ ) {
if( pl.unify( event[i].goal, goal ) !== null ) {
events.remove( elem.object, type.id, event[i].fn );
} else {
newevents.push( event[i] );
}
}
elem.object.tau_events[type.id] = newevents;
}
thread.success( point );
}
},
// event_property/3
"event_property/3": function( thread, point, atom ) {
var event = atom.args[0], prop = atom.args[1], val = atom.args[2]
if( pl.type.is_variable( event ) || pl.type.is_variable( prop ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_event_object( event ) ) {
thread.throw_error( pl.error.type( "DOMEventObject", event, atom.indicator ) );
} else if( !pl.type.is_atom( prop ) ) {
thread.throw_error( pl.error.type( "atom", prop, atom.indicator ) );
} else if( !pl.type.is_variable( val ) && !pl.type.is_atomic( val ) ) {
thread.throw_error( pl.error.type( "atomic", val, atom.indicator ) );
} else {
if( event.event !== null && event.event[prop.id] ) {
var value = event.event[prop.id];
value = isNaN(value) ? new pl.type.Term( value, [] ) : new pl.type.Num( value );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [value, val] ) ), point.substitution, point )] );
}
}
},
// prevent_default/1
"prevent_default/1": function( thread, point, atom ) {
var event = atom.args[0];
if( pl.type.is_variable( event ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_event_object( event ) ) {
thread.throw_error( pl.error.type( "eventObject", event, atom.indicator ) );
} else {
if( event.event !== null ) {
event.event.preventDefault();
thread.success( point );
}
}
},
// EFFECTS
// hide/1
"hide/1": function( thread, point, atom ) {
var element = atom.args[0];
if( pl.type.is_variable( element ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( element ) ) {
thread.throw_error( pl.error.type( "HTMLObject", element, atom.indicator ) );
} else {
var state = document.defaultView.getComputedStyle( element.object, "" ).display;
if( state !== undefined && state !== "none" )
element.object.tau_display = state;
element.object.style.display = "none";
thread.success( point );
}
},
// show/1
"show/1": function( thread, point, atom ) {
var element = atom.args[0];
if( pl.type.is_variable( element ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( element ) ) {
thread.throw_error( pl.error.type( "HTMLObject", element, atom.indicator ) );
} else {
element.object.style.display = element.object.tau_display !== undefined ? element.object.tau_display : "block";
thread.success( point );
}
},
// toggle/1
"toggle/1": [
new pl.type.Rule(new pl.type.Term("toggle", [new pl.type.Var("X")]), new pl.type.Term(";", [new pl.type.Term("->", [new pl.type.Term(",", [new pl.type.Term("style", [new pl.type.Var("X"),new pl.type.Term("display", []),new pl.type.Var("Y")]),new pl.type.Term("=", [new pl.type.Var("Y"),new pl.type.Term("none", [])])]),new pl.type.Term("show", [new pl.type.Var("X")])]),new pl.type.Term("hide", [new pl.type.Var("X")])]))
],
// DOM MANIPULATION
// document/1
"document/1": function( session, point, atom) {
var doc = atom.args[0];
var newdoc = new pl.type.DOM( document );
session.prepend( [new pl.type.State(
point.goal.replace(new pl.type.Term("=", [doc, newdoc])),
point.substitution,
point
)] );
},
// head/1
"head/1": function( session, point, atom) {
var head = atom.args[0];
var newhead = new pl.type.DOM( document.head );
session.prepend( [new pl.type.State(
point.goal.replace(new pl.type.Term("=", [head, newhead])),
point.substitution,
point
)] );
},
// body/1
"body/1": function( session, point, atom) {
var body = atom.args[0];
var newbody = new pl.type.DOM( document.body );
session.prepend( [new pl.type.State(
point.goal.replace(new pl.type.Term("=", [body, newbody])),
point.substitution,
point
)] );
},
// get_by_id/2
"get_by_id/2": function( session, point, atom ) {
var id = atom.args[0], object = atom.args[1];
if( pl.type.is_variable( id ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( id ) ) {
session.throw_error( pl.error.type( "atom", id, atom.indicator ) );
} else if( !pl.type.is_variable( object ) && !pl.type.is_dom_object( object ) ) {
session.throw_error( pl.error.type( "HTMLObject", object, atom.indicator ) );
} else {
var element = document.getElementById( id.id );
if( element ) {
var html = new pl.type.DOM( element );
session.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [html, object] ) ), point.substitution, point )] );
}
}
},
// get_by_class/2
"get_by_class/2": [
new pl.type.Rule(new pl.type.Term("get_by_class", [new pl.type.Var("Class"),new pl.type.Var("Html")]), new pl.type.Term(",", [new pl.type.Term("document", [new pl.type.Var("D")]),new pl.type.Term("get_by_class", [new pl.type.Var("D"),new pl.type.Var("Class"),new pl.type.Var("Html")])]))
],
// get_by_class/3
"get_by_class/3": function( session, point, atom ) {
var parent = atom.args[0], name = atom.args[1], object = atom.args[2];
if( pl.type.is_variable( parent ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( parent ) ) {
session.throw_error( pl.error.type( "HTMLObject", parent, atom.indicator ) );
} else if( pl.type.is_variable( name ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( name ) ) {
session.throw_error( pl.error.type( "atom", name, atom.indicator ) );
} else if( !pl.type.is_variable( object ) && !pl.type.is_dom_object( object ) ) {
session.throw_error( pl.error.type( "HTMLObject", object, atom.indicator ) );
} else {
var elements = parent.object.getElementsByClassName( name.id );
if( elements ) {
var states = [];
for( var i = 0; i < elements.length; i++ ) {
var html = new pl.type.DOM( elements[i] );
states.push( new pl.type.State( point.goal.replace( new pl.type.Term( "=", [html, object] ) ), point.substitution, point ) );
}
session.prepend( states );
}
}
},
// get_by_tag/2
"get_by_tag/2": [
new pl.type.Rule(new pl.type.Term("get_by_tag", [new pl.type.Var("Tag"),new pl.type.Var("Html")]), new pl.type.Term(",", [new pl.type.Term("document", [new pl.type.Var("D")]),new pl.type.Term("get_by_tag", [new pl.type.Var("D"),new pl.type.Var("Tag"),new pl.type.Var("Html")])]))
],
// get_by_tag/3
"get_by_tag/3": function( session, point, atom ) {
var parent = atom.args[0], tag = atom.args[1], object = atom.args[2];
if( pl.type.is_variable( parent ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( parent ) ) {
session.throw_error( pl.error.type( "HTMLObject", parent, atom.indicator ) );
} else if( pl.type.is_variable( tag ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( tag ) ) {
session.throw_error( pl.error.type( "atom", tag, atom.indicator ) );
} else if( !pl.type.is_variable( object ) && !pl.type.is_dom_object( object ) ) {
session.throw_error( pl.error.type( "HTMLObject", object, atom.indicator ) );
} else {
var elements = parent.object.getElementsByTagName( tag.id );
if( elements ) {
var states = [];
for( var i = 0; i < elements.length; i++ ) {
var html = new pl.type.DOM( elements[i] );
states.push( new pl.type.State( point.goal.replace( new pl.type.Term( "=", [html, object] ) ), point.substitution, point ) );
}
session.prepend( states );
}
}
},
// get_by_name/2
"get_by_name/2": function( session, point, atom ) {
var name = atom.args[0], object = atom.args[1];
if( pl.type.is_variable( name ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( name ) ) {
session.throw_error( pl.error.type( "atom", name, atom.indicator ) );
} else if( !pl.type.is_variable( object ) && !pl.type.is_dom_object( object ) ) {
session.throw_error( pl.error.type( "HTMLObject", object, atom.indicator ) );
} else {
var elements = document.getElementsByName( name.id );
if( elements ) {
var states = [];
for( var i = 0; i < elements.length; i++ ) {
var html = new pl.type.DOM( elements[i] );
states.push( new pl.type.State( point.goal.replace( new pl.type.Term( "=", [html, object] ) ), point.substitution, point ) );
}
session.prepend( states );
}
}
},
// get_style/3
"get_style/3": function( session, point, atom ) {
var html = atom.args[0], property = atom.args[1], value = atom.args[2];
if( pl.type.is_variable( html ) || pl.type.is_variable( property ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( html ) ) {
session.throw_error( pl.error.type( "HTMLObject", selector, atom.indicator ) );
} else if( !pl.type.is_atom( property ) ) {
session.throw_error( pl.error.type( "atom", property, atom.indicator ) );
} else {
if( html.object === document ) return;
var style = document.defaultView.getComputedStyle( html.object, "" )[property.id] || "";
if( style === '' && html.object.style[property.id] )
style = html.object.style[property.id];
var html_value = styleToProlog( style );
session.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [value, html_value] ) ), point.substitution, point )] );
}
},
// set_style/3
"set_style/3": function( session, point, atom ) {
var html = atom.args[0], property = atom.args[1], value = atom.args[2];
var styleValue = styleFromProlog( value );
var ground = pl.type.is_ground( value );
if( pl.type.is_variable( html ) || pl.type.is_variable( property ) || !ground ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( html ) ) {
session.throw_error( pl.error.type( "HTMLObject", selector, atom.indicator ) );
} else if( !pl.type.is_atom( property ) ) {
session.throw_error( pl.error.type( "atom", property, atom.indicator ) );
} else if( styleValue === false ) {
session.throw_error( pl.error.domain( "style_value", value, atom.indicator ) );
} else {
if( html.object === document ) return;
html.object.style[property.id] = styleValue;
session.success( point );
}
},
// style/3
"style/3": function( session, point, atom ) {
var html = atom.args[0], property = atom.args[1], value = atom.args[2];
var styleValue = styleFromProlog( value );
var ground = pl.type.is_ground( value );
if( pl.type.is_variable( html ) || pl.type.is_variable( property ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( html ) ) {
session.throw_error( pl.error.type( "HTMLObject", html, atom.indicator ) );
} else if( !pl.type.is_atom( property ) ) {
session.throw_error( pl.error.type( "atom", property, atom.indicator ) );
} else if( !pl.type.is_variable( value ) && ground && styleValue === false ) {
session.throw_error( pl.error.domain( "style_value", value, atom.indicator ) );
} else {
if( html.object === document ) return;
if( !ground ) {
var style = document.defaultView.getComputedStyle( html.object, "" )[property.id] || "";
if( style === '' && html.object.style[property.id] )
style = html.object.style[property.id];
var html_value = styleToProlog( style );
session.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [value, html_value] ) ), point.substitution, point )] );
} else {
html.object.style[property.id] = styleValue;
session.success( point );
}
}
},
// get_attr/3
"get_attr/3": function( session, point, atom ) {
var html = atom.args[0], attr = atom.args[1], value = atom.args[2];
if( pl.type.is_variable( html ) || pl.type.is_variable( attr ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( html ) ) {
session.throw_error( pl.error.type( "HTMLObject", selector, atom.indicator ) );
} else if( !pl.type.is_atom( attr ) ) {
session.throw_error( pl.error.type( "atom", attr, atom.indicator ) );
} else {
if( html.object === document ) return;
var html_value = attr.id === "value" ? new pl.type.Term(html.object.value) : styleToProlog(html.object.getAttribute(attr.id));
if( html_value !== null && html_value !== undefined )
session.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term( "=", [value, html_value] ) ),
point.substitution, point
)] );
}
},
// set_attr/3
"set_attr/3": function( session, point, atom ) {
var html = atom.args[0], attr = atom.args[1], value = atom.args[2];
var styleValue = styleFromProlog( value );
var ground = pl.type.is_ground( value );
if( pl.type.is_variable( html ) || pl.type.is_variable( attr ) || !ground ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( html ) ) {
session.throw_error( pl.error.type( "HTMLObject", selector, atom.indicator ) );
} else if( !pl.type.is_atom( attr ) ) {
session.throw_error( pl.error.type( "atom", attr, atom.indicator ) );
} else if( styleValue === false ) {
session.throw_error( pl.error.domain( "attribute_value", value, atom.indicator ) );
} else {
if( html.object === document ) return;
if( attr.id === "value" ) {
html.object.value = styleValue;
} else {
html.object.setAttribute( attr.id, styleValue );
}
session.success( point );
}
},
// attr/3
"attr/3": function( session, point, atom ) {
var html = atom.args[0], attr = atom.args[1], value = atom.args[2];
var styleValue = styleFromProlog( value );
var ground = pl.type.is_ground( value );
if( pl.type.is_variable( html ) || pl.type.is_variable( attr ) ) {
session.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( html ) ) {
session.throw_error( pl.error.type( "HTMLObject", selector, atom.indicator ) );
} else if( !pl.type.is_atom( attr ) ) {
session.throw_error( pl.error.type( "atom", attr, atom.indicator ) );
} else if( !pl.type.is_variable( value ) && ground && styleValue === false ) {
session.throw_error( pl.error.domain( "attribute_value", value, atom.indicator ) );
} else {
if( html.object === document ) return;
if( !ground ) {
var html_value = attr.id === "value" ? new pl.type.Term(html.object.value) : styleToProlog(html.object.getAttribute(attr.id));
if( html_value !== null && html_value !== undefined )
session.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term( "=", [value, html_value] ) ),
point.substitution, point
)] );
} else {
if( attr.id === "value" ) {
html.object.value = styleValue;
} else {
html.object.setAttribute( attr.id, styleValue );
}
session.success( point );
}
}
},
// get_html/2
"get_html/2": function( thread, point, atom ) {
var html = atom.args[0], value = atom.args[1];
if( pl.type.is_variable( html ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( html ) ) {
session.throw_error( pl.error.type( "HTMLObject", html, atom.indicator ) );
} else {
if( html.object === document ) return;
var inner = new pl.type.Term( html.object.innerHTML );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [inner, value] ) ), point.substitution, point )] );
}
},
// set_html/2
"set_html/2": function( thread, point, atom ) {
var html = atom.args[0], value = atom.args[1];
if( pl.type.is_variable( html ) || pl.type.is_variable( value ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( html ) ) {
session.throw_error( pl.error.type( "HTMLObject", html, atom.indicator ) );
} else {
if( html.object === document ) return;
if( pl.type.is_atom( value ) )
html.object.innerHTML = value.id;
else
html.object.innerHTML = value.toString();
thread.success( point );
}
},
// html/2
"html/2": function( thread, point, atom ) {
var html = atom.args[0], value = atom.args[1];
if( pl.type.is_variable( html ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else {
if( html.object === document ) return;
if( pl.type.is_variable( value ) ) {
var inner = new pl.type.Term( html.object.innerHTML );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [inner, value] ) ), point.substitution, point )] );
} else {
if( pl.type.is_atom( value ) )
html.object.innerHTML = value.id;
else
html.object.innerHTML = value.toString();
thread.success( point );
}
}
},
// create/2
"create/2": function( thread, point, atom ) {
var tag = atom.args[0], element = atom.args[1];
if( pl.type.is_variable( tag ) && pl.type.is_variable( element ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( tag ) && !pl.type.is_atom( tag ) ) {
thread.throw_error( pl.error.type( "atom", tag, atom.indicator ) );
} else if( !pl.type.is_variable( element ) && !pl.type.is_dom_object( element ) ) {
thread.throw_error( pl.error.type( "HTMLObject", element, atom.indicator ) );
} else if( pl.type.is_variable( element ) ) {
var node = new pl.type.DOM( document.createElement( tag.id ) );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [element, node] ) ), point.substitution, point )] );
} else if( pl.type.is_variable( element ) ) {
var node = new pl.type.DOM( document.createElement( tag.id.toLowerCase() ) );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [element, node] ) ), point.substitution, point )] );
} else if( pl.type.is_variable( tag ) ) {
var type = new pl.type.Term( element.object.nodeName.toLowerCase() );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [type, tag] ) ), point.substitution, point )] );
}
},
// parent_of/2
"parent_of/2": function( thread, point, atom ) {
var child = atom.args[0], parent = atom.args[1];
if( pl.type.is_variable( parent ) && pl.type.is_variable( child ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( parent ) && !pl.type.is_dom_object( parent ) ) {
thread.throw_error( pl.error.type( "HTMLObject", parent, atom.indicator ) );
} else if( !pl.type.is_variable( child ) && !pl.type.is_dom_object( child ) ) {
thread.throw_error( pl.error.type( "HTMLObject", child, atom.indicator ) );
} else if( pl.type.is_variable( child ) ) {
var children = parent.object.children;
var states = [];
for( var i = 0; i < children.length; i++ ) {
states.push( new pl.type.State( point.goal.replace( new pl.type.Term( "=", [child, new pl.type.DOM( children[i] )] ) ), point.substitution, point ) );
}
thread.prepend( states );
} else {
if( child.object.parentNode ) {
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [parent, new pl.type.DOM( child.object.parentNode )] ) ), point.substitution, point )] );
}
}
},
// sibling/2
"sibling/2": function( thread, point, atom ) {
var left = atom.args[0], right = atom.args[1];
if( pl.type.is_variable( left ) && pl.type.is_variable( right ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( left ) && !pl.type.is_dom_object( left ) ) {
thread.throw_error( pl.error.type( "HTMLObject", left, atom.indicator ) );
} else if( !pl.type.is_variable( right ) && !pl.type.is_dom_object( right ) ) {
thread.throw_error( pl.error.type( "HTMLObject", right, atom.indicator ) );
} else {
if( pl.type.is_variable( left ) && right.object.previousElementSibling ) {
var elem = new pl.type.DOM( right.object.previousElementSibling );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [left, elem] ) ), point.substitution, point )] );
} else if( !pl.type.is_variable( left ) && left.object.nextElementSibling ) {
var elem = new pl.type.DOM( left.object.nextElementSibling );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [right, elem] ) ), point.substitution, point )] );
}
}
},
// remove/1
"remove/1": function( thread, point, atom ) {
var element = atom.args[0];
if( pl.type.is_variable( element ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( element ) ) {
thread.throw_error( pl.error.type( "HTMLObject", element, atom.indicator ) );
} else {
if( element.object.parentNode ) {
element.object.parentNode.removeChild( element.object );
thread.success( point );
}
}
},
// insert_after/2
"insert_after/2": function( thread, point, atom ) {
var element = atom.args[0], reference = atom.args[1];
if( pl.type.is_variable( element ) || pl.type.is_variable( reference ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( element ) ) {
thread.throw_error( pl.error.type( "HTMLObject", element, atom.indicator ) );
} else if( !pl.type.is_dom_object( reference ) ) {
thread.throw_error( pl.error.type( "HTMLObject", reference, atom.indicator ) );
} else {
if( reference.object.parentNode ) {
reference.object.parentNode.insertBefore(element.object, reference.object.nextSibling);
thread.success( point );
}
}
},
// insert_before/2
"insert_before/2": function( thread, point, atom ) {
var element = atom.args[0], reference = atom.args[1];
if( pl.type.is_variable( element ) || pl.type.is_variable( reference ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( element ) ) {
thread.throw_error( pl.error.type( "HTMLObject", element, atom.indicator ) );
} else if( !pl.type.is_dom_object( reference ) ) {
thread.throw_error( pl.error.type( "HTMLObject", reference, atom.indicator ) );
} else {
if( reference.object.parentNode ) {
reference.object.parentNode.insertBefore(element.object, reference.object);
thread.success( point );
}
}
},
// prepend_child/2
"prepend_child/2": function( thread, point, atom ) {
var parent = atom.args[0], child = atom.args[1];
if( pl.type.is_variable( parent ) || pl.type.is_variable( child ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( parent ) ) {
thread.throw_error( pl.error.type( "HTMLObject", parent, atom.indicator ) );
} else if( !pl.type.is_dom_object( child ) ) {
thread.throw_error( pl.error.type( "HTMLObject", child, atom.indicator ) );
} else {
if( parent.object.firstChild )
parent.object.insertBefore( child.object, parent.object.firstChild );
else
parent.object.appendChild( child.object );
thread.success( point );
}
},
// append_child/2
"append_child/2": function( thread, point, atom ) {
var parent = atom.args[0], child = atom.args[1];
if( pl.type.is_variable( parent ) || pl.type.is_variable( child ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( parent ) ) {
thread.throw_error( pl.error.type( "HTMLObject", parent, atom.indicator ) );
} else if( !pl.type.is_dom_object( child ) ) {
thread.throw_error( pl.error.type( "HTMLObject", child, atom.indicator ) );
} else {
parent.object.appendChild( child.object );
thread.success( point );
}
},
// add_class/2
"add_class/2": function( thread, point, atom ) {
var element = atom.args[0], name = atom.args[1];
if( pl.type.is_variable( element ) || pl.type.is_variable( name ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( element ) ) {
thread.throw_error( pl.error.type( "HTMLObject", element, atom.indicator ) );
} else if( !pl.type.is_atom( name ) ) {
thread.throw_error( pl.error.type( "atom", name, atom.indicator ) );
} else {
var arr = element.object.className.split(" ");
if( arr.indexOf( name.id ) === -1 ) {
element.object.className += " " + name.id;
}
thread.success( point );
}
},
// remove_class/2
"remove_class/2": function( thread, point, atom ) {
var element = atom.args[0], name = atom.args[1];
if( pl.type.is_variable( element ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( element ) ) {
thread.throw_error( pl.error.type( "HTMLObject", element, atom.indicator ) );
} else if( !pl.type.is_atom( name ) && !pl.type.is_variable( name ) ) {
thread.throw_error( pl.error.type( "atom", name, atom.indicator ) );
} else {
var arr = element.object.className.split(" ");
if( pl.type.is_variable( name ) ) {
var states = [];
for( var i = 0; i < arr.length; i++ ) {
states.push( new pl.type.State( point.goal.replace(
new pl.type.Term( ",", [
new pl.type.Term( "=", [name, new pl.type.Term( arr[i], [] )] ),
new pl.type.Term( "remove_class", [element, name] )
] )
), point.substitution, point ) );
}
thread.prepend( states );
} else {
var newclasses = "";
for( var i = 0; i < arr.length; i++ )
if( arr[i] !== name.id )
newclasses += arr[i] + " ";
element.object.className = newclasses;
thread.success( point );
}
}
},
// has_class/2
"hasClass/2": function( thread, point, atom ) {
var element = atom.args[0], name = atom.args[1];
if( pl.type.is_variable( element ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_dom_object( element ) ) {
thread.throw_error( pl.error.type( "HTMLObject", element, atom.indicator ) );
} else if( !pl.type.is_atom( name ) && !pl.type.is_variable( name ) ) {
thread.throw_error( pl.error.type( "atom", name, atom.indicator ) );
} else {
var arr = element.object.className.split(" ");
if( pl.type.is_variable( name ) ) {
var states = [];
for( var i = 0; i < arr.length; i++ )
states.push( new pl.type.State( point.goal.replace( new pl.type.Term( "=", [name, new pl.type.Term( arr[i], [] )] ) ), point.substitution, point ) );
thread.prepend( states );
} else {
if( arr.indexOf( name.id ) !== -1 )
thread.success( point );
}
}
}
};
};
var exports = ["document/1", "head/1", "body/1", "show/1", "hide/1", "toggle/1", "create/2", "get_by_id/2", "get_by_tag/2", "get_by_tag/3", "get_by_class/2", "get_by_class/3", "get_by_name/2", "attr/3", "set_attr/3", "get_attr/3", "style/3", "set_style/3", "get_style/3", "html/2", "set_html/2", "get_html/2", "parent_of/2", "insert_after/2", "insert_before/2", "append_child/2", "prepend_child/2", "sibling/2", "remove/1", "add_class/2", "remove_class/2", "has_class/2", "bind/4", "unbind/2", "unbind/3", "event_property/3", "prevent_default/1"];
// DOM HTML OBJECTS
// Get value of style from Prolog object
function styleFromProlog( obj ) {
if( obj === undefined || obj === null )
return false;
else if( pl.type.is_number( obj ) )
return obj.value;
else if( pl.type.is_term( obj ) && obj.args.length === 0 )
return obj.id;
else if( pl.type.is_term( obj ) && obj.indicator === "px/1" && pl.type.is_number( obj.args[0] ) )
return obj.args[0].value.toString() + "px";
else if( pl.type.is_term( obj ) && obj.indicator === "%/1" && pl.type.is_number( obj.args[0] ) )
return obj.args[0].value.toString() + "%";
else if( pl.type.is_term( obj ) && obj.indicator === "url/1" && pl.type.is_atom( obj.args[0] ) )
return "url(\"" + obj.args[0].id + "\")";
else if( pl.type.is_term( obj ) && obj.indicator === "rgb/3" && pl.type.is_integer( obj.args[0] ) && pl.type.is_integer( obj.args[1] ) && pl.type.is_integer( obj.args[2] ) )
return obj.toString();
return false;
}
// Get value of style to Prolog object
function styleToProlog( str ) {
if( str === undefined || str === null )
return
else if( /^-?[0-9]*\.?[0-9]*\s*px\s*$/.test( str ) )
return new pl.type.Term( "px", [new pl.type.Num( parseInt( str ) )] );
else if( /^-?[0-9]*\.?[0-9]*\s*\%\s*$/.test( str ) )
return new pl.type.Term( "%", [new pl.type.Num( parseFloat( str ) )] );
else if( /^url\(["'].*["']\)$/.test( str ) )
return new pl.type.Term( "url", [new pl.type.Term( str.substring(5, str.length-2) )] );
else if( /^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)\s*$/.test( str ) ) {
var rgb = str.replace("rgb","").replace("(","").replace(")","").split(",");
return new pl.type.Term( "rgb", [new pl.type.Num(parseInt(rgb[0]), false), new pl.type.Num(parseInt(rgb[1]), false), new pl.type.Num(parseInt(rgb[2]), false)] );
}
return new pl.type.Term( str.toString(), [] );
}
// Is a DOM object
pl.type.is_dom_object = function( obj ) {
return obj instanceof pl.type.DOM;
};
// Ordering relation
pl.type.order.push( pl.type.DOM );
// DOM Prolog object
pl.type.DOM = function( object ) {
this.object = object;
}
// toString
pl.type.DOM.prototype.toString = function() {
return "<html>(" + (this.object.id !== "" && this.object.id !== undefined ? "#" + this.object.id : this.object.nodeName.toLowerCase().replace("#", "")) + ")";
};
// clone
pl.type.DOM.prototype.clone = function() {
return new pl.type.DOM( this.object );
};
// equals
pl.type.DOM.prototype.equals = function( obj ) {
return pl.type.is_dom_object( obj ) && this.object === obj.object;
};
// rename
pl.type.DOM.prototype.rename = function( _ ) {
return this;
};
// get variables
pl.type.DOM.prototype.variables = function() {
return [];
};
// apply substitutions
pl.type.DOM.prototype.apply = function( _ ) {
return this;
};
// unify
pl.type.DOM.prototype.unify = function( obj, _ ) {
if( pl.type.is_dom_object( obj ) && this.object === obj.object ) {
return new pl.type.State( obj, new pl.type.Substitution() );
}
return null;
};
// interpret
pl.type.DOM.prototype.interpret = function( indicator ) {
return pl.error.instantiation( indicator );
};
// compare
pl.type.DOM.prototype.compare = function( obj ) {
if( this.object === obj.object ) {
return 0;
} else if( this.object < obj.object ) {
return -1;
} else if( this.object > obj.object ) {
return 1;
}
};
// to javascript
pl.type.DOM.prototype.toJavaScript = function() {
return this.object;
};
// from javascript
pl.fromJavaScript.test.dom = function( obj ) {
return obj instanceof HTMLElement;
};
pl.fromJavaScript.conversion.dom = function( obj ) {
return new pl.type.DOM( obj );
};
// from javascript (collection)
pl.fromJavaScript.test.dom_collection = function( obj ) {
return obj instanceof HTMLCollection;
};
pl.fromJavaScript.conversion.dom_collection = function( obj ) {
var arr = Array.prototype.slice.call( obj, 0 );
return pl.fromJavaScript.apply( arr );
};
// Streamable
pl.type.DOM.prototype.stream = function( options, mode ) {
if( mode === "write" )
if( this.object instanceof HTMLInputElement )
this.object.value = "";
else
this.object.innerHTML = "";
return {
object: this.object,
get: function( length, position ) {
var text;
if( this.object instanceof HTMLInputElement )
text = this.object.value.substring( position, position+length );
else
text = this.object.innerHTML;
if( position >= text.length )
return "end_of_html";
return text.substring( position, position+length );
},
put: function( text, position ) {
if( position === "end_of_file" ) {
if( this.object instanceof HTMLInputElement )
this.object.value += text;
else
this.object.innerHTML += text;
return true;
} else if( position === "past_end_of_file" ) {
return null;
} else {
if( this.object instanceof HTMLInputElement )
this.object.value = this.object.value.substring(0, position) + text + this.object.value.substring(position+text.length);
else
this.object.innerHTML = this.object.innerHTML.substring(0, position) + text + this.object.innerHTML.substring(position+text.length);
return true;
}
},
get_byte: function( position ) {
if( position === "end_of_stream" )
return -1;
var index = Math.floor(position/2);
var text;
if( this.object instanceof HTMLInputElement )
text = this.object.value.substring( position, position+length );
else
text = this.object.innerHTML;
if( text.length <= index )
return -1;
var code = pl.utils.codePointAt( text[Math.floor(position/2)], 0 );
if( position % 2 === 0 )
return code & 0xff;
else
return code / 256 >>> 0;
},
put_byte: function( byte, position ) {
var text;
if( this.object instanceof HTMLInputElement )
text = this.object.value;
else
text = this.object.innerHTML;
var index = position === "end_of_stream" ? text.length : Math.floor(position/2);
if( text.length < index )
return null;
var code = text.length === index ? -1 : pl.utils.codePointAt( text[Math.floor(position/2)], 0 );
if( position % 2 === 0 ) {
code = code / 256 >>> 0;
code = ((code & 0xff) << 8) | (byte & 0xff);
} else {
code = code & 0xff;
code = ((byte & 0xff) << 8) | (code & 0xff);
}
if( text.length === index )
text += pl.utils.fromCodePoint( code );
else
text = text.substring( 0, index ) + pl.utils.fromCodePoint( code ) + text.substring( index+1 );
if( this.object instanceof HTMLInputElement )
this.object.value = text;
else
this.object.innerHTML = text;
return true;
},
flush: function() {
return true;
},
close: function() {
return true;
}
};
};
// DOM EVENT OBJECTS
// Is a DOM Event object
pl.type.is_dom_event_object = function( obj ) {
return obj instanceof pl.type.DOMEvent;
};
// Ordering relation
pl.type.order.push( pl.type.DOMEvent );
// DOM Event Prolog object
pl.type.DOMEvent = function( type, event, epoch ) {
this.type = type;
this.event = event || null;
this.epoch = epoch || (new Date).getTime();
}
// toString
pl.type.DOMEvent.prototype.toString = function() {
return "<event>(" + this.type.toLowerCase() + ")";
};
// clone
pl.type.DOMEvent.prototype.clone = function() {
return new pl.type.DOMEvent( this.type, this.event, this.epoch );
};
// equals
pl.type.DOMEvent.prototype.equals = function( obj ) {
return pl.type.is_dom_event_object( obj ) && this.type === obj.type && this.epoch === obj.epoch;
};
// rename
pl.type.DOMEvent.prototype.rename = function( _ ) {
return this;
};
// get variables
pl.type.DOMEvent.prototype.variables = function() {
return [];
};
// apply substitutions
pl.type.DOMEvent.prototype.apply = function( _ ) {
return this;
};
// unify
pl.type.DOMEvent.prototype.unify = function( obj, _ ) {
if( pl.type.is_dom_event_object( obj ) && this.type === obj.type && this.epoch === obj.epoch ) {
return new pl.type.State( obj, new pl.type.Substitution() );
}
return null;
};
// interpret
pl.type.DOMEvent.prototype.interpret = function( indicator ) {
return pl.error.instantiation( indicator );
};
// compare
pl.type.DOMEvent.prototype.compare = function( obj ) {
if( this.epoch === obj.epoch ) {
return 0;
} else if( this.epoch < obj.epoch ) {
return -1;
} else if( this.epoch > obj.epoch ) {
return 1;
}
};
// to javascript
pl.type.DOMEvent.prototype.toJavaScript = function() {
return this.event;
};
// from javascript
pl.fromJavaScript.test.event = function( obj ) {
return obj instanceof Event;
};
pl.fromJavaScript.conversion.event = function( obj ) {
return new pl.type.DOMEvent( obj.type, obj );
};
// EVENT HANDLING
var events = (function() {
var tau_fn_event = {};
var add = function(element, evt, fn) {
if(element.addEventListener !== undefined) {
element.addEventListener(evt, fn);
return true;
}
else if(element.attachEvent !== undefined) {
element.attachEvent("on" + evt, fn);
return true;
}
var prop = element["on" + evt];
var fns = [];
if(prop) {
if(prop.tau_fn_event === tau_fn_event) {
if(prop.fns.indexOf(fn) === -1 )
prop.fns.push(fn);
return true;
} else {
fns.push(prop);
}
}
fns.push(fn);
element["on" + evt] = function(e) {
for(var i = 0; i < fns.length; i++)
fns[i].call(element, e, element);
};
element["on" + evt].fns = fns;
element["on" + evt].tau_fn_event = tau_fn_event;
return true;
};
var remove = function(element, evt, fn) {
if(element.removeEventListener) {
element.removeEventListener(evt, fn);
return true;
}
else if(element.detachEvent) {
element.detachEvent("on" + evt, fn);
return true;
}
else if(element["on" + evt]) {
var f = element["on" + evt];
if(f === fn)
element["on" + evt] = undefined;
else if(f.tau_fn_event === tau_fn_event) {
for(var i = 0; i < f.fns.length; i++) {
if(f.fns[i] === fn) {
f.fns.splice(i, 1);
break;
}
}
return true;
}
else
return false;
}
return true;
};
return {
add: add,
remove: remove
};
})();
new pl.type.Module( "dom", predicates(), exports );
})( prolog );
(function( pl ) {
var predicates = function() {
return {
// global/1
"global/1": function( thread, point, atom ) {
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [atom.args[0], pl.fromJavaScript.apply(pl.__env)] ) ), point.substitution, point )] );
},
// apply/3:
"apply/3": [
new pl.type.Rule(new pl.type.Term("apply", [new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z")]), new pl.type.Term(",", [new pl.type.Term("global", [new pl.type.Var("G")]),new pl.type.Term("apply", [new pl.type.Var("G"),new pl.type.Var("X"),new pl.type.Var("Y"),new pl.type.Var("Z")])]))
],
// apply/4
"apply/4": function( thread, point, atom ) {
var context = atom.args[0], name = atom.args[1], args = atom.args[2], result = atom.args[3];
if( pl.type.is_variable( context ) || pl.type.is_variable( name ) || pl.type.is_variable( args ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom( name ) && (!pl.type.is_js_object( name ) || typeof name.value !== "function") ) {
thread.throw_error( pl.error.type( "atom_or_JSValueFUNCTION", name, atom.indicator ) );
} else if( !pl.type.is_list( args ) ) {
thread.throw_error( pl.error.type( "list", args, atom.indicator ) );
}
var ctx = context.toJavaScript();
var fn = pl.type.is_atom( name ) ? ctx[name.id] : name.toJavaScript();
if( typeof fn === "function" ) {
var pointer = args;
var pltojs;
var arr = [];
while( pointer.indicator === "./2" ) {
pltojs = pointer.args[0].toJavaScript();
if( pltojs === undefined ) {
thread.throw_error( pl.error.domain( "javascript_object", pointer.args[0], atom.indicator ) );
return undefined;
}
arr.push( pltojs );
pointer = pointer.args[1];
}
if( pl.type.is_variable( pointer ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if( pointer.indicator !== "[]/0" ) {
thread.throw_error( pl.error.type( "list", args, atom.indicator ) );
return
}
var value;
try {
value = fn.apply( ctx, arr );
} catch( e ) {
thread.throw_error( pl.error.javascript( e.toString(), atom.indicator ) );
return;
}
value = pl.fromJavaScript.apply( value );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [value, result] ) ), point.substitution, point )] );
}
},
// prop/2:
"prop/2": [
new pl.type.Rule(new pl.type.Term("prop", [new pl.type.Var("X"),new pl.type.Var("Y")]), new pl.type.Term(",", [new pl.type.Term("global", [new pl.type.Var("G")]),new pl.type.Term("prop", [new pl.type.Var("G"),new pl.type.Var("X"),new pl.type.Var("Y")])]))
],
// prop/3
"prop/3": function( thread, point, atom ) {
var context = atom.args[0], name = atom.args[1], result = atom.args[2];
if( pl.type.is_variable( context ) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable( name ) && !pl.type.is_atom( name ) ) {
thread.throw_error( pl.error.type( "atom", name, atom.indicator ) );
} else {
if( pl.type.is_atom( name ) ) {
var fn = context.toJavaScript()[name.id];
if( fn !== undefined ) {
fn = pl.fromJavaScript.apply( fn );
thread.prepend( [new pl.type.State( point.goal.replace( new pl.type.Term( "=", [fn, result] ) ), point.substitution, point )] );
}
} else {
var fn = context.toJavaScript();
var states = [];
for( var x in fn ) {
if( fn.hasOwnProperty( x ) ) {
var fn_ = pl.fromJavaScript.apply( fn[x] );
states.push( new pl.type.State( point.goal.replace( new pl.type.Term( ",", [
new pl.type.Term( "=", [fn_, result] ),
new pl.type.Term( "=", [new pl.type.Term(x, []), name] )
]) ), point.substitution, point ) );
}
}
thread.prepend( states );
}
}
},
// json_prolog/2
"json_prolog/2": function( thread, point, atom ) {
var json = atom.args[0], prolog = atom.args[1];
if( pl.type.is_variable(json) && pl.type.is_variable(prolog) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable(json) && (!pl.type.is_js_object(json) || typeof(json.value) !== "object")) {
thread.throw_error( pl.error.type( "JsValueOBJECT", json, atom.indicator ) );
} else if( !pl.type.is_variable(prolog) && !pl.type.is_list(prolog) ) {
thread.throw_error( pl.error.type( "list", prolog, atom.indicator ) );
} else {
if(pl.type.is_variable(prolog)) {
var list = pl.fromJavaScript.apply(json.value, true);
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [prolog, list])),
point.substitution,
point
)]);
} else {
var obj = new pl.type.JSValue(prolog.toJavaScript());
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [json, obj])),
point.substitution,
point
)]);
}
}
},
// json_atom/2
"json_atom/2": function( thread, point, atom ) {
var json = atom.args[0], prolog = atom.args[1];
if( pl.type.is_variable(json) && pl.type.is_variable(prolog) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_variable(json) && (!pl.type.is_js_object(json) || typeof(json.value) !== "object")) {
thread.throw_error( pl.error.type( "JsValueOBJECT", json, atom.indicator ) );
} else if( !pl.type.is_variable(prolog) && !pl.type.is_atom(prolog) ) {
thread.throw_error( pl.error.type( "atom", prolog, atom.indicator ) );
} else {
if(pl.type.is_variable(prolog)) {
try {
var jatom = new pl.type.Term(JSON.stringify(json.value), []);
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [prolog, jatom])),
point.substitution,
point
)]);
} catch(ex) {}
} else {
try {
console.log(JSON.parse(prolog.id));
var obj = pl.fromJavaScript.apply(JSON.parse(prolog.id));
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [json, obj])),
point.substitution,
point
)]);
} catch(ex) {}
}
}
},
// ajax/3
"ajax/3": [
new pl.type.Rule(new pl.type.Term("ajax", [new pl.type.Var("Method"),new pl.type.Var("URL"),new pl.type.Var("Response")]), new pl.type.Term("ajax", [new pl.type.Var("Method"),new pl.type.Var("URL"),new pl.type.Var("Response"),new pl.type.Term("[]", [])]))
],
// ajax/4
"ajax/4": function( thread, point, atom ) {
var method = atom.args[0], url = atom.args[1], value = atom.args[2], options = atom.args[3];
if(pl.type.is_variable(url) || pl.type.is_variable(method) || pl.type.is_variable(options)) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if(!pl.type.is_atom(url)) {
thread.throw_error( pl.error.type( "atom", url, atom.indicator ) );
} else if(!pl.type.is_atom(method)) {
thread.throw_error( pl.error.type( "atom", method, atom.indicator ) );
} else if(!pl.type.is_list(options)) {
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
} else if(["connect", "delete", "get", "head", "options", "patch", "post", "put", "trace"].indexOf(method.id) === -1) {
thread.throw_error( pl.error.domain( "http_method", method, atom.indicator ) );
} else {
var pointer = options;
var opt_type = null;
var opt_timeout = 0;
var opt_credentials = "false";
var opt_async = "true";
var opt_mime = null;
var opt_headers = [];
var opt_body = new FormData();
var opt_user = null;
var opt_password = null;
// Options
while(pl.type.is_term(pointer) && pointer.indicator === "./2") {
var option = pointer.args[0];
if(!pl.type.is_term(option) || option.args.length !== 1) {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
var prop = option.args[0];
// type/1
if(option.indicator === "type/1") {
if(!pl.type.is_atom(prop) || prop.id !== "text" && prop.id !== "json" && prop.id !== "document") {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
opt_type = prop.id;
// user/1
} else if(option.indicator === "user/1") {
if(!pl.type.is_atom(prop)) {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
opt_user = prop.id;
// password/1
} else if(option.indicator === "password/1") {
if(!pl.type.is_atom(prop)) {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
opt_password = prop.id;
// timeout/1
} else if(option.indicator === "timeout/1") {
if(!pl.type.is_integer(prop) || prop.value < 0) {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
opt_timeout = prop.value;
// async/1
} else if(option.indicator === "async/1") {
if(!pl.type.is_atom(prop) || prop.id !== "true" && prop.id !== "false") {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
opt_async = prop.id;
// credentials/1
} else if(option.indicator === "credentials/1") {
if(!pl.type.is_atom(prop) || prop.id !== "true" && prop.id !== "false") {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
opt_credentials = prop.id;
// mime/1
} else if(option.indicator === "mime/1") {
if(!pl.type.is_atom(prop)) {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
opt_mime = prop.id;
// headers/1
} else if(option.indicator === "headers/1") {
if(!pl.type.is_list(prop)) {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
var hpointer = prop;
while(pl.type.is_term(hpointer) && hpointer.indicator === "./2") {
var header = hpointer.args[0];
if(!pl.type.is_term(header) || header.indicator !== "-/2" || !pl.type.is_atom(header.args[0]) || !pl.type.is_atom(header.args[1])) {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
opt_headers.push({header: header.args[0].id, value: header.args[1].id});
hpointer = hpointer.args[1];
}
if(pl.type.is_variable(hpointer)) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if(!pl.type.is_term(hpointer) || hpointer.indicator !== "[]/0") {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
// body/1
} else if(option.indicator === "body/1") {
if(!pl.type.is_list(prop) && (pl.type.is_dom_object === undefined || !pl.type.is_dom_object(prop)) && !pl.type.is_atom(prop)) {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
if(pl.type.is_list(prop)) {
var hpointer = prop;
while(pl.type.is_term(hpointer) && hpointer.indicator === "./2") {
var body = hpointer.args[0];
if(!pl.type.is_term(body) || body.indicator !== "-/2" || !pl.type.is_atom(body.args[0]) || !pl.type.is_atom(body.args[1])) {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
opt_body.append(body.args[0].id, body.args[1].id);
hpointer = hpointer.args[1];
}
if(pl.type.is_variable(hpointer)) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if(!pl.type.is_term(hpointer) || hpointer.indicator !== "[]/0") {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
} else if(pl.type.is_atom(prop)) {
opt_body = prop.id;
} else {
opt_body = prop.value;
}
// otherwise
} else {
thread.throw_error( pl.error.domain( "ajax_option", option, atom.indicator ) );
return;
}
pointer = pointer.args[1];
}
if(pl.type.is_variable(pointer)) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
return;
} else if(!pl.type.is_term(pointer) || pointer.indicator !== "[]/0") {
thread.throw_error( pl.error.type( "list", options, atom.indicator ) );
return;
}
// Request
var xhttp = new XMLHttpRequest();
if(opt_mime !== null)
xhttp.overrideMimeType(opt_mime);
var fn = function() {
if(this.readyState == 4) {
if(this.status == 200) {
// Get response
var data = null;
var content_type = this.getResponseHeader("Content-Type");
if(this.responseType === "json" && this.response) {
data = pl.fromJavaScript.apply(this.response);
} else if(this.responseType === "" && content_type.indexOf("application/json") !== -1) {
try {
data = pl.fromJavaScript.apply(JSON.parse(this.responseText));
} catch(e) {}
}
if(data === null) {
if((this.responseType === "document" || this.responseType === "" && content_type.indexOf("text/html") !== -1 || this.responseType === "" && content_type.indexOf("application/xml") !== -1) && this.responseXML !== null && pl.type.DOM !== undefined) {
data = new pl.type.DOM( this.responseXML );
} else if(this.responseType === "" || this.responseType === "text") {
data = new pl.type.Term(this.responseText, []);
}
}
// Add answer
if(data !== null)
thread.prepend( [
new pl.type.State(
point.goal.replace(new pl.type.Term("=", [value, data])),
point.substitution,
point
)
] );
}
if(opt_async === "true")
thread.again();
}
};
xhttp.onreadystatechange = fn;
xhttp.open(method.id.toUpperCase(), url.id, opt_async === "true", opt_user, opt_password);
if(opt_type !== null && opt_async === "true")
xhttp.responseType = opt_type;
xhttp.withCredentials = opt_credentials === "true";
if(opt_async === "true")
xhttp.timeout = opt_timeout;
for(var i = 0; i < opt_headers.length; i++)
xhttp.setRequestHeader(opt_headers[i].header, opt_headers[i].value);
xhttp.send(opt_body);
if(opt_async === "true")
return true;
else
fn.apply(xhttp);
}
}
};
};
var exports = ["global/1", "apply/3", "apply/4", "prop/2", "prop/3", "json_prolog/2", "json_atom/2", "ajax/3", "ajax/4"];
/*function prolog_to_json(prolog) {
var pointer = prolog;
var obj = {};
while(pl.type.is_term(pointer) && pointer.indicator === "./2") {
var pair = pointer.args[0];
if(pl.type.is_variable(pair)) {
return pl.error.instantiation( atom.indicator );
} else if(!pl.type.is_term(pair) || pair.indicator !== "-/2" || !pl.type.is_atom(pair.args[0])) {
return pl.error.domain( "pair", pair, atom.indicator );
}
if()
obj[pair.args[0].id] = pair.args[1].toJavaScript();
pointer = pointer.args[1];
}
}*/
// JS OBJECTS
function define_properties() {
// Is a JS object
pl.type.is_js_object = function( obj ) {
return obj instanceof pl.type.JSValue;
};
// Ordering relation
pl.type.order.push( pl.type.JSValue );
// JSValue Prolog object
pl.type.JSValue = function( value ) {
this.value = value;
}
// toString
pl.type.JSValue.prototype.toString = function() {
return "<javascript>(" + (typeof this.value).toLowerCase() + ")";
};
// clone
pl.type.JSValue.prototype.clone = function() {
return new pl.type.JSValue( this.value );
};
// equals
pl.type.JSValue.prototype.equals = function( obj ) {
return pl.type.is_js_object( obj ) && this.value === obj.value;
};
// rename
pl.type.JSValue.prototype.rename = function( _ ) {
return this;
};
// get variables
pl.type.JSValue.prototype.variables = function() {
return [];
};
// apply substitutions
pl.type.JSValue.prototype.apply = function( _ ) {
return this;
};
// unify
pl.type.JSValue.prototype.unify = function( obj, _ ) {
if( pl.type.is_js_object( obj ) && this.value === obj.value ) {
return new pl.type.State( obj, new pl.type.Substitution() );
}
return null;
};
// interpret
pl.type.JSValue.prototype.interpret = function( indicator ) {
return pl.error.instantiation( indicator );
};
// compare
pl.type.JSValue.prototype.compare = function( obj ) {
if( this.value === obj.value ) {
return 0;
} else if( this.value < obj.value ) {
return -1;
} else if( this.value > obj.value ) {
return 1;
}
};
// to javascript
pl.type.JSValue.prototype.toJavaScript = function() {
return this.value;
};
// from javascript
pl.fromJavaScript.conversion.any = function( obj ) {
return new pl.type.JSValue( obj );
};
// JavaScript error
pl.error.javascript = function( error, indicator ) {
return new pl.type.Term( "error", [new pl.type.Term( "javascript_error", [new pl.type.Term( error )] ), pl.utils.str_indicator( indicator )] );
};
}
define_properties();
new pl.type.Module( "js", predicates(), exports );
})( prolog );
(function( pl ) {
var predicates = function() {
return {
// OPERATING SYSTEM INTERACTION
// sleep/1
"sleep/1": function( thread, point, atom ) {
var time = atom.args[0];
if( pl.type.is_variable( time ) ) {
thread.throw_error( pl.error.instantiation( thread.level ) );
} else if( !pl.type.is_integer( time ) ) {
thread.throw_error( pl.error.type( "integer", time, thread.level ) );
} else {
setTimeout( function() {
thread.success( point );
thread.again();
}, time.value );
return true;
}
},
// shell/1
"shell/1": function( thread, point, atom ) {
var command = atom.args[0];
thread.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term("shell", [command, new pl.type.Num(0, false)]) ),
point.substitution,
point
)] );
},
// shell/2
"shell/2": function( thread, point, atom ) {
var command = atom.args[0], status = atom.args[1];
if( pl.type.is_variable(command) ) {
thread.throw_error( pl.error.instantiation( atom.indicator ) );
} else if( !pl.type.is_atom(command) ) {
thread.throw_error( pl.error.type( "atom", command, atom.indicator ) );
} else if( !pl.type.is_variable(status) && !pl.type.is_integer(status) ) {
thread.throw_error( pl.error.type( "integer", status, atom.indicator ) );
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
const exec = require('child_process').exec;
exec( command.id, function() {} ).on( 'exit', function(code) {
thread.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term("=", [status, new pl.type.Num(code, false)]) ),
point.substitution,
point
)] );
thread.again();
} );
return true;
} else {
try {
eval( command.id );
thread.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term("=", [status, new pl.type.Num(0, false)]) ),
point.substitution,
point
)] );
} catch( error ) {
thread.prepend( [new pl.type.State(
point.goal.replace( new pl.type.Term("=", [status, new pl.type.Num(1, false)]) ),
point.substitution,
point
)] );
}
}
}
},
// directory_files/2
"directory_files/2": function(thread, point, atom) {
var path = atom.args[0], entries = atom.args[1];
if(pl.type.is_variable(path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(path)) {
thread.throw_error(pl.error.type("atom", path, atom.indicator));
} else if(!pl.type.is_variable(entries) && !pl.type.is_list(entries)) {
thread.throw_error(pl.error.type("list", entries, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.readdir(path.id, function(error, items) {
if(error) {
thread.throw_error(pl.error.existence("directory", path, atom.indicator));
} else {
var listing = new pl.type.Term("[]", []);
for(var i = items.length-1; i >= 0; i--)
listing = new pl.type.Term(".", [new pl.type.Term(items[i], []), listing]);
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [entries, listing])),
point.substitution,
point
)]);
}
thread.again();
});
return true;
} else {
var absolute = pl.utils.cd(thread.session.working_directory, path.id);
var file = thread.session.file_system.get(absolute);
if(pl.type.is_directory(file)) {
var items = [];
for(var prop in file.files)
items.push(prop);
var listing = new pl.type.Term("[]", []);
for(var i = items.length-1; i >= 0; i--)
listing = new pl.type.Term(".", [new pl.type.Term(items[i], []), listing]);
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [entries, listing])),
point.substitution,
point
)]);
} else {
thread.throw_error(pl.error.existence("directory", path, atom.indicator));
}
}
}
},
// working_directory/2
"working_directory/2": function(thread, point, atom) {
var oldcwd = atom.args[0], newcwd = atom.args[1];
if(pl.type.is_variable(newcwd) && (!pl.type.is_variable(oldcwd) || oldcwd.id !== newcwd.id)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_variable(oldcwd) && !pl.type.is_atom(oldcwd)) {
thread.throw_error(pl.error.type("atom", oldcwd, atom.indicator));
} else if(!pl.type.is_variable(newcwd) && !pl.type.is_atom(newcwd)) {
thread.throw_error(pl.error.type("atom", newcwd, atom.indicator));
} else {
var wd;
if(thread.get_flag("nodejs").indicator === "true/0") {
wd = process.cwd();
if(!pl.type.is_variable(newcwd))
process.chdir(newcwd.id);
} else {
wd = thread.session.working_directory;
if(!pl.type.is_variable(newcwd)) {
thread.session.working_directory = pl.utils.cd(wd, newcwd.id);
}
}
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [oldcwd, new pl.type.Term(wd, [])])),
point.substitution,
point
)]);
}
},
// delete_file/1
"delete_file/1": function(thread, point, atom) {
var path = atom.args[0];
if(pl.type.is_variable(path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(path)) {
thread.throw_error(pl.error.type("atom", path, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.stat(path.id, function(error, stat) {
if(!error && stat.isFile()) {
fs.unlink(path.id, function(error) {
if(error)
thread.throw_error(pl.error.permission("delete", "source_sink", path, atom.indicator));
else
thread.success( point );
thread.again();
});
} else {
thread.throw_error(pl.error.existence("source_sink", path, atom.indicator));
thread.again();
}
});
return true;
} else {
var absolute = pl.utils.cd(thread.session.working_directory, path.id);
var file = thread.session.file_system.get(absolute);
if(pl.type.is_file(file)) {
file.parent.remove(file.name);
thread.success(point);
} else {
thread.throw_error(pl.error.existence("source_sink", path, atom.indicator));
}
}
}
},
// delete_directory/1
"delete_directory/1": function(thread, point, atom) {
var path = atom.args[0];
if(pl.type.is_variable(path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(path)) {
thread.throw_error(pl.error.type("atom", path, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.stat(path.id, function(error, stat) {
if(!error && stat.isDirectory()) {
fs.rmdir(path.id, function(error) {
if(error)
thread.throw_error(pl.error.permission("delete", "directory", path, atom.indicator));
else
thread.success( point );
thread.again();
});
} else {
thread.throw_error(pl.error.existence("directory", path, atom.indicator));
thread.again();
}
});
return true;
} else {
var absolute = pl.utils.cd(thread.session.working_directory, path.id);
var file = thread.session.file_system.get(absolute);
if(pl.type.is_directory(file)) {
if(file !== thread.session.file_system.files && file.empty()) {
file.parent.remove(file.name);
thread.success(point);
} else {
thread.throw_error(pl.error.permission("delete", "directory", path, atom.indicator));
}
} else {
thread.throw_error(pl.error.existence("directory", path, atom.indicator));
}
}
}
},
// make_directory/1
"make_directory/1": function(thread, point, atom) {
var path = atom.args[0];
if(pl.type.is_variable(path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(path)) {
thread.throw_error(pl.error.type("atom", path, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.stat(path.id, function(error, stat) {
if(!error && (stat.isDirectory() || stat.isFile())) {
thread.throw_error(pl.error.permission("create", "directory", path, atom.indicator));
thread.again();
} else {
fs.mkdir(path.id, function(error) {
if(error)
thread.throw_error(pl.error.existence("directory", path, atom.indicator));
else
thread.success(point);
thread.again();
});
}
});
return true;
} else {
var absolute = pl.utils.cd(thread.session.working_directory, path.id);
var dirs = absolute.replace(/\/$/, "").split("/");
var dir = thread.session.file_system.files;
var name = dirs[dirs.length-1];
for(var i = 1; i < dirs.length-1; i++) {
dir = dir.lookup(dirs[i]);
if(!pl.type.is_directory(dir)) {
thread.throw_error(pl.error.existence("directory", path, atom.indicator));
return;
}
}
if(dir.lookup(name)) {
thread.throw_error(pl.error.permission("create", "directory", path, atom.indicator));
} else {
dir.push(name, new pl.type.Directory(name, dir));
thread.success(point);
}
}
}
},
// rename_file/2
"rename_file/2": function(thread, point, atom) {
var old_path = atom.args[0], new_path = atom.args[1];
if(pl.type.is_variable(old_path) || pl.type.is_variable(new_path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(old_path)) {
thread.throw_error(pl.error.type("atom", old_path, atom.indicator));
} else if(!pl.type.is_atom(new_path)) {
thread.throw_error(pl.error.type("atom", new_path, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.stat(old_path.id, function(error, stat) {
if(error || !stat.isFile()) {
thread.throw_error(pl.error.existence("source_sink", old_path, atom.indicator));
thread.again();
} else {
fs.rename(old_path.id, new_path.id, function(error) {
if(error)
thread.throw_error(pl.error.existence("source_sink", new_path, atom.indicator));
else
thread.success(point);
thread.again();
});
}
});
return true;
} else {
var old_file = thread.file_system_open(old_path.id, "text", "read");
if(old_file) {
var new_file = thread.file_system_open(new_path.id, "text", "write");
if(new_file) {
new_file.text = old_file.text;
var absolute = pl.utils.cd(thread.session.working_directory, old_path.id);
var dirs = absolute.replace(/\/$/, "").split("/");
var dir = thread.session.file_system.files;
var name = dirs[dirs.length-1];
for(var i = 1; i < dirs.length-1; i++)
dir = dir.lookup(dirs[i]);
dir.remove(name);
thread.success(point);
} else {
thread.throw_error(pl.error.existence("source_sink", new_path, atom.indicator));
}
} else {
thread.throw_error(pl.error.existence("source_sink", old_path, atom.indicator));
}
}
}
},
// exists_file/1
"exists_file/1": function(thread, point, atom) {
var path = atom.args[0];
if(pl.type.is_variable(path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(path)) {
thread.throw_error(pl.error.type("atom", path, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.stat(path.id, function(error, stat) {
if(!error && stat.isFile())
thread.success(point);
thread.again();
});
return true;
} else {
var absolute = pl.utils.cd(thread.session.working_directory, path.id);
var file = thread.session.file_system.get(absolute);
if(pl.type.is_file(file))
thread.success(point);
}
}
},
// exists_directory/1
"exists_directory/1": function(thread, point, atom) {
var path = atom.args[0];
if(pl.type.is_variable(path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(path)) {
thread.throw_error(pl.error.type("atom", path, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.stat(path.id, function(error, stat) {
if(!error && stat.isDirectory())
thread.success(point);
thread.again();
});
return true;
} else {
var absolute = pl.utils.cd(thread.session.working_directory, path.id);
var file = thread.session.file_system.get(absolute);
if(pl.type.is_directory(file))
thread.success(point);
}
}
},
// same_file/2
"same_file/2": function(thread, point, atom) {
var fst_path = atom.args[0], snd_path = atom.args[1];
if(pl.type.is_variable(fst_path) || pl.type.is_variable(snd_path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(fst_path)) {
thread.throw_error(pl.error.type("atom", fst_path, atom.indicator));
} else if(!pl.type.is_atom(snd_path)) {
thread.throw_error(pl.error.type("atom", snd_path, atom.indicator));
} else {
if(fst_path.id === snd_path.id) {
thread.success(point);
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.stat(fst_path.id, function(error, fst_stat) {
if(!error)
fs.stat(snd_path.id, function(error, snd_stat) {
if(!error && fst_stat.dev === snd_stat.dev && fst_stat.ino === snd_stat.ino)
thread.success(point);
thread.again();
});
else
thread.again();
});
return true;
} else {
var working_directory = thread.session.working_directory;
var fst_file = thread.session.file_system.get(pl.utils.cd(working_directory, fst_path.id));
var snd_file = thread.session.file_system.get(pl.utils.cd(working_directory, snd_path.id));
if(fst_file && snd_file && fst_file === snd_file)
thread.success(point);
}
}
}
},
// absolute_file_name/2
"absolute_file_name/2": function(thread, point, atom) {
var filename = atom.args[0], absolute = atom.args[1];
if(pl.type.is_variable(filename)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(filename)) {
thread.throw_error(pl.error.type("atom", filename, atom.indicator));
} else if(!pl.type.is_variable(absolute) && !pl.type.is_atom(absolute)) {
thread.throw_error(pl.error.type("atom", absolute, atom.indicator));
} else {
var absolute_filename;
if(thread.get_flag("nodejs").indicator === "true/0") {
var path = require("path");
absolute_filename = path.resolve(filename.id);
} else {
absolute_filename = pl.utils.cd(thread.session.working_directory, filename.id);
}
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [
absolute,
new pl.type.Term(absolute_filename, [])])),
point.substitution,
point
)]);
}
},
// is_absolute_file_name/1
"is_absolute_file_name/1": function(thread, point, atom) {
var filename = atom.args[0];
if(pl.type.is_variable(filename)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(filename)) {
thread.throw_error(pl.error.type("atom", filename, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var path = require('path');
if(path.isAbsolute(filename.id))
thread.success(point);
} else {
if(filename.id.length > 0 && filename.id[0] === "/")
thread.success(point);
}
}
},
// size_file/2
"size_file/2": function(thread, point, atom) {
var path = atom.args[0], size = atom.args[1];
if(pl.type.is_variable(path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(path)) {
thread.throw_error(pl.error.type("atom", path, atom.indicator));
} else if(!pl.type.is_variable(size) && !pl.type.is_integer(size)) {
thread.throw_error(pl.error.type("integer", size, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.stat(path.id, function(error, stat) {
if(!error) {
var filesize = stat.size;
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [size, new pl.type.Num(filesize, false)])),
point.substitution,
point
)]);
} else {
thread.throw_error(pl.error.existence("source_sink", path, atom.indicator));
}
thread.again();
});
return true;
} else {
var absolute = pl.utils.cd(thread.session.working_directory, path.id);
var file = thread.session.file_system.get(absolute);
if(pl.type.is_file(file) || pl.type.is_directory(file)) {
var filesize = file.size();
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [size, new pl.type.Num(filesize, false)])),
point.substitution,
point
)]);
} else {
thread.throw_error(pl.error.existence("source_sink", path, atom.indicator));
}
}
}
},
// time_file/2
"time_file/2": function(thread, point, atom) {
var path = atom.args[0], time = atom.args[1];
if(pl.type.is_variable(path)) {
thread.throw_error(pl.error.instantiation(atom.indicator));
} else if(!pl.type.is_atom(path)) {
thread.throw_error(pl.error.type("atom", path, atom.indicator));
} else if(!pl.type.is_variable(time) && !pl.type.is_number(time)) {
thread.throw_error(pl.error.type("number", time, atom.indicator));
} else {
if(thread.get_flag("nodejs").indicator === "true/0") {
var fs = require('fs');
fs.stat(path.id, function(error, stat) {
if(!error) {
var mtime = stat.mtime / 1000;
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [time, new pl.type.Num(mtime)])),
point.substitution,
point
)]);
} else {
thread.throw_error(pl.error.existence("source_sink", path, atom.indicator));
}
thread.again();
});
return true;
} else {
var absolute = pl.utils.cd(thread.session.working_directory, path.id);
var file = thread.session.file_system.get(absolute);
if(pl.type.is_file(file) || pl.type.is_directory(file)) {
var mtime = file.modified;
thread.prepend([new pl.type.State(
point.goal.replace(new pl.type.Term("=", [time, new pl.type.Num(mtime)])),
point.substitution,
point
)]);
} else {
thread.throw_error(pl.error.existence("source_sink", path, atom.indicator));
}
}
}
}
};
};
var exports = ["sleep/1", "shell/1", "shell/2", "directory_files/2", "working_directory/2", "delete_file/1", "delete_directory/1", "rename_file/2", "make_directory/1", "exists_file/1", "exists_directory/1", "same_file/2", "absolute_file_name/2", "is_absolute_file_name/1", "size_file/2", "time_file/2"];
new pl.type.Module( "os", predicates(), exports );
})( prolog );
if (typeof module != 'undefined') module.exports=prolog;
};
BundleModuleCode['parser/papaparse']=function (module,exports){
/*!
Papa Parse
v4.1.2
https://github.com/mholt/PapaParse
*/
(function(global)
{
'use strict';
var IS_WORKER = !global.document && !!global.postMessage,
IS_PAPA_WORKER = IS_WORKER && /(\?|&)papaworker(=|&|$)/.test(global.location.search),
LOADED_SYNC = false, AUTO_SCRIPT_PATH;
var workers = {}, workerIdCounter = 0;
var Papa = {};
Papa.parse = CsvToJson;
Papa.unparse = JsonToCsv;
Papa.RECORD_SEP = String.fromCharCode(30);
Papa.UNIT_SEP = String.fromCharCode(31);
Papa.BYTE_ORDER_MARK = '\ufeff';
Papa.BAD_DELIMITERS = ['\r', '\n', '"', Papa.BYTE_ORDER_MARK];
Papa.WORKERS_SUPPORTED = !IS_WORKER && !!global.Worker;
Papa.SCRIPT_PATH = null; // Must be set by your code if you use workers and this lib is loaded asynchronously
// Configurable chunk sizes for local and remote files, respectively
Papa.LocalChunkSize = 1024 * 1024 * 10; // 10 MB
Papa.RemoteChunkSize = 1024 * 1024 * 5; // 5 MB
Papa.DefaultDelimiter = ','; // Used if not specified and detection fails
// Exposed for testing and development only
Papa.Parser = Parser;
Papa.ParserHandle = ParserHandle;
Papa.NetworkStreamer = NetworkStreamer;
Papa.FileStreamer = FileStreamer;
Papa.StringStreamer = StringStreamer;
function detectCSV (chunk, opts) {
opts = opts || {}
if (Buffer.isBuffer(chunk)) chunk = chunk + ''
var delimiters = opts.delimiters || [',', ';', '\t', '|']
var newlines = opts.newlines || ['\n', '\r']
var lines = chunk.split(/[\n\r]+/g)
var delimiter = determineMost(lines[0], delimiters)
var newline = determineMost(chunk, newlines)
if (!delimiter) {
if (isQuoted(lines[0])) return { newline: newline }
return null
}
return {
delimiter: delimiter,
newline: newline
}
}
function determineMost (chunk, items) {
var ignoreString = false
var itemCount = {}
var maxValue = 0
var maxChar
var currValue
items.forEach(function (item) {
itemCount[item] = 0
})
for (var i = 0; i < chunk.length; i++) {
if (chunk[i] === '"') ignoreString = !ignoreString
else if (!ignoreString && chunk[i] in itemCount) {
currValue = ++itemCount[chunk[i]]
if (currValue > maxValue) {
maxValue = currValue
maxChar = chunk[i]
}
}
}
return maxChar
}
function isQuoted (chunk) {
// is correctly quoted
var nextQuote = false
if (chunk[0] !== '"') return false
if (chunk[chunk.length - 1] !== '"') return false
for (var i = 1; i < chunk.length - 1; i++) {
if (chunk[i] === '"') nextQuote = !nextQuote
else if (nextQuote) return false
}
return !nextQuote
}
Papa.detect = detectCSV;
if (typeof module !== 'undefined' && module.exports)
{
// Export to Node...
module.exports = Papa;
}
else if (isFunction(global.define) && global.define.amd)
{
// Wireup with RequireJS
define(function() { return Papa; });
}
else
{
// ...or as browser global
global.Papa = Papa;
}
if (global.jQuery)
{
var $ = global.jQuery;
$.fn.parse = function(options)
{
var config = options.config || {};
var queue = [];
this.each(function(idx)
{
var supported = $(this).prop('tagName').toUpperCase() === 'INPUT'
&& $(this).attr('type').toLowerCase() === 'file'
&& global.FileReader;
if (!supported || !this.files || this.files.length === 0)
return true; // continue to next input element
for (var i = 0; i < this.files.length; i++)
{
queue.push({
file: this.files[i],
inputElem: this,
instanceConfig: $.extend({}, config)
});
}
});
parseNextFile(); // begin parsing
return this; // maintains chainability
function parseNextFile()
{
if (queue.length === 0)
{
if (isFunction(options.complete))
options.complete();
return;
}
var f = queue[0];
if (isFunction(options.before))
{
var returned = options.before(f.file, f.inputElem);
if (typeof returned === 'object')
{
if (returned.action === 'abort')
{
error('AbortError', f.file, f.inputElem, returned.reason);
return; // Aborts all queued files immediately
}
else if (returned.action === 'skip')
{
fileComplete(); // parse the next file in the queue, if any
return;
}
else if (typeof returned.config === 'object')
f.instanceConfig = $.extend(f.instanceConfig, returned.config);
}
else if (returned === 'skip')
{
fileComplete(); // parse the next file in the queue, if any
return;
}
}
// Wrap up the user's complete callback, if any, so that ours also gets executed
var userCompleteFunc = f.instanceConfig.complete;
f.instanceConfig.complete = function(results)
{
if (isFunction(userCompleteFunc))
userCompleteFunc(results, f.file, f.inputElem);
fileComplete();
};
Papa.parse(f.file, f.instanceConfig);
}
function error(name, file, elem, reason)
{
if (isFunction(options.error))
options.error({name: name}, file, elem, reason);
}
function fileComplete()
{
queue.splice(0, 1);
parseNextFile();
}
}
}
if (IS_PAPA_WORKER)
{
global.onmessage = workerThreadReceivedMessage;
}
else if (Papa.WORKERS_SUPPORTED)
{
AUTO_SCRIPT_PATH = getScriptPath();
// Check if the script was loaded synchronously
if (!document.body)
{
// Body doesn't exist yet, must be synchronous
LOADED_SYNC = true;
}
else
{
document.addEventListener('DOMContentLoaded', function () {
LOADED_SYNC = true;
}, true);
}
}
function CsvToJson(_input, _config)
{
_config = _config || {};
if (_config.worker && Papa.WORKERS_SUPPORTED)
{
var w = newWorker();
w.userStep = _config.step;
w.userChunk = _config.chunk;
w.userComplete = _config.complete;
w.userError = _config.error;
_config.step = isFunction(_config.step);
_config.chunk = isFunction(_config.chunk);
_config.complete = isFunction(_config.complete);
_config.error = isFunction(_config.error);
delete _config.worker; // prevent infinite loop
w.postMessage({
input: _input,
config: _config,
workerId: w.id
});
return;
}
var streamer = null;
if (typeof _input === 'string')
{
if (_config.download)
streamer = new NetworkStreamer(_config);
else
streamer = new StringStreamer(_config);
}
else if ((global.File && _input instanceof File) || _input instanceof Object) // ...Safari. (see issue #106)
streamer = new FileStreamer(_config);
return streamer.stream(_input);
}
function JsonToCsv(_input, _config)
{
var _output = '';
var _fields = [];
// Default configuration
/** whether to surround every datum with quotes */
var _quotes = false;
/** delimiting character */
var _delimiter = ',';
/** newline character(s) */
var _newline = '\r\n';
unpackConfig();
if (typeof _input === 'string')
_input = JSON.parse(_input);
if (_input instanceof Array)
{
if (!_input.length || _input[0] instanceof Array)
return serialize(null, _input);
else if (typeof _input[0] === 'object')
return serialize(objectKeys(_input[0]), _input);
}
else if (typeof _input === 'object')
{
if (typeof _input.data === 'string')
_input.data = JSON.parse(_input.data);
if (_input.data instanceof Array)
{
if (!_input.fields)
_input.fields = _input.meta && _input.meta.fields;
if (!_input.fields)
_input.fields = _input.data[0] instanceof Array
? _input.fields
: objectKeys(_input.data[0]);
if (!(_input.data[0] instanceof Array) && typeof _input.data[0] !== 'object')
_input.data = [_input.data]; // handles input like [1,2,3] or ['asdf']
}
return serialize(_input.fields || [], _input.data || []);
}
// Default (any valid paths should return before this)
throw 'exception: Unable to serialize unrecognized input';
function unpackConfig()
{
if (typeof _config !== 'object')
return;
if (typeof _config.delimiter === 'string'
&& _config.delimiter.length === 1
&& Papa.BAD_DELIMITERS.indexOf(_config.delimiter) === -1)
{
_delimiter = _config.delimiter;
}
if (typeof _config.quotes === 'boolean'
|| _config.quotes instanceof Array)
_quotes = _config.quotes;
if (typeof _config.newline === 'string')
_newline = _config.newline;
}
/** Turns an object's keys into an array */
function objectKeys(obj)
{
if (typeof obj !== 'object')
return [];
var keys = [];
for (var key in obj)
keys.push(key);
return keys;
}
/** The double for loop that iterates the data and writes out a CSV string including header row */
function serialize(fields, data)
{
var csv = '';
if (typeof fields === 'string')
fields = JSON.parse(fields);
if (typeof data === 'string')
data = JSON.parse(data);
var hasHeader = fields instanceof Array && fields.length > 0;
var dataKeyedByField = !(data[0] instanceof Array);
// If there a header row, write it first
if (hasHeader)
{
for (var i = 0; i < fields.length; i++)
{
if (i > 0)
csv += _delimiter;
csv += safe(fields[i], i);
}
if (data.length > 0)
csv += _newline;
}
// Then write out the data
for (var row = 0; row < data.length; row++)
{
var maxCol = hasHeader ? fields.length : data[row].length;
for (var col = 0; col < maxCol; col++)
{
if (col > 0)
csv += _delimiter;
var colIdx = hasHeader && dataKeyedByField ? fields[col] : col;
csv += safe(data[row][colIdx], col);
}
if (row < data.length - 1)
csv += _newline;
}
return csv;
}
/** Encloses a value around quotes if needed (makes a value safe for CSV insertion) */
function safe(str, col)
{
if (typeof str === 'undefined' || str === null)
return '';
str = str.toString().replace(/"/g, '""');
var needsQuotes = (typeof _quotes === 'boolean' && _quotes)
|| (_quotes instanceof Array && _quotes[col])
|| hasAny(str, Papa.BAD_DELIMITERS)
|| str.indexOf(_delimiter) > -1
|| str.charAt(0) === ' '
|| str.charAt(str.length - 1) === ' ';
return needsQuotes ? '"' + str + '"' : str;
}
function hasAny(str, substrings)
{
for (var i = 0; i < substrings.length; i++)
if (str.indexOf(substrings[i]) > -1)
return true;
return false;
}
}
/** ChunkStreamer is the base prototype for various streamer implementations. */
function ChunkStreamer(config)
{
this._handle = null;
this._paused = false;
this._finished = false;
this._input = null;
this._baseIndex = 0;
this._partialLine = '';
this._rowCount = 0;
this._start = 0;
this._nextChunk = null;
this.isFirstChunk = true;
this._completeResults = {
data: [],
errors: [],
meta: {}
};
replaceConfig.call(this, config);
this.parseChunk = function(chunk)
{
// First chunk pre-processing
if (this.isFirstChunk && isFunction(this._config.beforeFirstChunk))
{
var modifiedChunk = this._config.beforeFirstChunk(chunk);
if (modifiedChunk !== undefined)
chunk = modifiedChunk;
}
this.isFirstChunk = false;
// Rejoin the line we likely just split in two by chunking the file
var aggregate = this._partialLine + chunk;
this._partialLine = '';
var results = this._handle.parse(aggregate, this._baseIndex, !this._finished);
if (this._handle.paused() || this._handle.aborted())
return;
var lastIndex = results.meta.cursor;
if (!this._finished)
{
this._partialLine = aggregate.substring(lastIndex - this._baseIndex);
this._baseIndex = lastIndex;
}
if (results && results.data)
this._rowCount += results.data.length;
var finishedIncludingPreview = this._finished || (this._config.preview && this._rowCount >= this._config.preview);
if (IS_PAPA_WORKER)
{
global.postMessage({
results: results,
workerId: Papa.WORKER_ID,
finished: finishedIncludingPreview
});
}
else if (isFunction(this._config.chunk))
{
this._config.chunk(results, this._handle);
if (this._paused)
return;
results = undefined;
this._completeResults = undefined;
}
if (!this._config.step && !this._config.chunk) {
this._completeResults.data = this._completeResults.data.concat(results.data);
this._completeResults.errors = this._completeResults.errors.concat(results.errors);
this._completeResults.meta = results.meta;
}
if (finishedIncludingPreview && isFunction(this._config.complete) && (!results || !results.meta.aborted))
this._config.complete(this._completeResults, this._input);
if (!finishedIncludingPreview && (!results || !results.meta.paused))
this._nextChunk();
return results;
};
this._sendError = function(error)
{
if (isFunction(this._config.error))
this._config.error(error);
else if (IS_PAPA_WORKER && this._config.error)
{
global.postMessage({
workerId: Papa.WORKER_ID,
error: error,
finished: false
});
}
};
function replaceConfig(config)
{
// Deep-copy the config so we can edit it
var configCopy = copy(config);
configCopy.chunkSize = parseInt(configCopy.chunkSize); // parseInt VERY important so we don't concatenate strings!
if (!config.step && !config.chunk)
configCopy.chunkSize = null; // disable Range header if not streaming; bad values break IIS - see issue #196
this._handle = new ParserHandle(configCopy);
this._handle.streamer = this;
this._config = configCopy; // persist the copy to the caller
}
}
function NetworkStreamer(config)
{
config = config || {};
if (!config.chunkSize)
config.chunkSize = Papa.RemoteChunkSize;
ChunkStreamer.call(this, config);
var xhr;
if (IS_WORKER)
{
this._nextChunk = function()
{
this._readChunk();
this._chunkLoaded();
};
}
else
{
this._nextChunk = function()
{
this._readChunk();
};
}
this.stream = function(url)
{
this._input = url;
this._nextChunk(); // Starts streaming
};
this._readChunk = function()
{
if (this._finished)
{
this._chunkLoaded();
return;
}
xhr = new XMLHttpRequest();
if (this._config.withCredentials)
{
xhr.withCredentials = this._config.withCredentials;
}
if (!IS_WORKER)
{
xhr.onload = bindFunction(this._chunkLoaded, this);
xhr.onerror = bindFunction(this._chunkError, this);
}
xhr.open('GET', this._input, !IS_WORKER);
if (this._config.chunkSize)
{
var end = this._start + this._config.chunkSize - 1; // minus one because byte range is inclusive
xhr.setRequestHeader('Range', 'bytes='+this._start+'-'+end);
xhr.setRequestHeader('If-None-Match', 'webkit-no-cache'); // https://bugs.webkit.org/show_bug.cgi?id=82672
}
try {
xhr.send();
}
catch (err) {
this._chunkError(err.message);
}
if (IS_WORKER && xhr.status === 0)
this._chunkError();
else
this._start += this._config.chunkSize;
}
this._chunkLoaded = function()
{
if (xhr.readyState != 4)
return;
if (xhr.status < 200 || xhr.status >= 400)
{
this._chunkError();
return;
}
this._finished = !this._config.chunkSize || this._start > getFileSize(xhr);
this.parseChunk(xhr.responseText);
}
this._chunkError = function(errorMessage)
{
var errorText = xhr.statusText || errorMessage;
this._sendError(errorText);
}
function getFileSize(xhr)
{
var contentRange = xhr.getResponseHeader('Content-Range');
return parseInt(contentRange.substr(contentRange.lastIndexOf('/') + 1));
}
}
NetworkStreamer.prototype = Object.create(ChunkStreamer.prototype);
NetworkStreamer.prototype.constructor = NetworkStreamer;
function FileStreamer(config)
{
config = config || {};
if (!config.chunkSize)
config.chunkSize = Papa.LocalChunkSize;
ChunkStreamer.call(this, config);
var reader, slice;
// FileReader is better than FileReaderSync (even in worker) - see http://stackoverflow.com/q/24708649/1048862
// But Firefox is a pill, too - see issue #76: https://github.com/mholt/PapaParse/issues/76
var usingAsyncReader = typeof FileReader !== 'undefined'; // Safari doesn't consider it a function - see issue #105
this.stream = function(file)
{
this._input = file;
slice = file.slice || file.webkitSlice || file.mozSlice;
if (usingAsyncReader)
{
reader = new FileReader(); // Preferred method of reading files, even in workers
reader.onload = bindFunction(this._chunkLoaded, this);
reader.onerror = bindFunction(this._chunkError, this);
}
else
reader = new FileReaderSync(); // Hack for running in a web worker in Firefox
this._nextChunk(); // Starts streaming
};
this._nextChunk = function()
{
if (!this._finished && (!this._config.preview || this._rowCount < this._config.preview))
this._readChunk();
}
this._readChunk = function()
{
var input = this._input;
if (this._config.chunkSize)
{
var end = Math.min(this._start + this._config.chunkSize, this._input.size);
input = slice.call(input, this._start, end);
}
var txt = reader.readAsText(input, this._config.encoding);
if (!usingAsyncReader)
this._chunkLoaded({ target: { result: txt } }); // mimic the async signature
}
this._chunkLoaded = function(event)
{
// Very important to increment start each time before handling results
this._start += this._config.chunkSize;
this._finished = !this._config.chunkSize || this._start >= this._input.size;
this.parseChunk(event.target.result);
}
this._chunkError = function()
{
this._sendError(reader.error);
}
}
FileStreamer.prototype = Object.create(ChunkStreamer.prototype);
FileStreamer.prototype.constructor = FileStreamer;
function StringStreamer(config)
{
config = config || {};
ChunkStreamer.call(this, config);
var string;
var remaining;
this.stream = function(s)
{
string = s;
remaining = s;
return this._nextChunk();
}
this._nextChunk = function()
{
if (this._finished) return;
var size = this._config.chunkSize;
var chunk = size ? remaining.substr(0, size) : remaining;
remaining = size ? remaining.substr(size) : '';
this._finished = !remaining;
return this.parseChunk(chunk);
}
}
StringStreamer.prototype = Object.create(StringStreamer.prototype);
StringStreamer.prototype.constructor = StringStreamer;
// Use one ParserHandle per entire CSV file or string
function ParserHandle(_config)
{
// One goal is to minimize the use of regular expressions...
var FLOAT = /^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i;
var self = this;
var _stepCounter = 0; // Number of times step was called (number of rows parsed)
var _input; // The input being parsed
var _parser; // The core parser being used
var _paused = false; // Whether we are paused or not
var _aborted = false; // Whether the parser has aborted or not
var _delimiterError; // Temporary state between delimiter detection and processing results
var _fields = []; // Fields are from the header row of the input, if there is one
var _results = { // The last results returned from the parser
data: [],
errors: [],
meta: {}
};
if (isFunction(_config.step))
{
var userStep = _config.step;
_config.step = function(results)
{
_results = results;
if (needsHeaderRow())
processResults();
else // only call user's step function after header row
{
processResults();
// It's possbile that this line was empty and there's no row here after all
if (_results.data.length === 0)
return;
_stepCounter += results.data.length;
if (_config.preview && _stepCounter > _config.preview)
_parser.abort();
else
userStep(_results, self);
}
};
}
/**
* Parses input. Most users won't need, and shouldn't mess with, the baseIndex
* and ignoreLastRow parameters. They are used by streamers (wrapper functions)
* when an input comes in multiple chunks, like from a file.
*/
this.parse = function(input, baseIndex, ignoreLastRow)
{
if (!_config.newline)
_config.newline = guessLineEndings(input);
_delimiterError = false;
if (!_config.delimiter)
{
var delimGuess = guessDelimiter(input, _config.newline);
if (delimGuess.successful)
_config.delimiter = delimGuess.bestDelimiter;
else
{
_delimiterError = true; // add error after parsing (otherwise it would be overwritten)
_config.delimiter = Papa.DefaultDelimiter;
}
_results.meta.delimiter = _config.delimiter;
}
var parserConfig = copy(_config);
if (_config.preview && _config.header)
parserConfig.preview++; // to compensate for header row
_input = input;
_parser = new Parser(parserConfig);
_results = _parser.parse(_input, baseIndex, ignoreLastRow);
processResults();
return _paused ? { meta: { paused: true } } : (_results || { meta: { paused: false } });
};
this.paused = function()
{
return _paused;
};
this.pause = function()
{
_paused = true;
_parser.abort();
_input = _input.substr(_parser.getCharIndex());
};
this.resume = function()
{
_paused = false;
self.streamer.parseChunk(_input);
};
this.aborted = function ()
{
return _aborted;
};
this.abort = function()
{
_aborted = true;
_parser.abort();
_results.meta.aborted = true;
if (isFunction(_config.complete))
_config.complete(_results);
_input = '';
};
function processResults()
{
if (_results && _delimiterError)
{
addError('Delimiter', 'UndetectableDelimiter', 'Unable to auto-detect delimiting character; defaulted to \''+Papa.DefaultDelimiter+'\'');
_delimiterError = false;
}
if (_config.skipEmptyLines)
{
for (var i = 0; i < _results.data.length; i++)
if (_results.data[i].length === 1 && _results.data[i][0] === '')
_results.data.splice(i--, 1);
}
if (needsHeaderRow())
fillHeaderFields();
return applyHeaderAndDynamicTyping();
}
function needsHeaderRow()
{
return _config.header && _fields.length === 0;
}
function fillHeaderFields()
{
if (!_results)
return;
for (var i = 0; needsHeaderRow() && i < _results.data.length; i++)
for (var j = 0; j < _results.data[i].length; j++)
_fields.push(_results.data[i][j]);
_results.data.splice(0, 1);
}
function applyHeaderAndDynamicTyping()
{
if (!_results || (!_config.header && !_config.dynamicTyping))
return _results;
for (var i = 0; i < _results.data.length; i++)
{
var row = {};
for (var j = 0; j < _results.data[i].length; j++)
{
if (_config.dynamicTyping)
{
var value = _results.data[i][j];
if (value === 'true' || value === 'TRUE')
_results.data[i][j] = true;
else if (value === 'false' || value === 'FALSE')
_results.data[i][j] = false;
else
_results.data[i][j] = tryParseFloat(value);
}
if (_config.header)
{
if (j >= _fields.length)
{
if (!row['__parsed_extra'])
row['__parsed_extra'] = [];
row['__parsed_extra'].push(_results.data[i][j]);
}
else
row[_fields[j]] = _results.data[i][j];
}
}
if (_config.header)
{
_results.data[i] = row;
if (j > _fields.length)
addError('FieldMismatch', 'TooManyFields', 'Too many fields: expected ' + _fields.length + ' fields but parsed ' + j, i);
else if (j < _fields.length)
addError('FieldMismatch', 'TooFewFields', 'Too few fields: expected ' + _fields.length + ' fields but parsed ' + j, i);
}
}
if (_config.header && _results.meta)
_results.meta.fields = _fields;
return _results;
}
function guessDelimiter(input, newline)
{
var delimChoices = [',', '\t', '|', ';', Papa.RECORD_SEP, Papa.UNIT_SEP];
var bestDelim, bestDelta, fieldCountPrevRow;
for (var i = 0; i < delimChoices.length; i++)
{
var delim = delimChoices[i];
var delta = 0, avgFieldCount = 0;
fieldCountPrevRow = undefined;
var preview = new Parser({
delimiter: delim,
newline: newline,
preview: 10
}).parse(input);
for (var j = 0; j < preview.data.length; j++)
{
var fieldCount = preview.data[j].length;
avgFieldCount += fieldCount;
if (typeof fieldCountPrevRow === 'undefined')
{
fieldCountPrevRow = fieldCount;
continue;
}
else if (fieldCount > 1)
{
delta += Math.abs(fieldCount - fieldCountPrevRow);
fieldCountPrevRow = fieldCount;
}
}
if (preview.data.length > 0)
avgFieldCount /= preview.data.length;
if ((typeof bestDelta === 'undefined' || delta < bestDelta)
&& avgFieldCount > 1.99)
{
bestDelta = delta;
bestDelim = delim;
}
}
_config.delimiter = bestDelim;
return {
successful: !!bestDelim,
bestDelimiter: bestDelim
}
}
function guessLineEndings(input)
{
input = input.substr(0, 1024*1024); // max length 1 MB
var r = input.split('\r');
var n = input.split('\n');
var nAppearsFirst = (n.length > 1 && n[0].length < r[0].length);
if (r.length === 1 || nAppearsFirst)
return '\n';
var numWithN = 0;
for (var i = 0; i < r.length; i++)
{
if (r[i][0] === '\n')
numWithN++;
}
return numWithN >= r.length / 2 ? '\r\n' : '\r';
}
function tryParseFloat(val)
{
var isNumber = FLOAT.test(val);
return isNumber ? parseFloat(val) : val;
}
function addError(type, code, msg, row)
{
_results.errors.push({
type: type,
code: code,
message: msg,
row: row
});
}
}
/** The core parser implements speedy and correct CSV parsing */
function Parser(config)
{
// Unpack the config object
config = config || {};
var delim = config.delimiter;
var newline = config.newline;
var comments = config.comments;
var step = config.step;
var preview = config.preview;
var fastMode = config.fastMode;
// Delimiter must be valid
if (typeof delim !== 'string'
|| Papa.BAD_DELIMITERS.indexOf(delim) > -1)
delim = ',';
// Comment character must be valid
if (comments === delim)
throw 'Comment character same as delimiter';
else if (comments === true)
comments = '#';
else if (typeof comments !== 'string'
|| Papa.BAD_DELIMITERS.indexOf(comments) > -1)
comments = false;
// Newline must be valid: \r, \n, or \r\n
if (newline != '\n' && newline != '\r' && newline != '\r\n')
newline = '\n';
// We're gonna need these at the Parser scope
var cursor = 0;
var aborted = false;
this.parse = function(input, baseIndex, ignoreLastRow)
{
// For some reason, in Chrome, this speeds things up (!?)
if (typeof input !== 'string')
throw 'Input must be a string';
// We don't need to compute some of these every time parse() is called,
// but having them in a more local scope seems to perform better
var inputLen = input.length,
delimLen = delim.length,
newlineLen = newline.length,
commentsLen = comments.length;
var stepIsFunction = typeof step === 'function';
// Establish starting state
cursor = 0;
var data = [], errors = [], row = [], lastCursor = 0;
if (!input)
return returnable();
if (fastMode || (fastMode !== false && input.indexOf('"') === -1))
{
var rows = input.split(newline);
for (var i = 0; i < rows.length; i++)
{
var row = rows[i];
cursor += row.length;
if (i !== rows.length - 1)
cursor += newline.length;
else if (ignoreLastRow)
return returnable();
if (comments && row.substr(0, commentsLen) === comments)
continue;
if (stepIsFunction)
{
data = [];
pushRow(row.split(delim));
doStep();
if (aborted)
return returnable();
}
else
pushRow(row.split(delim));
if (preview && i >= preview)
{
data = data.slice(0, preview);
return returnable(true);
}
}
return returnable();
}
var nextDelim = input.indexOf(delim, cursor);
var nextNewline = input.indexOf(newline, cursor);
// Parser loop
for (;;)
{
// Field has opening quote
if (input[cursor] === '"')
{
// Start our search for the closing quote where the cursor is
var quoteSearch = cursor;
// Skip the opening quote
cursor++;
for (;;)
{
// Find closing quote
var quoteSearch = input.indexOf('"', quoteSearch+1);
if (quoteSearch === -1)
{
if (!ignoreLastRow) {
// No closing quote... what a pity
errors.push({
type: 'Quotes',
code: 'MissingQuotes',
message: 'Quoted field unterminated',
row: data.length, // row has yet to be inserted
index: cursor
});
}
return finish();
}
if (quoteSearch === inputLen-1)
{
// Closing quote at EOF
var value = input.substring(cursor, quoteSearch).replace(/""/g, '"');
return finish(value);
}
// If this quote is escaped, it's part of the data; skip it
if (input[quoteSearch+1] === '"')
{
quoteSearch++;
continue;
}
if (input[quoteSearch+1] === delim)
{
// Closing quote followed by delimiter
row.push(input.substring(cursor, quoteSearch).replace(/""/g, '"'));
cursor = quoteSearch + 1 + delimLen;
nextDelim = input.indexOf(delim, cursor);
nextNewline = input.indexOf(newline, cursor);
break;
}
if (input.substr(quoteSearch+1, newlineLen) === newline)
{
// Closing quote followed by newline
row.push(input.substring(cursor, quoteSearch).replace(/""/g, '"'));
saveRow(quoteSearch + 1 + newlineLen);
nextDelim = input.indexOf(delim, cursor); // because we may have skipped the nextDelim in the quoted field
if (stepIsFunction)
{
doStep();
if (aborted)
return returnable();
}
if (preview && data.length >= preview)
return returnable(true);
break;
}
}
continue;
}
// Comment found at start of new line
if (comments && row.length === 0 && input.substr(cursor, commentsLen) === comments)
{
if (nextNewline === -1) // Comment ends at EOF
return returnable();
cursor = nextNewline + newlineLen;
nextNewline = input.indexOf(newline, cursor);
nextDelim = input.indexOf(delim, cursor);
continue;
}
// Next delimiter comes before next newline, so we've reached end of field
if (nextDelim !== -1 && (nextDelim < nextNewline || nextNewline === -1))
{
row.push(input.substring(cursor, nextDelim));
cursor = nextDelim + delimLen;
nextDelim = input.indexOf(delim, cursor);
continue;
}
// End of row
if (nextNewline !== -1)
{
row.push(input.substring(cursor, nextNewline));
saveRow(nextNewline + newlineLen);
if (stepIsFunction)
{
doStep();
if (aborted)
return returnable();
}
if (preview && data.length >= preview)
return returnable(true);
continue;
}
break;
}
return finish();
function pushRow(row)
{
data.push(row);
lastCursor = cursor;
}
/**
* Appends the remaining input from cursor to the end into
* row, saves the row, calls step, and returns the results.
*/
function finish(value)
{
if (ignoreLastRow)
return returnable();
if (typeof value === 'undefined')
value = input.substr(cursor);
row.push(value);
cursor = inputLen; // important in case parsing is paused
pushRow(row);
if (stepIsFunction)
doStep();
return returnable();
}
/**
* Appends the current row to the results. It sets the cursor
* to newCursor and finds the nextNewline. The caller should
* take care to execute user's step function and check for
* preview and end parsing if necessary.
*/
function saveRow(newCursor)
{
cursor = newCursor;
pushRow(row);
row = [];
nextNewline = input.indexOf(newline, cursor);
}
/** Returns an object with the results, errors, and meta. */
function returnable(stopped)
{
return {
data: data,
errors: errors,
meta: {
delimiter: delim,
linebreak: newline,
aborted: aborted,
truncated: !!stopped,
cursor: lastCursor + (baseIndex || 0)
}
};
}
/** Executes the user's step function and resets data & errors. */
function doStep()
{
step(returnable());
data = [], errors = [];
}
};
/** Sets the abort flag */
this.abort = function()
{
aborted = true;
};
/** Gets the cursor position */
this.getCharIndex = function()
{
return cursor;
};
}
// If you need to load Papa Parse asynchronously and you also need worker threads, hard-code
// the script path here. See: https://github.com/mholt/PapaParse/issues/87#issuecomment-57885358
function getScriptPath()
{
var scripts = document.getElementsByTagName('script');
return scripts.length ? scripts[scripts.length - 1].src : '';
}
function newWorker()
{
if (!Papa.WORKERS_SUPPORTED)
return false;
if (!LOADED_SYNC && Papa.SCRIPT_PATH === null)
throw new Error(
'Script path cannot be determined automatically when Papa Parse is loaded asynchronously. ' +
'You need to set Papa.SCRIPT_PATH manually.'
);
var workerUrl = Papa.SCRIPT_PATH || AUTO_SCRIPT_PATH;
// Append 'papaworker' to the search string to tell papaparse that this is our worker.
workerUrl += (workerUrl.indexOf('?') !== -1 ? '&' : '?') + 'papaworker';
var w = new global.Worker(workerUrl);
w.onmessage = mainThreadReceivedMessage;
w.id = workerIdCounter++;
workers[w.id] = w;
return w;
}
/** Callback when main thread receives a message */
function mainThreadReceivedMessage(e)
{
var msg = e.data;
var worker = workers[msg.workerId];
var aborted = false;
if (msg.error)
worker.userError(msg.error, msg.file);
else if (msg.results && msg.results.data)
{
var abort = function() {
aborted = true;
completeWorker(msg.workerId, { data: [], errors: [], meta: { aborted: true } });
};
var handle = {
abort: abort,
pause: notImplemented,
resume: notImplemented
};
if (isFunction(worker.userStep))
{
for (var i = 0; i < msg.results.data.length; i++)
{
worker.userStep({
data: [msg.results.data[i]],
errors: msg.results.errors,
meta: msg.results.meta
}, handle);
if (aborted)
break;
}
delete msg.results; // free memory ASAP
}
else if (isFunction(worker.userChunk))
{
worker.userChunk(msg.results, handle, msg.file);
delete msg.results;
}
}
if (msg.finished && !aborted)
completeWorker(msg.workerId, msg.results);
}
function completeWorker(workerId, results) {
var worker = workers[workerId];
if (isFunction(worker.userComplete))
worker.userComplete(results);
worker.terminate();
delete workers[workerId];
}
function notImplemented() {
throw 'Not implemented.';
}
/** Callback when worker thread receives a message */
function workerThreadReceivedMessage(e)
{
var msg = e.data;
if (typeof Papa.WORKER_ID === 'undefined' && msg)
Papa.WORKER_ID = msg.workerId;
if (typeof msg.input === 'string')
{
global.postMessage({
workerId: Papa.WORKER_ID,
results: Papa.parse(msg.input, msg.config),
finished: true
});
}
else if ((global.File && msg.input instanceof File) || msg.input instanceof Object) // thank you, Safari (see issue #106)
{
var results = Papa.parse(msg.input, msg.config);
if (results)
global.postMessage({
workerId: Papa.WORKER_ID,
results: results,
finished: true
});
}
}
/** Makes a deep copy of an array or object (mostly) */
function copy(obj)
{
if (typeof obj !== 'object')
return obj;
var cpy = obj instanceof Array ? [] : {};
for (var key in obj)
cpy[key] = copy(obj[key]);
return cpy;
}
function bindFunction(f, self)
{
return function() { f.apply(self, arguments); };
}
function isFunction(func)
{
return typeof func === 'function';
}
})(typeof window !== 'undefined' ? window : this);
};
BundleModuleCode['numerics/numerics']=function (module,exports){
var current=none;
var Aios=none;
var options = {
version: '1.1.1'
}
module.exports = {
current:function (module) { current=module.current; Aios=module; },
agent: {
fft:Require('numerics/fft'),
matrix:Require('numerics/matrix'),
vector:Require('numerics/vector'),
}
}
};
BundleModuleCode['numerics/fft']=function (module,exports){
/*===========================================================================*\
* Fast Fourier Transform (Cooley-Tukey Method)
*
* (c) Vail Systems. Joshua Jung and Ben Bryan. 2015
*
* This code is not designed to be highly optimized but as an educational
* tool to understand the Fast Fourier Transform.
*
* Can be used with Vectors (typed arrays), Arrays
*
*
\*===========================================================================*/
var Vector = Require('numerics/vector');
var Matrix = Require('numerics/matrix');
//------------------------------------------------
// Note: Some of this code is not optimized and is
// primarily designed as an educational and testing
// tool.
// To get high performace would require transforming
// the recursive calls into a loop and then loop
// unrolling. All of this is best accomplished
// in C or assembly.
//-------------------------------------------------
//-------------------------------------------------
// The following code assumes a complex number is
// an array: [real, imaginary]
//-------------------------------------------------
var complex = {
//-------------------------------------------------
// Add two complex numbers
//-------------------------------------------------
add : function (a, b)
{
return [a[0] + b[0], a[1] + b[1]];
},
//-------------------------------------------------
// Subtract two complex numbers
//-------------------------------------------------
subtract : function (a, b)
{
return [a[0] - b[0], a[1] - b[1]];
},
//-------------------------------------------------
// Multiply two complex numbers
//
// (a + bi) * (c + di) = (ac - bd) + (ad + bc)i
//-------------------------------------------------
multiply : function (a, b)
{
return [(a[0] * b[0] - a[1] * b[1]),
(a[0] * b[1] + a[1] * b[0])];
},
//-------------------------------------------------
// Calculate |a + bi|
//
// sqrt(a*a + b*b)
//-------------------------------------------------
magnitude : function (c)
{
return Math.sqrt(c[0]*c[0] + c[1]*c[1]);
},
phase : function (c)
{
return c[0]!=0?Math.atan(c[1]/c[0])*180/Math.PI:(c[1]>0?90:-90);
}
}
var mapExponent = {};
var fftUtil = {
//-------------------------------------------------
// By Eulers Formula:
//
// e^(i*x) = cos(x) + i*sin(x)
//
// and in DFT:
//
// x = -2*PI*(k/N)
//-------------------------------------------------
exponent : function (k, N) {
var x = -2 * Math.PI * (k / N);
mapExponent[N] = mapExponent[N] || {};
mapExponent[N][k] = mapExponent[N][k] || [Math.cos(x), Math.sin(x)];// [Real, Imaginary]
return mapExponent[N][k];
},
//-------------------------------------------------
// Calculate FFT Magnitude for complex numbers.
//-------------------------------------------------
fftMag : function (fftBins) {
if (isArray(fftBins)) {
var ret = fftBins.map(complex.magnitude);
return ret.slice(0, ret.length / 2);
} else if (isVector(fftBins) || isMatrix(fftBins)) {
// complex matrix (2 columns)
if (fftBins.columns != 2) throw "fft.fftMag: Complex matrix columns != 2";
var ret = Vector(fftBins.rows,{dtn:fftBins.dtn})
return ret.eval(function (v,i) {
return complex.magnitude([fftBins.get(i,0),fftBins.get(i,1)]) });
}
},
fftPha : function (fftBins) {
if (isArray(fftBins)) {
var ret = fftBins.map(complex.phase);
return ret.slice(0, ret.length / 2);
} else if (isVector(fftBins) || isMatrix(fftBins)) {
// complex matrix (2 columns)
if (fftBins.columns != 2) throw "fft.fftMag: Complex matrix columns != 2";
var ret = Vector(fftBins.rows,{dtn:fftBins.dtn})
return ret.eval(function (v,i) {
return complex.phase([fftBins.get(i,0),fftBins.get(i,1)]) });
}
},
//-------------------------------------------------
// Calculate Frequency Bins
//
// Returns an array of the frequencies (in hertz) of
// each FFT bin provided, assuming the sampleRate is
// samples taken per second.
//-------------------------------------------------
fftFreq : function (fftBins, sampleRate) {
var stepFreq = sampleRate / (fftBins.length);
var ret = fftBins.slice(0, fftBins.length / 2);
return ret.map(function (__, ix) {
return ix * stepFreq;
});
}
}
// Bit-twiddle
var REVERSE_TABLE = new Array(256);
var INT_BITS = 32; //Number of bits in an integer
(function(tab) {
for(var i=0; i<256; ++i) {
var v = i, r = i, s = 7;
for (v >>>= 1; v; v >>>= 1) {
r <<= 1;
r |= v & 1;
--s;
}
tab[i] = (r << s) & 0xff;
}
})(REVERSE_TABLE);
var twiddle = {
//Constants
INT_BITS : INT_BITS,
INT_MAX : 0x7fffffff,
INT_MIN : -1<<(INT_BITS-1),
//Returns -1, 0, +1 depending on sign of x
sign : function(v) {
return (v > 0) - (v < 0);
},
//Computes absolute value of integer
abs : function(v) {
var mask = v >> (INT_BITS-1);
return (v ^ mask) - mask;
},
//Computes minimum of integers x and y
min : function(x, y) {
return y ^ ((x ^ y) & -(x < y));
},
//Computes maximum of integers x and y
max : function(x, y) {
return x ^ ((x ^ y) & -(x < y));
},
//Checks if a number is a power of two
isPow2 : function(v) {
return !(v & (v-1)) && (!!v);
},
//Computes log base 2 of v
log2 : function(v) {
var r, shift;
r = (v > 0xFFFF) << 4; v >>>= r;
shift = (v > 0xFF ) << 3; v >>>= shift; r |= shift;
shift = (v > 0xF ) << 2; v >>>= shift; r |= shift;
shift = (v > 0x3 ) << 1; v >>>= shift; r |= shift;
return r | (v >> 1);
},
//Computes log base 10 of v
log10 : function(v) {
return (v >= 1000000000) ? 9 : (v >= 100000000) ? 8 : (v >= 10000000) ? 7 :
(v >= 1000000) ? 6 : (v >= 100000) ? 5 : (v >= 10000) ? 4 :
(v >= 1000) ? 3 : (v >= 100) ? 2 : (v >= 10) ? 1 : 0;
},
//Counts number of bits
popCount : function(v) {
v = v - ((v >>> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
return ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
},
//Counts number of trailing zeros
countTrailingZeros : function (v) {
var c = 32;
v &= -v;
if (v) c--;
if (v & 0x0000FFFF) c -= 16;
if (v & 0x00FF00FF) c -= 8;
if (v & 0x0F0F0F0F) c -= 4;
if (v & 0x33333333) c -= 2;
if (v & 0x55555555) c -= 1;
return c;
},
//Rounds to next power of 2
nextPow2 : function(v) {
v += v === 0;
--v;
v |= v >>> 1;
v |= v >>> 2;
v |= v >>> 4;
v |= v >>> 8;
v |= v >>> 16;
return v + 1;
},
//Rounds down to previous power of 2
prevPow2 : function(v) {
v |= v >>> 1;
v |= v >>> 2;
v |= v >>> 4;
v |= v >>> 8;
v |= v >>> 16;
return v - (v>>>1);
},
//Computes parity of word
parity : function(v) {
v ^= v >>> 16;
v ^= v >>> 8;
v ^= v >>> 4;
v &= 0xf;
return (0x6996 >>> v) & 1;
},
//Reverse bits in a 32 bit word
reverse : function(v) {
return (REVERSE_TABLE[ v & 0xff] << 24) |
(REVERSE_TABLE[(v >>> 8) & 0xff] << 16) |
(REVERSE_TABLE[(v >>> 16) & 0xff] << 8) |
REVERSE_TABLE[(v >>> 24) & 0xff];
},
//Interleave bits of 2 coordinates with 16 bits. Useful for fast quadtree codes
interleave2 : function(x, y) {
x &= 0xFFFF;
x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F;
x = (x | (x << 2)) & 0x33333333;
x = (x | (x << 1)) & 0x55555555;
y &= 0xFFFF;
y = (y | (y << 8)) & 0x00FF00FF;
y = (y | (y << 4)) & 0x0F0F0F0F;
y = (y | (y << 2)) & 0x33333333;
y = (y | (y << 1)) & 0x55555555;
return x | (y << 1);
},
//Extracts the nth interleaved component
deinterleave2 : function(v, n) {
v = (v >>> n) & 0x55555555;
v = (v | (v >>> 1)) & 0x33333333;
v = (v | (v >>> 2)) & 0x0F0F0F0F;
v = (v | (v >>> 4)) & 0x00FF00FF;
v = (v | (v >>> 16)) & 0x000FFFF;
return (v << 16) >> 16;
},
//Interleave bits of 3 coordinates, each with 10 bits. Useful for fast octree codes
interleave3 : function(x, y, z) {
x &= 0x3FF;
x = (x | (x<<16)) & 4278190335;
x = (x | (x<<8)) & 251719695;
x = (x | (x<<4)) & 3272356035;
x = (x | (x<<2)) & 1227133513;
y &= 0x3FF;
y = (y | (y<<16)) & 4278190335;
y = (y | (y<<8)) & 251719695;
y = (y | (y<<4)) & 3272356035;
y = (y | (y<<2)) & 1227133513;
x |= (y << 1);
z &= 0x3FF;
z = (z | (z<<16)) & 4278190335;
z = (z | (z<<8)) & 251719695;
z = (z | (z<<4)) & 3272356035;
z = (z | (z<<2)) & 1227133513;
return x | (z << 2);
},
//Extracts nth interleaved component of a 3-tuple
deinterleave3 : function(v, n) {
v = (v >>> n) & 1227133513;
v = (v | (v>>>2)) & 3272356035;
v = (v | (v>>>4)) & 251719695;
v = (v | (v>>>8)) & 4278190335;
v = (v | (v>>>16)) & 0x3FF;
return (v<<22)>>22;
},
//Computes next combination in colexicographic order (this is mistakenly called nextPermutation on the bit twiddling hacks page)
nextCombination : function(v) {
var t = v | (v - 1);
return (t + 1) | (((~t & -~t) - 1) >>> (twiddle.countTrailingZeros(v) + 1));
}
}
function checkpow2(info,vector) {
if (Math.floor(Math.log2(vector.length)) != Math.log2(vector.length))
throw ('fft.'+info+' error: vector length must be a power of 2')
}
module.exports = {
//-------------------------------------------------
// Calculate FFT for vector where vector.length
// is assumed to be a power of 2.
//-------------------------------------------------
fft: function fft(vector) {
if (vector.data) vector=vector.data; // Matrix|Vector
checkpow2('fft',vector);
var X = [],
N = vector.length;
// Base case is X = x + 0i since our input is assumed to be real only.
if (N == 1) {
if (Array.isArray(vector[0])) //If input vector contains complex numbers
return [[vector[0][0], vector[0][1]]];
else
return [[vector[0], 0]];
}
// Recurse: all even samples
var X_evens = fft(vector.filter(even)),
// Recurse: all odd samples
X_odds = fft(vector.filter(odd));
// Now, perform N/2 operations!
for (var k = 0; k < N / 2; k++) {
// t is a complex number!
var t = X_evens[k],
e = complex.multiply(fftUtil.exponent(k, N), X_odds[k]);
X[k] = complex.add(t, e);
X[k + (N / 2)] = complex.subtract(t, e);
}
function even(__, ix) {
return ix % 2 == 0;
}
function odd(__, ix) {
return ix % 2 == 1;
}
return X;
},
//-------------------------------------------------
// Calculate FFT for vector where vector.length
// is assumed to be a power of 2. This is the in-
// place implementation, to avoid the memory
// footprint used by recursion.
//-------------------------------------------------
fftInPlace: function(vector) {
if (vector.data) vector=vector.data; // Matrix|Vector
checkpow2('fftInPlace',vector);
var N = vector.length;
var trailingZeros = twiddle.countTrailingZeros(N); //Once reversed, this will be leading zeros
// Reverse bits
for (var k = 0; k < N; k++) {
var p = twiddle.reverse(k) >>> (twiddle.INT_BITS - trailingZeros);
if (p > k) {
var complexTemp = [vector[k], 0];
vector[k] = vector[p];
vector[p] = complexTemp;
} else {
vector[p] = [vector[p], 0];
}
}
//Do the DIT now in-place
for (var len = 2; len <= N; len += len) {
for (var i = 0; i < len / 2; i++) {
var w = fftUtil.exponent(i, len);
for (var j = 0; j < N / len; j++) {
var t = complex.multiply(w, vector[j * len + i + len / 2]);
vector[j * len + i + len / 2] = complex.subtract(vector[j * len + i], t);
vector[j * len + i] = complex.add(vector[j * len + i], t);
}
}
}
},
ifft: function ifft(signal){
if (signal.data) signal=signal.data; // Matrix
checkpow2('ifft',signal);
//Interchange real and imaginary parts
var csignal=[];
for(var i=0; i<signal.length; i++){
csignal[i]=[signal[i][1], signal[i][0]];
}
//Apply fft
var ps=module.exports.fft(csignal);
//Interchange real and imaginary parts and normalize
var res=[];
for(var j=0; j<ps.length; j++){
res[j]=[ps[j][1]/ps.length, ps[j][0]/ps.length];
}
return res;
},
fftMag: fftUtil.fftMag,
fftPha: fftUtil.fftPha,
fftFreq: fftUtil.fftFreq,
};
};
BundleModuleCode['numerics/vector']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.sblab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2019 bLAB
** $CREATED: 1-1-19 by sbosse.
** $VERSION: 1.4.4
**
** $INFO:
**
** Vector module supporting typed and generic arrays.
**
**
** $ENDOFINFO
*/
Require('numerics/polyfill')
var sprintf = Require('com/sprintf');
/********** TYPEDARRY/ARRAY Extension for Matrix/Vector compatibility *************/
if (typeof Array.prototype.get == 'undefined') {
Object.defineProperty(Array.prototype, 'get', {value:function (i) {
return this[i];
}, configurable: true})
Object.defineProperty(Array.prototype, 'set', {value: function (a,b) {
this[a]=b;
}, configurable: true})
}
if (typeof Array.prototype.print == 'undefined') {
Object.defineProperty(Array.prototype, 'print', {value: function (format) {
var i,s='',sep='', columns=this.length,complex=isArray(this[0]);
if (!format) format = '%4.2f';
for(i=0;i<columns;i++) {
if (i!=0) s = s + '\n';
if (complex)
s = s + sprintf.sprintf(format,this[i][0]) + ',' +
sprintf.sprintf(format,this[i][1]);
else
s = s + sprintf.sprintf(format,this[i]) ;
}
return s;
}, configurable: true})
}
if (typeof Array.prototype.info == 'undefined') {
Object.defineProperty(Array.prototype, 'info', {value: function () {
return {
dtn:'Array',
size:this.length,
columns:this.length,
offset:0,
}
}, configurable: true})
}
/********************* STRING Conversion ******************************/
function toUTF8Array(str) {
var utf8 = [];
for (var i=0; i < str.length; i++) {
var charcode = str.charCodeAt(i);
if (charcode < 0x80) utf8.push(charcode);
else if (charcode < 0x800) {
utf8.push(0xc0 | (charcode >> 6),
0x80 | (charcode & 0x3f));
}
else if (charcode < 0xd800 || charcode >= 0xe000) {
utf8.push(0xe0 | (charcode >> 12),
0x80 | ((charcode>>6) & 0x3f),
0x80 | (charcode & 0x3f));
}
// surrogate pair
else {
i++;
charcode = ((charcode&0x3ff)<<10)|(str.charCodeAt(i)&0x3ff)
utf8.push(0xf0 | (charcode >>18),
0x80 | ((charcode>>12) & 0x3f),
0x80 | ((charcode>>6) & 0x3f),
0x80 | (charcode & 0x3f));
}
}
return utf8;
}
function fromUTF8Array(data) { // array of bytes
var str = '', i;
for (i = 0; i < data.length; i++) {
var value = data[i];
if (value < 0x80) {
str += String.fromCharCode(value);
} else if (value > 0xBF && value < 0xE0) {
str += String.fromCharCode((value & 0x1F) << 6 | data[i + 1] & 0x3F);
i += 1;
} else if (value > 0xDF && value < 0xF0) {
str += String.fromCharCode((value & 0x0F) << 12 | (data[i + 1] & 0x3F) << 6 | data[i + 2] & 0x3F);
i += 2;
} else {
// surrogate pair
var charCode = ((value & 0x07) << 18 | (data[i + 1] & 0x3F) << 12 | (data[i + 2] & 0x3F) << 6 | data[i + 3] & 0x3F) - 0x010000;
str += String.fromCharCode(charCode >> 10 | 0xD800, charCode & 0x03FF | 0xDC00);
i += 3;
}
}
return str;
}
var complex = {
//-------------------------------------------------
// Add two complex numbers
//-------------------------------------------------
add : function (a, b)
{
return [a[0] + b[0], a[1] + b[1]];
},
//-------------------------------------------------
// Subtract two complex numbers
//-------------------------------------------------
subtract : function (a, b)
{
return [a[0] - b[0], a[1] - b[1]];
},
//-------------------------------------------------
// Multiply two complex numbers
//
// (a + bi) * (c + di) = (ac - bd) + (ad + bc)i
//-------------------------------------------------
multiply : function (a, b)
{
return [(a[0] * b[0] - a[1] * b[1]),
(a[0] * b[1] + a[1] * b[0])];
},
//-------------------------------------------------
// Calculate |a + bi|
//
// sqrt(a*a + b*b)
//-------------------------------------------------
magnitude : function (offset,c)
{
return Math.sqrt(c[offset]*c[offset] + c[offset+1]*c[offset+1]);
},
phase : function (offset,c)
{
return c[offset]!=0?Math.atan(c[offset+1]/c[offset])*180/Math.PI:(c[offset+1]>0?90:-90);
}
}
/*********** VECTOR ************/
function Vector(a,b) {
var self = this;
var i,columns,size,offset=0,dim=1,dtn,dt=Vector.options.dt,data;
if (!(this instanceof Vector)) return new Vector(a,b);
var options=isObject(b)?b:{};
if (isNumber(a)) {
// Create a new empty vector (rows=1)
columns=a;
if (options.type) dt=options.type;
if (options.dtn) dt=options.dtn=='Array'?Array:TypedArrayOfName[options.dtn];
size=columns;
if (options.complex) size *=2;
if (options.dtn && !dt) throw ("Vector: Unknown array type dtn="+options.dtn)
data=new dt(size);
} else if (isArray(a)) {
size=columns=a.length;
if (options.type) dt=options.type;
if (options.dtn) dt=options.dtn=='Array'?Array:TypedArrayOfName[options.dtn];
if (options.dtn && !dt) throw ("Vector: Unknown array type dtn="+options.dtn)
if (options.dtn && options.dtn != 'Array') {
// Create typedarray from generic array
data=new dt(a);
} else {
// Matrix wrapper for generic arrays and array arrays
// modify .get .set .getRow prototype ...
// no _Matrix.call
dt=Array;
data=a;
}
} else if (isObject(a)) {
// partial object
columns=a.columns;
size=a.size||columns;
scale=options.scale;
if (options.dtn) dt=options.dtn=='Array'?Array:TypedArrayOfName[options.dtn];
if (options.dtn && !dt) throw ("Vector: Unknown array type dtn="+options.dtn)
if (options.dtn && a.dtn != options.dtn) {
// convert dtn
if (isArray(a.data) && !scale)
data=new dt(a.data);
else {
data=new dt(size);
if (scale) for(i=0;i<size;i++) data[i]=a.data[i]/scale;
else for(i=0;i<size;i++) data[i]=a.data[i];
}
dtn=options.dtn;
} else {
dtn=a.dtn;
data=a.data;
offset=a.offset;
}
if (a.scale) this.scale=a.scale;
if (a.complex) this.complex=a.complex;
} else if (isString(a)) {
columns=a.length;
if (options.type) dt=options.type;
if (options.dtn) dt=options.dtn=='Array'?Array:TypedArrayOfName[options.dtn];
if (options.dtn && !dt) throw ("Vector: Unknown array type dtn="+options.dtn)
data=new dt(toUTF8Array(a));
}
this.columns=columns;
this.size=this.length=size;
this.layout=1;
this.data=data;
this.dims=dim;
this.offset=offset;
if (options.complex) this.complex=true;
if (options.scale) this.scaler=options.scale;
this.dtn=dtn||dt.name;
if (this.dtn=='Array') this._arrayFix();
}
Vector.options = {
dt : Float32Array,
dtn : 'Float32Array'
}
/********* STATIC MEMBERS *********/
Vector.abs = function (m) {
return Vector.clone(m).abs();
}
Vector.add = function (m,v) {
return Vector.clone(m).add(v);
}
Vector.clone = function (src) {
return Vector(src);
}
Vector.checkVector = function (o) {
if (o instanceof Vector) return o;
else return Vector(o);
}
Vector.cos = function (m) {
return Vector.clone(m).cos();
}
Vector.div = function (m,v) {
return Vector.clone(m).div(v);
}
Vector.empty = function (columns) {
return Vector(columns);
}
Vector.exp = function (m) {
return Vector.clone(m).exp();
}
isVector = Vector.isVector = function (o) {
return (o instanceof Vector)
}
Vector.max = function(vector1, vector2) {
vector1 = Vector.checkVector(vector1);
vector2 = Vector.checkVector(vector2);
var columns =vector1.columns;
var result = Vector(columns,{dtn:vector1.dtn});
for (var i = 0; i< columns; i++) {
result.data[i]= Math.max(vector1.data[i], vector2.data[i]);
}
return result;
}
Vector.min = function(vector1, vector2) {
vector1 = Vector.checkVector(vector1);
vector2 = Vector.checkVector(vector2);
var columns =vector1.columns;
var result = Vector(columns,{dtn:vector1.dtn});
for (var i = 0; i< columns; i++) {
result.data[i]=Math.min(vector1.data[i], vector2.data[i]);
}
return result;
}
Vector.mod = function (m,v) {
return Vector.clone(m).mod(v);
}
Vector.mul = function (m,v) {
return Vector.clone(m).mul(v);
}
Vector.neg = function (m) {
return Vector.clone(m).neg();
}
Vector.ones = function (columns) {
return Vector(columns).fill(1);
}
Vector.rand = function (columns, rng) {
if (rng==undefined) rng=Math.random;
return Vector(columns).fill(function () {
return rng();
});
}
Vector.randInt = function (columns, maxValue, rng) {
if (rng==undefined) rng=Math.random;
return Vector(columns).fill(function () {
return (rng()*maxValue)|0;
});
}
Vector.sin = function (m) {
return Vector.clone(m).sin();
}
Vector.sub = function (m,v) {
return Vector.clone(m).sub(v);
}
Vector.zero = function (columns) {
return Vector(columns).fill(0);
}
/********* INSTANCE MEMBERS *********/
// Fix some prototype methods for generic array data content
Vector.prototype._arrayFix = function () {
var self=this;
this.get=function (column) { return self.data[self.offset+column] };
this.set=function (column,v) { self.data[self.offset+column]=v };
}
Vector.prototype.abs = function (v) {
var i,j;
for(i=0;i<this.size;i++) this.data[i]=Math.abs(this.data[i]);
return this;
}
Vector.prototype.add = function (v) {
var i,j;
for(i=0;i<this.size;i++) this.data[i]+=v;
return this;
}
Vector.prototype.apply = function (f) {
for(var i=0; i < this.columns; i++)
f.call(this,i)
}
Vector.prototype.clone = function () {
return Vector(this);
}
Vector.prototype.cos = function (v) {
var i,j;
for(i=0;i<this.size;i++) this.data[i]=Math.cos(this.data[i]);
return this;
}
Vector.prototype.div = function (v) {
var i,j;
for(i=0;i<this.size;i++) this.data[i]/=v;
return this;
}
Vector.prototype.divide = function (column,k) {
return this.data[column] /= k;
}
// Evaluate all elements x of matrix by applying function f(x)
Vector.prototype.eval = function (f) {
var i;
switch (this.dtn) {
case 'Array':
for(i=0; i < this.columns; i++)
this.set(i,f(this.get(i)))
break;
default:
for(i=0;i<this.size;i++) this.data[i]=f(this.data[i],i);
}
return this;
}
Vector.prototype.exp = function (v) {
var i,j;
for(i=0;i<this.size;i++) this.data[i]=Math.exp(this.data[i]);
return this;
}
Vector.prototype.fill = function (valueOrFunction) {
if (typeof valueOrFunction == 'function') {
for(var i=0;i<this.columns;i++) {
this.data[i]=valueOrFunction(i);
}
} else this.data.fill(valueOrFunction);
return this;
}
Vector.prototype.filter = function (f,asArray) {
var i,j=0,res = Vector(this.columns,{dtn:asArray?'Array':this.dtn});
for(i=0;i<this.columns;i++) {
v=f(this.data[i],i);
if (v) res.data[j]=this.data[i],j++;
}
return j<this.columns?res.slice(j):res;
}
Vector.prototype.get = function (column) {
return this.data[this.offset+column];
}
Vector.prototype.imag = function (i) {
if (this.complex) return this.get(i*2+1);
}
Vector.prototype.incr = function (column,delta) {
return this.data[column] += delta;
}
Vector.prototype.info = function () {
var i = {
dtn:this.dtn,
size:this.size,
columns:this.columns,
offset:this.offset,
}
if (this.scaler) i.scaler=this.scaler;
if (this.complex) i.complex=true;
return i;
}
isVector = Vector.isVector = function (o) {
return (o instanceof Vector)
}
Vector.prototype.magnitude = function () {
var res;
if (this.complex) {
res=Vector(this.columns,{dtn:this.dtn});
for(var i=0; i < res.columns; i++)
res.data[i]=complex.magnitude(this.offset+i*2,this.data);
}
return res;
}
Vector.prototype.map = function (f,asArray) {
var res = Vector(this.columns,{dtn:asArray?'Array':this.dtn});
for(var i=0;j<this.columns;i++)
res.data[i]=f(this.data[i],i);
return res;
}
Vector.prototype.multiply = function (column,k) {
return this.data[column] *= k;
}
Vector.prototype.mean = function (v) {
return this.sum()/this.size;
}
Vector.prototype.mod = function (v) {
var i,j;
for(i=0;i<this.size;i++) this.data[i]=this.data[i]%v;
return this;
}
Vector.prototype.mul = function (v) {
var i,j;
for(var i=0;i<this.size;i++) this.data[i]*=v;
return this;
}
Vector.prototype.neg = function (v) {
var i,j;
for(var i=0;i<this.size;i++) this.data[i]=-this.data[i];
return this;
}
Vector.prototype.phase = function () {
var res;
if (this.complex) {
res=Vector(this.columns,{dtn:this.dtn});
for(var i=0; i < res.columns; i++)
res.data[i]=complex.phase(this.offset+i*2,this.data);
}
return res;
}
Vector.prototype.prod = function (v) {
var i,j,v = 1;
for (i = 0; i < this.size; i++) v *= this.data[i];
return v;
}
Vector.prototype.print = function (format,transpose) {
var j, s='';
if (!format) format = '%4.2f';
if (!this.complex)
for(j=0;j<this.columns;j++) {
if (j!=0) s = s + (transpose?' ':'\n');
s = s + sprintf.sprintf(format,this.data[j]) ;
}
else
for(j=0;j<this.columns;j=j+2) {
if (j!=0) s = s + (transpose?' ':'\n');
s = s + '('+sprintf.sprintf(format,this.data[j])+','+sprintf.sprintf(format,this.data[j+1])+')' ;
}
return s;
}
Vector.prototype.reduce = function (f) {
return this.data.reduce(f);
}
Vector.prototype.real = function (i) {
if (this.complex) return this.get(i*2);
}
Vector.prototype.resize = function (options) {
if ((options.offset && (options.columns+options.offset) > this.columns) ||
!options.columns) throw new Error('Vecotr.resize: invalid argument(s)');
this.columns=options.columns;
if (options.offset) this.offset=options.offset;
this.size=this.length=this.columns;
if (options.slice)
this.data=options.offset?this.data.slice(options.offset,options.columns+offset):
this.data.slice(0,options.columns);
return this;
}
Vector.prototype.set = function (column,val) {
this.data[this.offset+column]=val;
return this;
}
Vector.prototype.sin = function (v) {
var i,j;
for(i=0;i<this.size;i++) this.data[i]=Math.sin(this.data[i]);
}
/*
size
Properties
size (number) : The number of elements in the matrix.
*/
Vector.prototype.size = function () {
return this.size;
}
/** Return new vecotr with sliced data
*
*/
Vector.prototype.slice = function (columns,offset) {
return Vector(this,{columns:columns,offset:offset,slice:true});
}
Vector.prototype.sub = function (v) {
var i,j;
for(i=0;i<this.size;i++) this.data[i]-=v;
return this;
}
Vector.prototype.subRange = function (columns,offset) {
offset=checkOption(offset,0);
var res=Vector({columns:columns,data:this.data.slice(offset,columns+offset),dtn:this.dtn});
return res;
}
Vector.prototype.sum = function () {
var sum=0;
for(var i=0;i<this.size;i++) sum += this.data[i];
return sum
}
module.exports = Vector;
};
BundleModuleCode['numerics/polyfill']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.sblab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2019 bLAB
** $CREATED: 1-1-19 by sbosse.
** $VERSION: 1.2.1
**
** $INFO:
**
** Various core poylfills and extensions
**
** $ENDOFINFO
*/
/* TYPED ARRAY */
var typed_arrays = [
Int8Array,
Uint8Array,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array
];
TypedArrayOfName = {
Int8Array:Int8Array,
Uint8Array:Uint8Array,
Int16Array:Int16Array,
Uint16Array:Uint16Array,
Int32Array:Int32Array,
Uint32Array:Uint32Array,
Float32Array:Float32Array,
Float64Array:Float64Array
}
typed_arrays.forEach(function (typed_array) {
if (!typed_array.prototype.slice) typed_array.prototype.slice = function (begin, end) {
var len = this.length;;
var size;
var start = begin || 0;
start = (start >= 0) ? start : Math.max(0, len + start);
end = end || len;
var up_to = (typeof end == 'number') ? Math.min(end, len) : len;
if (end < 0) up_to = len + end;
// actual expected size of the slice
size = up_to - start;
// if size is negative it should return an empty array
if (size <= 0) size = 0;
var typed_array_constructor = this.constructor;
var cloned = new typed_array_constructor(size);
for (var i = 0; i < size; i++) {
cloned[i] = this[start + i];
}
return cloned;
};
if (!typed_array.prototype.print) typed_array.prototype.print = function () {
var s = '[ ', sep='';
for (var i=0;i<this.length;i++) {
s = s + (i>0?' , ':'') + this[i].toString();
}
return s+' ]';
};
if (!typed_array.prototype.reduce) typed_array.prototype.reduce = function (apply) {
var res=this[0];
for (var i=1;i<this.length;i++) {
res=apply(res,this[i]);
}
return res;
};
});
isTypedArray = function (o) {
for(var t in typed_arrays) if (o instanceof typed_arrays[t]) return true;
return false;
}
isArray = function (v) { return v instanceof Array }
isNumber = function (v) { return typeof v == 'number' }
isObject = function (v) { return typeof v == 'object' }
isBuffer = function (v) { return v instanceof Buffer}
isFunction = function (v) { return typeof v == 'function' }
isString = function (v) { return typeof v == 'string' }
isBoolean = function (v) { return typeof v == 'boolean' }
// ARRAY polyfills
if (typeof Array.prototype.scale != 'function')
Object.defineProperty(Array.prototype, 'scale', {value: function (k,off,inplace) {
var ar = this;
if (isBoolean(off)) inplave=off,off=undefined;
if (off!=undefined) {
if (inplace) for(var i=0;i< ar.length; i++) ar[i]=(ar[i]-off)*k;
else ar=this.map(function (v) { return (v-off)*k });
} else {
if (inplace) for(var i=0;i< ar.length; i++) ar[i]=ar[i]*k;
else ar=this.map(function (v) { return v*k });
}
return ar;
}, configurable: true})
if (typeof Array.prototype.get == 'undefined') {
Object.defineProperty(Array.prototype, 'get', {value:function (i) {
return this[i];
}, configurable: true})
Object.defineProperty(Array.prototype, 'set', {value: function (i,v) {
this[i]=v;
}, configurable: true})
}
};
BundleModuleCode['numerics/matrix']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.sblab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2019 bLAB
** $CREATED: 1-1-19 by sbosse.
** $VERSION: 1.3.3
**
** $INFO:
**
** Numerical Matrix Module associated with typed arrays, but with genery array compatibility.
** A matrix provides a wrapper and multi-dimensional array view for one-dimensional byte arrays (typed arrays using buffers).
**
** $ENDOFINFO
*/
Require('numerics/polyfill')
var sprintf = Require('com/sprintf');
var Vector = Require('numerics/vector');
var ALL = [],
FORALL = '*',
FOREACH = 'x';
isRange = function (v) { return isArray(v) && v.length==2 }
isAll = function (v) { return v=='*' || (isArray(v) && v.length==0) }
isForEach = function (v) { return v == FOREACH }
isArrayArray = function (v) { return isArray(v) && isArray(v[0]) }
isArrayArrayArray = function (v) { return isArray(v) && isArray(v[0]) && isArray(v[0][0]) }
integer = function (v) { return Math.floor(v) }
divide = function (a,b) { return Math.floor(a/b) }
function todo (what) { throw ("Not implemented: Matrix."+what) }
function checkNumber(name, value) {
if (typeof value !== 'number') {
throw new TypeError(name+' must be a number');
}
}
function transpose (layout) {
switch (layout) {
case 12: return 21;
case 21: return 12;
}
}
/********** TYPEDARRY/ARRAY Extension for Matrix/Vector compatibility *************/
// Most generic versions - always overwrite (polyfill/vector definitions)
Object.defineProperty(Array.prototype, 'get', {value: function (i,j,k) {
if (k!=undefined)
return this[i][j][k];
else if (j!=undefined)
return this[i][j];
else
return this[i];
}, configurable: true})
Object.defineProperty(Array.prototype, 'getRow', {value: function (i) {
return this[i];
}, configurable: true})
Object.defineProperty(Array.prototype, 'mapRow', {value: function (f) {
return this[i].map(f);
}, configurable: true})
Object.defineProperty(Array.prototype, 'set', {value: function (a,b,c,d) {
if (d!=undefined)
return this[a][b][c]=d;
else if (c!=undefined)
return this[a][b]=c;
else
return this[a]=b;
}, configurable: true})
Object.defineProperty(Array.prototype, 'setRow', {value: function (i,row) {
return this[i]=row;
}, configurable: true})
Object.defineProperty(Array.prototype, 'print', {value: function (format) {
var i,j,k,s='',sep='', info=this.info();
if (!format) format = '%4.2f';
switch (info.dims) {
case 1:
for(j=0;j<info.columns;j++) {
if (j!=0) s = s + '\n';
s = s + sprintf.sprintf(format,this[j]) ;
}
break;
case 2:
for(j=0;j<info.rows;j++) {
sep = '';
if (j!=0) s = s + '\n';
for (i=0;i<info.columns;i++) {
s = s + sep + sprintf.sprintf(format,this[j][i]) ;
sep = ' ';
}
}
break;
case 3:
for(k=0;k<info.levels;k++) {
if (k!=0) s = s + '\n\n';
for(j=0;j<info.rows;j++) {
sep = '';
if (j!=0) s = s + '\n';
for (i=0;i<info.columns;i++) {
s = s + sep + sprintf.sprintf(format,this[k][j][i]) ;
sep = ' ';
}
}
}
}
return s;
}, configurable: true})
Object.defineProperty(Array.prototype, 'info', {value: function () {
var rows,columns,levels;
if (isArrayArrayArray(this)) levels=this.length,rows=this[0].length,columns=this[0][0].length;
else if (isArrayArray(this)) rows=this.length,columns=this[0].length;
else columns=this.length;
if (levels) return {
dtn:'Array',
size:levels*rows*columns,
levels:levels,
rows:rows,
columns:columns,
dims:3,
offset:0,
}; else if (rows) return {
dtn:'Array',
size:rows*columns,
rows:rows,
columns:columns,
dims:2,
offset:0,
}; else return {
dtn:'Array',
size:columns,
columns:columns,
dims:1,
offset:0,
}
}, configurable: true})
/****************** MATRIX ***************************/
// Matrix object based on typed arrays!
// Supports mixed mode typed arrays and generic arrays!
// {type:function,dtn:string} specifies data type
//
// Usage:
// Matrix(columns:number)
// Matrix(rows:number,columns:number)
// Matrix(levels:number,rows:number,columns:number)
// Matrix(rows:number,columns:number,options:{dtn:string})
// Matrix([])
// Matrix([][])
// Matrix([][][])
// Matrix({data:buffer|typedarray,rows:numner,columns:number,dtn:..})
//
// typeof return = Matrix
function Matrix (a,b,c,d) {
var self = this;
var rows,columns,levels,dims=2,dtn,dt=Matrix.options.dt,data,
layout=12,size,transpose;
var options = isObject(d)?d:(isObject(c)?c:(isObject(b)?b:{}));
if (!(this instanceof Matrix)) return new Matrix(a,b,c,d);
if (isNumber(a) && isNumber(b)) {
// Create new empty matrix (2/3 dims)
rows=a;
columns=b;
if (isNumber(c)) levels=c;
dims=levels?3:2;
if (options.type) dt=options.type;
if (options.dtn) dt=options.dtn=='Array'?Array:TypedArrayOfName[options.dtn];
if (options.layout) layout=options.layout;
else layout=dims==2?12:123;
size=rows*columns;
if (levels) size *= levels;
if (options.dtn && !dt) throw ("Matrix: Unknown array type dtn="+options.dtn)
if (dt.name=='Array')
data=new Array(rows).fill(null).map(function (row) { return new Array(columns).fill(0) });
else
data=new dt(size);
}
else if (isNumber(a)) {
// Create a new empty matrix vector (rows=1)
rows=1;
columns=a;
dims=2;
if (options.type) dt=options.type;
if (options.dtn) dt=TypedArrayOfName[options.dtn];
if (options.layout) layout=options.layout;
else layout=12;
if (options.dtn && !dt) throw ("Matrix: Unknown array type dtn="+options.dtn)
size=columns;
data=new dt(size);
}
else if (isArrayArray(a)) {
rows=a.length;
columns=a[0].length;
if (isArrayArrayArray(a)) levels=rows,rows=a[0].length,columns=a[0][0].length;
size=rows*columns;
if (levels) size *= levels;
dims=levels?3:2;
if (options.type) dt=options.type;
if (options.dtn) {
dt=TypedArrayOfName[options.dtn];
}
if (options.layout) layout=options.layout;
else layout=dims==2?12:123;
if (options.dtn && !dt) throw ("Matrix: Unknown array type dtn="+options.dtn)
if (options.dtn && options.dtn != 'Array') {
// Create typedarray from generic array
data=new dt(size);
switch (layout) {
case 12:
a.forEach(function (row,rowi) {
row.forEach(function (col,coli) {
data[coli+rowi*columns]=col;
})
});
break;
case 21:
a.forEach(function (row,rowi) {
row.forEach(function (col,coli) {
data[rowi+coli*rows]=col; // TBCHECK!
})
});
break;
}
} else {
// Matrix wrapper for generic arrays and array arrays
// modify .get .set .getRow prototype ...
// no _Matrix.call
dt=Array;
data=a;
}
} else if (isArray(a)) {
// Vector
rows=1;
columns=a.length;
size=columns;
dims=2;
if (options.type) dt=options.type;
if (options.dtn) dt=TypedArrayOfName[options.dtn];
if (options.layout) layout=options.layout;
else layout=12;
if (options.dtn && !dt) throw ("Matrix: Unknown array type dtn="+options.dtn)
if (options.dtn && options.dtn != 'Array') {
// Create typedarray from generic array
data=new dt(a);
} else {
// Matrix wrapper for generic arrays and array arrays
// modify .get .set .getRow prototype ...
// no _Matrix.call
dt=Array;
data=[a];
}
} else if (a instanceof Matrix) {
if (options.transpose) {
// transposeView !
rows=a.columns;
columns=a.rows;
levels=a.levels;
size=a.size;
dims=a.dims;
transpose=true;
data=a.data;
dtn=a.dtn;
switch (a.layout) {
case 12: layout=21; break;
case 21: layout=12; break;
case 123: layout=321; break;
case 321: layout=123; break;
}
} else {
// Copy
rows=options.rows||a.rows;
columns=options.columns||a.columns;
levels=options.levels||a.levels;
dims=a.dims;
size=rows*columns;
if(levels) size*=levels;
transpose=false;
scale=options.scale;
if ((options.dtn && options.dtn != a.dtn) || size != a.size) {
// convert or resize dtn
dtn = options.dtn;
data=new dt(size);
if (scale) for(i=0;i<size;i++) data[i]=a.data[i]/scale;
else for(i=0;i<size;i++) data[i]=a.data[i];
} else {
dtn=a.dtn;
if (dtn != 'Array')
data=a.data.slice();
else {
// TODO dims=3
data=a.data.map(function (row) { return row.slice() });
}
}
if (a.scale) this.scale=a.scale;
if (a.complex) this.complex=a.complex;
layout=a.layout;
}
} else if (isObject(a) && a.data) {
// Partial matrix object
rows=a.rows||(a.y && a.x);
columns=a.columns||a.y||a.x;
levels=a.levels||a.z;
size=a.size||((rows?rows:1)*(columns?columns:1)*(levels?levels:1));
dims=a.dims||(levels?3:(rows?2:1));
layout=a.layout||(levels?123:(rows?12:1));
dtn=a.dtn;
data=a.data;
}
if (levels) this.levels=levels; // z
this.rows=rows; // x
this.columns=columns; // x/y
this.size=size;
this.layout=layout;
this.data=data;
this.dims=dims;
this.length=levels?levels:(rows?rows:columns);
this.dtn=dtn||dt.name;
if (options.complex) this.complex=true;
if (options.scale) this.scaler=options.scale;
// get/set index order:
// 1. column(x)
// 2. row(x),column(y)
// 3. row(x),column(y),level(z)
if (this.dtn=='Array') {
switch (this.layout) {
case 12:
this.get=function (row,column) { return this.data[row][column] };
this.set=function (row,column,v) { this.data[row][column]=v };
break;
case 21:
// transposed view
this.get=function (column,row) { return this.data[row][column] };
this.set=function (column,row,v) { this.data[row][column]=v };
break;
case 123:
this.get=function (row,column,level) { return this.data[row][column][level] };
this.set=function (row,column,level,v) { this.data[row][column][level]=v };
break;
}
} else switch (this.layout) {
case 1:
// x=column
this.index = function (x) { return x }
this.get = function (x) { return this.data[x] }
this.set = function (x,v) { return this.data[x]=v }
break;
case 12:
// x=row,y=column
this.index = function (x,y) { return x*self.columns+y}
this.get = function (x,y) { return this.data[x*this.columns+y] }
this.set = function (x,y,v) { return this.data[x*this.columns+y]=v }
break;
case 21:
// x=row,y=column
this.index = function (x,y) { return y*this.rows+x }
this.get = function (x,y) { return this.data[y*this.rows+x] }
this.set = function (x,y,v) { return this.data[y*this.rows+x]=v }
break;
case 123:
// x=row,y=column,z=level
this.index = function (x,y,z) { return z+y*self.columns+x*this.columns*this.rows }
this.get = function (x,y,z) { return this.data[z+y*this.levels*this.rows+x*this.levels] }
this.set = function (x,y,z,v) { return this.data[z+y*this.levels*this.rows+x*this.levels]=v }
break;
case 321:
// x=row,y=column,z=level
// TBC
this.index = function (x,y,z) { return x+y*self.rows+z*this.columns*this.rows }
this.get = function (x,y,z) { return this.data[x+y*this.rows+z*this.columns*this.rows] }
this.set = function (x,y,z,v) { return this.data[x+y*self.rows+z*this.columns*this.rows]=v }
break;
}
}
Matrix.options = {
dt : Float32Array,
dtn : 'Float32Array'
}
/******** STATIC MEMBERS ********/
Matrix.abs = function (m) {
return Matrix.clone(m).abs();
}
Matrix.add = function (m,v) {
return Matrix.clone(m).add(v);
}
Matrix.clone = function (src) {
return Matrix(src);
}
Matrix.columnVector = function (array) {
return Matrix(array)
}
// Return an (typed)array
Matrix.checkArray = function (arrayOrMatrix) {
if (arrayOrMatrix instanceof _MatrixConstructor) return arrayOrMatrix.data;
else return arrayOrMatrix;
}
// Return a Matrix
Matrix.checkMatrix = function (arrayOrMatrix) {
if (arrayOrMatrix instanceof Matrix) return arrayOrMatrix;
else return Matrix(arrayOrMatrix);
}
Matrix.checkMatrixSize = function (matrix1,matrix2) {
if (matrix1.dims != matrix2.dims) return false;
if (matrix1.rows != matrix2.rows ||
matrix1.columns != matrix2.columns ||
matrix1.levels != matrix2.levels ) return false;
}
Matrix.cos = function (m) {
return Matrix.clone(m).cos();
}
Matrix.diag = function (array,rows,columns) {
if (!rows) rows=array.length;
if (!columns) columns=rows;
if (rows!=columns) Matrix.error("Not a square matrix",'diag');
return Matrix(rows,columns).fill(function (i,j) {
return i==j?array[i]:0;
})
}
Matrix.div = function (m,v) {
return Matrix.clone(m).div(v);
}
Matrix.empty = function (rows,columns) {
return Matrix(rows,columns);
}
Matrix.error = function (what,where,ref) {
throw new Error((where?('Matrix.'+where+': '):'')+what);
}
Matrix.errorRange = function (what,where,ref) {
throw new RangeError((where?('Matrix.'+where+': '):'')+what);
}
Matrix.eye = function (rows,columns,val,options) {
if (!val) val=1;
if (!columns) columns=rows;
return Matrix(rows,columns,options).fill(function (i,j) {
return i==j?val:0;
});
}
Matrix.exp = function (m) {
return Matrix.clone(m).exp();
}
isMatrix = Matrix.isMatrix = function (o) {
return (o instanceof Matrix)
}
Matrix.max = function(matrix1, matrix2) {
var result;
matrix1 = this.checkMatrix(matrix1);
matrix2 = this.checkMatrix(matrix2);
if (!this.checkMatrixSize(matrix1,matrix2)) Matrix.error('matrix1 not compatble with matrix2','max');
var rows = matrix1.rows;
var columns = matrix1.columns;
var levels = matrix1.levels;
switch (matrix1.dims) {
case 1:
break;
case 2:
result = Matrix(rows, columns, {dtn:matrix1.dtn});
for (var i = 0; i < rows; i++) {
for (var j = 0; j < columns; j++) {
result.set(i, j, Math.max(matrix1.get(i, j), matrix2.get(i, j)));
}
}
break;
case 3:
break;
}
return result;
}
Matrix.min = function(matrix1, matrix2) {
var result;
matrix1 = this.checkMatrix(matrix1);
matrix2 = this.checkMatrix(matrix2);
if (!this.checkMatrixSize(matrix1,matrix2)) Matrix.error('matrix1 not compatble with matrix2','min');
var rows = matrix1.rows;
var columns = matrix1.columns;
var levels = matrix1.levels;
switch (matrix1.dims) {
case 1:
break;
case 2:
result = Matrix(rows, columns, levels, {dtn:matrix1.dtn});
for (var i = 0; i < rows; i++) {
for (var j = 0; j < columns; j++) {
result.set(i, j, Math.min(matrix1.get(i, j), matrix2.get(i, j)));
}
}
break;
}
return result;
}
Matrix.mod = function (m,v) {
return Matrix.clone(m).mod(v);
}
Matrix.mul = function (m,v) {
return Matrix.clone(m).mul(v);
}
Matrix.neg = function (m) {
return Matrix.clone(m).neg();
}
Matrix.ones = function (rows,columns) {
return Matrix(rows,columns).fill(1);
}
Matrix.rand = function (rows, columns, rng) {
if (rng==undefined) rng=Math.random;
return Matrix(rows,columns).fill(function () {
return rng();
});
}
Matrix.randInt = function (rows, columns, maxValue, rng) {
if (rng==undefined) rng=Math.random;
return Matrix(rows,columns).fill(function () {
return (rng()*maxValue)|0;
});
}
Matrix.sin = function (m) {
return Matrix.clone(m).sin();
}
Matrix.sub = function (m,v) {
return Matrix.clone(m).sub(v);
}
Matrix.zero = function (rows,columns) {
return Matrix(columns,rows).fill(0);
}
/********* INSTANCE MEMBERS *********/
Matrix.prototype.abs = function (v) {
this.eval(Math.abs);
return this;
}
Matrix.prototype.add = function (v) {
this.eval(function (x) {return x+v});
return this;
}
Matrix.prototype.apply = function (f) {
var i,j,k;
switch (this.dims) {
case 1:
for(j=0; j < this.columns; j++)
f.call(this,j)
return this;
case 2:
for(i=0; i<this.rows;i++)
for(j=0; j < this.columns; j++)
f.call(this,i,j)
return this;
case 3:
for(i=0; i<this.rows;i++)
for(j=0; j < this.columns; j++)
for(k=0; k<this.levels;k++)
f.call(this,i,j,k)
return this;
}
}
Matrix.prototype.checkMatrixDims = function(dims) {
if (this.dims != dims) this.errorRange('Matrix has not expected dimension '+dims);
}
/**
* @private
* Check that a column index is not out of bounds
* @param {Matrix} matrix
* @param {number} index
* @param {boolean} [outer]
*/
Matrix.prototype.checkColumnIndex = function(index, outer) {
var max = outer ? this.columns : this.columns - 1;
if (index < 0 || index > max) this.errorRange('Column index out of range');
}
/**
* @private
* Check that a row index is not out of bounds
* @param {Matrix} matrix
* @param {number} index
* @param {boolean} [outer]
*/
Matrix.prototype.checkRowIndex = function(index, outer) {
var max = outer ? this.rows : this.rows - 1;
if (index < 0 || index > max)
this.errorRange('Row index out of range');
}
/**
* @private
* Check that the provided vector is an array with the right length
* @param {Matrix} matrix
* @param {Array|Matrix} vector
* @return {Array}
* @throws {RangeError}
*/
Matrix.prototype.checkRowVector = function(vector) {
if (vector.to1DArray) {
vector = vector.to1DArray();
}
if (vector.length !== this.columns)
this.errorRange(
'vector size must be the same as the number of columns'
);
return vector;
}
/**
* @private
* Check that the provided vector is an array with the right length
* @param {Matrix} matrix
* @param {Array|Matrix} vector
* @return {Array}
* @throws {RangeError}
*/
Matrix.prototype.checkColumnVector = function(vector) {
if (vector.to1DArray) {
vector = vector.to1DArray();
}
if (vector.length !== this.rows)
this.errorRange('vector size must be the same as the number of rows');
return vector;
}
Matrix.prototype.checkIndices = function(rowIndices, columnIndices) {
return {
row: this.checkRowIndices(rowIndices),
column: this.checkColumnIndices(columnIndices)
};
}
Matrix.prototype.checkRowIndices = function(rowIndices) {
var self=this;
if (typeof rowIndices !== 'object') {
throw new TypeError('unexpected type for row indices');
}
var rowOut = rowIndices.some(function (r) {
return r < 0 || r >= self.rows;
});
if (rowOut) {
throw new RangeError('row indices are out of range');
}
if (!Array.isArray(rowIndices)) rowIndices = Array.from(rowIndices);
return rowIndices;
}
Matrix.prototype.checkColumnIndices = function(columnIndices) {
var self=this;
if (typeof columnIndices !== 'object') {
throw new TypeError('unexpected type for column indices');
}
var columnOut = columnIndices.some(function (c) {
return c < 0 || c >= self.columns;
});
if (columnOut) {
throw new RangeError('column indices are out of range');
}
if (!Array.isArray(columnIndices)) columnIndices = Array.from(columnIndices);
return columnIndices;
}
Matrix.prototype.checkRange = function(startRow, endRow, startColumn, endColumn) {
if (arguments.length !== 4) {
throw new RangeError('expected 4 arguments');
}
checkNumber('startRow', startRow);
checkNumber('endRow', endRow);
checkNumber('startColumn', startColumn);
checkNumber('endColumn', endColumn);
if (
startRow > endRow ||
startColumn > endColumn ||
startRow < 0 ||
startRow >= this.rows ||
endRow < 0 ||
endRow >= this.rows ||
startColumn < 0 ||
startColumn >= this.columns ||
endColumn < 0 ||
endColumn >= this.columns
) {
throw new RangeError('Submatrix indices are out of range');
}
}
Matrix.prototype.clone = function () {
return Matrix(this);
}
/** Copy (1) a sorurce array (vector) into this matrix (row/column w or w/o subrange), or (2) create a copy of this matrix (empty argument list)
*
* copy()
* copy([a,b]|[],[v1,v2,...])
* copy(number,[a,b]|[],[v1,v2,...])
* copy([a,b]|[],number,[v1,v2,...])
* copy(number,number,[a,b]|[],[v1,v2,...])
* ..
*/
Matrix.prototype.copy = function (a,b,c,d) {
var x,y,z,rx,ry,rz,i,j,k,src;
if (isNumber(a)) i=a;
if (isNumber(b)) j=b;
if (isNumber(c)) k=c;
if (isArray(a)) rx=a;
if (isArray(b)) ry=b;
if (isArray(c)) rz=c;
if (isArray(d)) src=d;
if (isVector(d)) src=d;
if (!src && !d && (isArray(c) || isVector(c))) src=c,rz=undefined;
if (!src && !c && (isArray(b) || isVector(b))) src=b,ry=undefined;
if (!src && !a && (isArray(a) || isVector(a))) src=a,rx=[0,this.columns-1]; // 1-dim only
if (isVector(src)) src=src.data;
if (!src) return Matrix({
rows:this.rows,
columns:this.columns,
levels:this.levels,
dtn:this.dtn,
layout:this.layout,
data:this.data.slice()
})
if (!src) throw "Matrix.copy: no source array provided";
if (rx && rx.length==0) rx=[0,this.rows-1];
if (ry && ry.length==0) ry=[0,this.columns-1];
if (rz && rz.length==0) rz=[0,this.levels-1];
if (rx && (rx[1]-rx[0]+1)!=src.length) throw "Matrix.copy: range mismatch (src)"
if (ry && (ry[1]-ry[0]+1)!=src.length) throw "Matrix.copy: range mismatch (src)"
if (rz && (rz[1]-rz[0]+1)!=src.length) throw "Matrix.copy: range mismatch (src)"
switch (this.dims) {
case 1:
for(x=rx[0];x<rx[1];x++) this.data[x]=src[x-rx[0]];
break;
case 2:
if (rx && j != undefined)
for(x=rx[0];x<=rx[1];x++)
this.data[this.index(x,j)]=src[x-rx[0]];
else if (i != undefined && ry)
for(y=ry[0];y<=ry[1];y++)
this.data[this.index(i,y)]=src[y-ry[0]];
else todo('copy 2');
break;
case 3:
if (rx && j != undefined && k != undefined)
for(x=rx[0];x<=rx[1];x++)
this.data[this.index(x,j,k)]=src[x-rx[0]];
else if (ry && i != undefined && k != undefined)
for(y=ry[0];y<=ry[1];y++)
this.data[this.index(i,y,k)]=src[y-ry[0]];
else if (rz && i != undefined && j != undefined)
for(z=rz[0];z<=rz[1];z++)
this.data[this.index(i,j,z)]=src[z-rz[0]];
else todo('copy 3');
break;
}
return this;
}
/** Convert size using a data filter.
** The target size must be provided.
* typeof @filter = 'mean' | 'peak' | 'min' | 'max' | 'win' | 'exp' | 'exp-peak' | function (a:number,b:number,i:number) -> number
*/
Matrix.prototype.convert = function (a,b,c,d) {
var i,j,k,d,v,m,ni,nj,nk,filter;
if (isNumber(a)) i=a;
if (isNumber(b)) j=b;
if (isNumber(c)) k=c;
if (isString(b)) filter=b;
if (isString(c)) filter=c;
if (isString(d)) filter=d;
if (!filter) filter='mean';
if (!i) throw "Matrix.convert: no target size (number)";
m = Matrix(i,j,k);
switch (filter) {
case 'mean': filter=function (a,b,i,n) { if (i==n-1) return (a+b)/n; else return a+b }; break;
case 'exp': filter=function (a,b,i,n) { return (a+b)/2 }; break;
case 'exp-peak': filter=function (a,b,i,n) { return (Math.abs(a)+Math.abs(b))/2 }; break;
case 'peak': filter=function (a,b,i,n) { if (Math.abs(a)>Math.abs(b)) return Math.abs(a); else return Math.abs(a); }; break;
case 'min': filter=function (a,b,i,n) { return a<b?a:b }; break;
case 'max': filter=function (a,b,i,n) { return a>b?a:b }; break;
default: filter = function () { return 0 }
}
switch (this.dims) {
case 1:
ni=Math.floor(this.i/m.i);
for(i=0;i<m.i;i++) {
v=this.data[i*ni];
for(d=1;d<ni;d++) {
v=filter(v,this.data[i*ni+d],d,ni);
}
m.data[i]=v;
}
break;
}
return m;
}
Matrix.prototype.cos = function (v) {
this.eval(Math.cos);
return this;
}
Matrix.prototype.diag = function (v) {
// TODO Vector
var a = [];
if (this.rows!=this.columns) return;
for(var i=0;i<this.rows;i++) a.push(this.data[i+i*this.i]);
return a;
}
Matrix.prototype.dim = function () {
switch (this.dims) {
case 1: return [this.columns];
case 2: return [this.rows,this.columns];
case 3: return [this.rows,this.columns,this.levels];
}
}
Matrix.prototype.div = function (v) {
this.eval(function (x) {return x/v});
return this;
}
Matrix.prototype.divide = function (a,b,c,d) {
switch (this.dims) {
case 1: return this.set(a,this.get(a)/b);
case 2: return this.set(a,b,this.get(a,b)/c);
case 3: return this.set(a,b,c,this.get(a,b,c)/d);
}
}
Matrix.prototype.error = function (what,where) {
throw new Error((where?('Matrix.'+where+': '):'')+what);
}
Matrix.prototype.errorRange = function (what,where) {
throw new RangeError((where?('Matrix.'+where+': '):'')+what);
}
// Evaluate all elements x of matrix by applying function f(x)
Matrix.prototype.eval = function (f) {
var i,j,k;
switch (this.dtn) {
case 'Array':
switch (this.dims) {
case 1:
for(i=0; i < this.columns; i++)
this.set(i,f(this.get(i)))
break;
case 2:
for(i=0; i < this.rows;i++)
for(j=0; j < this.columns; j++)
this.set(i,j,f(this.get(i,j)))
break
case 3:
for(i=0; i < this.rows;i++)
for(j=0; j < this.columns; j++)
for(k=0; k < this.levels; k++)
this.set(i,j,k,f(this.get(i,j,k)))
break;
}
break;
default:
for(i=0;i<this.size;i++) this.data[i]=f(this.data[i]);
}
return this;
}
Matrix.prototype.exp = function (v) {
this.eval(Math.exp);
return this;
}
Matrix.prototype.fill = function (valueOrFunction) {
if (typeof valueOrFunction == 'function') {
switch (this.dims) {
case 1:
for(i=0; i < this.columns; i++)
this.set(i,valueOrFunction(i.j))
return this;
case 2:
for(i=0; i < this.rows;i++)
for(j=0; j < this.columns; j++)
this.set(i,j,valueOrFunction(i,j))
return this;
case 3:
for(i=0; i < this.rows;i++)
for(j=0; j < this.columns; j++)
for(k=0; k < this.levels; k++)
this.set(i,j,k,valueOrFunction(i,j,k))
return this;
}
} else this.data.fill(valueOrFunction);
return this;
}
Matrix.prototype.getCol = function (index,asVector) {
// TODO
}
// Return array or vector
Matrix.prototype.getRow = function (index,asVector) {
this.checkMatrixDims(2);
this.checkRowIndex(index);
var row,data,i,j;
switch (this.dtn) {
case 'Array':
if (this.layout==12) {
if (!asVector)
return this.data[index];
else
return Vector(this.data[index]);
} else {
// transposed view
if (!asVector) {
row = new Array(this.columns);
if (this.rows==1) return this.data;
for (i = 0; i < this.columns; i++) {
row[i] = this.get(index, i);
}
} else {
if (this.rows==1) return this.data;
row= Vector(this.columns,{dtn:this.dtn});
for (i = 0; i < this.columns; i++) {
row.set(i, this.get(index, i));
};
}
}
break;
default:
// With advanced slicing
if (!asVector) {
row = new Array(this.columns);
if (this.rows==1) return this.data;
for (i = 0; i < this.columns; i++) {
row[i] = this.get(index, i);
}
} else if (this.layout == 12) {
// data = this.data.slice(index*this.columns,(index+1)*this.columns);
row= Vector({dtn:this.dtn,data:this.data,offset:index*this.columns,columns:this.columns});
} else {
if (this.rows==1) return this.data;
row= Vector(this.columns,{dtn:this.dtn});
for (i = 0; i < this.columns; i++) {
row.set(i, this.get(index, i));
};
}
}
return row;
}
// x += delta
Matrix.prototype.incr = function (a,b,c,d) {
switch (this.dims) {
case 1: return this.set(a,this.get(a)+b);
case 2: return this.set(a,b,this.get(a,b)+c);
case 3: return this.set(a,b,c,this.get(a,b,c)+d);
}
}
Matrix.prototype.info = function () {
var o = {
dtn:this.dtn,
size:this.size,
columns:this.columns,
layout:this.layout,
dims:this.dims
}
if (this.rows) o.rows=this.rows;
if (this.levels) o.levels=this.levels;
if (this.scaler) o.scaler=this.scaler;
if (this.complex) o.complex=true;
return o;
}
Matrix.prototype.isColumnVector = function () {
return this.columns === 1;
}
Matrix.prototype.isEchelonForm = function () {
this.checkMatrixDims(2);
var i = 0;
var j = 0;
var previousColumn = -1;
var isEchelonForm = true;
var checked = false;
while ((i < this.rows) && (isEchelonForm)) {
j = 0;
checked = false;
while ((j < this.columns) && (checked === false)) {
if (this.get(i, j) === 0) {
j++;
} else if ((this.get(i, j) === 1) && (j > previousColumn)) {
checked = true;
previousColumn = j;
} else {
isEchelonForm = false;
checked = true;
}
}
i++;
}
return isEchelonForm;
}
Matrix.prototype.isReducedEchelonForm = function () {
this.checkMatrixDims(2);
var i = 0;
var j = 0;
var previousColumn = -1;
var isReducedEchelonForm = true;
var checked = false;
while ((i < this.rows) && (isReducedEchelonForm)) {
j = 0;
checked = false;
while ((j < this.columns) && (checked === false)) {
if (this.get(i, j) === 0) {
j++;
} else if ((this.get(i, j) === 1) && (j > previousColumn)) {
checked = true;
previousColumn = j;
} else {
isReducedEchelonForm = false;
checked = true;
}
}
for (var k = j + 1; k < this.rows; k++) {
if (this.get(i, k) !== 0) {
isReducedEchelonForm = false;
}
}
i++;
}
return isReducedEchelonForm;
}
Matrix.prototype.isRowVector = function () {
return this.rows === 1;
}
Matrix.prototype.isSquare = function () {
return this.rows==this.columns
}
Matrix.prototype.isSymmetric = function () {
if (this.isSquare()) {
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j <= i; j++) {
if (this.get(i, j) !== this.get(j, i)) {
return false;
}
}
}
return true;
}
return false;
}
/** Iterate over matrix elements
* Parameter arrays specify iteration ranges, FORALL='*' specifies a target vector range
* iter(function (@elem,@index,@array))
* iter(number [],function)
* iter(number [],number [],function)
* iter(number [],number [],number [],function)
* Examples:
* m.iter(FORALL,[],[],f) <=> for all x-vectors with y in [0,j-1], z in [0,k-1] do ..
* m.iter([], FORALL,[],f) <=> for all y-vectors with x in [0,j-1], z in [0,k-1] do ..
* m.iter([],[],[],f) <=> for all values with x in [0,i-1], y in [0,j-1], z in [0,k-1] do ..
* m.iter(f) <=> for all values with x in [0,i-1], y in [0,j-1], z in [0,k-1] do ..
*
*
*/
Matrix.prototype.iter = function (a,b,c,d) {
var func,rx,ry,rz,x,y,z,
self=this;
if (isFunction(a)) func=a;
else if (isFunction(b)) func=b;
else if (isFunction(c)) func=c;
else if (isFunction(d)) func=d;
if (isArray(a)) rx=a;
if (isArray(b)) ry=b;
if (isArray(c)) rz=c;
if (isString(a)) rx=a;
if (isString(b)) ry=b;
if (isString(c)) rz=c;
if (!func) throw "Matrx.iter: no function supplied";
if (!rx && !ry && !rz) // linear iteration over all elements
return this.data.forEach(func);
switch (this.dims) {
case 1: break;
// TODO
todo('iter 1')
case 2: break;
// TODO
todo('iter 2')
case 3:
if (isArray(rx) && rx.length==0) rx=[0,this.rows];
if (isArray(ry) && ry.length==0) ry=[0,this.columns];
if (isArray(rz) && rz.length==0) rz=[0,this.levels];
if (rz == FORALL) {
for(x=rx[0];x<rx[1];x++) {
for(y=ry[0];y<ry[1];y++) {
func(x,y,this.subMatrixRange(x,y,ALL))
}
}
} else if (rx==FORALL) {
// TODO
todo('iter 3.ryx=FORALL')
} else if (ry==FORALL) {
// TODO
todo('iter 3.ry=FORALL')
} else {
// single data cell iteration
// TODO
todo('iter 3')
}
}
// TODO
return this;
}
Matrix.prototype.map = function (f,asArray) {
var res,i,j,k;
switch (this.dims) {
case 1:
res = Matrix(this.columns,{dtn:asArray?'Array':this.dtn});
for(j=0;j<this.columns;j++)
res.set(j,f(this.get(j),j));
break;
case 2:
res = Matrix(this.rows,this.columns,{dtn:asArray?'Array':this.dtn});
for(i=0;i<this.rows;i++)
for(j=0;j<this.columns;j++)
res.set(i,j,f(this.get(i,j),i,j));
break;
case 3:
res = Matrix(this.rows,this.columns,this.levels,{dtn:asArray?'Array':this.dtn});
for(i=0;i<this.rows;i++)
for(j=0;j<this.columns;j++)
for(k=0;k<this.levels;k++)
res.set(i,j,k,f(this.get(i,j,k),i,j,k));
break;
}
return res;
}
// Row mapping function
Matrix.prototype.mapRow = function (f) {
var res=[];
for(var row=0;row<this.rows;row++) {
res.push(f(this.getRow(row)));
}
return res;
}
/** Return minimum and maximum value of the matrix
*
*/
Matrix.prototype.minmax = function () {
var d0=Number.MAX_VALUE,d1=Number.MIN_VALUE;
for (i = 0;i < this.size; i++) {
d0=Math.min(d0,this.data[i]);
d1=Math.max(d1,this.data[i]);
}
return { min:d0, max:d1 };
}
Matrix.prototype.mapToArray = function (f) {
var res = new Array(this.size);
for(var i=0;i<this.rows;i++)
for(var j=0;j<this.columns;j++)
res[i*this.columns+j]=f(this.get(i,j),i,j);
return res;
}
// x *= k
Matrix.prototype.multiply = function (a,b,c,d) {
switch (this.dims) {
case 1: return this.set(a,this.get(a)*b);
case 2: return this.set(a,b,this.get(a,b)*c);
case 3: return this.set(a,b,c,this.get(a,b,c)*d);
}
}
Matrix.prototype.mean = function (v) {
return this.sum()/this.size;
}
Matrix.prototype.mod = function (v) {
this.eval(function (x) {return x%v});
return this;
}
/**
* Returns the matrix product between this and other
* @param {Matrix} other
* @return {Matrix}
*/
Matrix.prototype.mmul = function (other) {
this.checkMatrixDims(2);
other = Matrix.checkMatrix(other);
if (this.columns !== other.rows) {
// eslint-disable-next-line no-console
console.warn('Number of columns of left matrix are not equal to number of rows of right matrix.');
}
var m = this.rows;
var n = this.columns;
var p = other.columns;
var result = Matrix(m, p, {dtn:this.dtn});
var Bcolj = new Array(n);
for (var j = 0; j < p; j++) {
for (var k = 0; k < n; k++) {
Bcolj[k] = other.get(k, j);
}
for (var i = 0; i < m; i++) {
var s = 0;
for (k = 0; k < n; k++) {
s += this.get(i, k) * Bcolj[k];
}
result.set(i, j, s);
}
}
return result;
}
Matrix.prototype.mul = function (v) {
this.eval(function (x) {return x*v});
return this;
}
Matrix.prototype.neg = function (v) {
this.eval(function (x) {return -x});
return this;
}
Matrix.prototype.prod = function (v) {
var i,j,k,v = 1;
// Comp. mode
switch (this.dtn+this.dims) {
case 'Array1':
for (i = 0; i < this.columns; i++) {
v *= this.data[i];
}
break;
case 'Array2':
for (i = 0; i < this.rows; i++) {
for (j = 0; j < this.columns; j++) {
v *= this.data[i][j];
}
}
break;
case 'Array3':
for (i = 0; i < this.rows; i++) {
for (j = 0; j < this.columns; j++) {
for (k = 0; k < this.levels; k++) {
v *= this.data[i][j][k];
}
}
}
break;
default:
for (i = 0; i < this.size; i++) v *= this.data[i];
}
return v;
}
Matrix.prototype.print = function (format) {
var i,j,k,s='',sep='';
if (!format) format = '%4.2f';
switch (this.dims) {
case 1:
for(i=0;i<this.columns;i++) {
if (i!=0) s = s + '\n';
s = s + sprintf.sprintf(format,this.get(i)) ;
}
break;
case 2:
for(i=0;i<this.rows;i++) {
sep = '';
if (i!=0) s = s + '\n';
for (j=0;j<this.columns;j++) {
s = s + sep + sprintf.sprintf(format,this.get(i,j)) ;
sep = ' ';
}
}
break;
case 3:
for(k=0;k<this.levels;k++) {
if (k!=0) s = s + '\n\n';
for(i=0;i<this.rows;i++) {
sep = '';
if (i!=0) s = s + '\n';
for (j=0;j<this.columns;j++) {
s = s + sep + sprintf.sprintf(format,this.get(i,j,k)) ;
sep = ' ';
}
}
}
}
return s;
}
/** Reduce dimension: Linear matrix data reduction applying a function (a,b) -> c to all elements.
* Returns a scalar value or any other object accumulated by the supplied function
*/
Matrix.prototype.reduce = function (f) {
return this.data.reduce(f);
}
/** resize matrix (only modifying meta data - not buffer data)
*
*/
Matrix.prototype.resize = function (options) {
for(var p in options) {
switch (p) {
case 'rows':
case 'columns':
case 'levels':
this[p]=options[p];
break;
}
}
this.size=this.columns*(this.rows?this.rows:1)*(this.levels?this.levels:1);
this.length=this.rows?this.rows:this.columns;
return this
}
Matrix.prototype.reverseRow = function (row) {
var t,len=this.columns;
for(var i=0;i<(len/2)|0;i++) {
t=this.get(row,i);
this.set(row,i,this.get(row,len-i-1));
this.set(row,len-i-1,t);
}
return this;
}
/** Scale (and/or adjust offset optionally of) all matrix elements -= offset *= k
* scale(k)
* scale(k,inplace:boolean)
* scale(k,offset)
* scale(k,offset,inplace:boolean)
*/
Matrix.prototype.scale = function (a,b,c) {
var m,k=1,offset,inplace=false;
if (isNumber(a)) k=a;
if (isBoolean(b)) inplace=b;
else if (isBoolean(c)) inplace=c;
if (isNumber(b)) offset=b;
else if (isNumber(c)) offset=c;
m = inplace?this:this.copy();
if (k!=1) {
if (offset)
for(var i=0;i<m.data.length;i++) m.data[i]=(m.data[i]-offset)*k;
else
for(var i=0;i<m.data.length;i++) m.data[i]=m.data[i]*k;
} else if (offset) {
for(var i=0;i<m.data.length;i++) m.data[i]=m.data[i]-offset;
}
return m;
}
/*
Return a new matrix based on a selection of rows and columns
selection(rowIndices: Array<number>, columnIndices: Array<number>): Matrix
Parameters
rowIndices (Array<number>) The row indices to select. Order matters and an index can be more than once.
columnIndices (Array<number>) The column indices to select. Order matters and an index can be use more than once.
Returns
Matrix: The new matrix
*/
Matrix.prototype.selection = function (rowIndices,columnIndices) {
this.checkMatrixDims(2);
var newMatrix = Matrix(rowIndices.length,columnIndices.length,{dtn:this.dtn});
for (var i = 0; i < rowIndices.length; i++) {
var rowIndex = rowIndices[i];
for (var j = 0; j < columnIndices.length; j++) {
var columnIndex = columnIndices[j];
newMatrix.set(i,j, this.get(rowIndex, columnIndex));
}
}
return newMatrix;
}
// Set a row of the matrix
Matrix.prototype.setRow = function (row,data) {
data=Matrix.checkArray(data);
for(var i=0;i<this.columns;i++) {
this.set(row,i,data[i]);
}
}
// Slice of data in major dimension
Matrix.prototype.slice = function (i,offset) {
var rows,columns,levels;
switch (this.dims) {
case 1:
return Matrix(this,{columns:i,offset:offset,slice:true});
break;
case 2:
case 3:
return Matrix(this,{rows:i,offset:offset,slice:true});
break;
}
}
Matrix.prototype.sin = function () {
this.eval(Math.sin);
return this;
}
/*
size
Properties
size (number) : The number of elements in the matrix.
*/
Matrix.prototype.size = function () {
return this.size;
}
Matrix.prototype.sub = function (v) {
this.eval(function (x) {return x-v});
return this;
}
Matrix.prototype.subMatrix = function (startRow, endRow, startColumn, endColumn) {
this.checkMatrixDims(2);
this.checkRange(startRow, endRow, startColumn, endColumn);
var newMatrix = Matrix(endRow - startRow + 1, endColumn - startColumn + 1, {dtn:this.dtn});
for (var i = startRow; i <= endRow; i++) {
for (var j = startColumn; j <= endColumn; j++) {
newMatrix.set(i - startRow,j - startColumn, this.get(i, j));
}
}
return newMatrix;
}
/** Return a sub-matrix (1-3 dims)
*
*/
Matrix.prototype.subMatrixRange = function (rx,ry,rz) {
var i,j,i0,i1,x0,x1,y0,y1,z0,z1,res;
switch (this.dims) {
case 1:
// simple case, return sliced array
x0=0,x1=this.i-1;
if (isRange(rx)) x0=rx[0],x1=rx[1];
else throw "Matrix.subMatrixRange: no range";
var i0=x0,i1=i0+1+x1;
return Vector({data:this.data.slice(i0,i1),columns:i1-i0,dtn:this.dtn});
case 2:
todo('subMatrixRange 2')
case 3:
if ((isAll(rz) || (isRange(rz)) && isNumber(rx) && isNumber(ry) && this.layout==123)) {
// simple case, return sliced array (1-dim matrix)
z0=0,z1=this.levels-1;
if (isRange(rz)) z0=rz[0],z1=rz[1];
var i0=this.index(rx,ry,z0),i1=i0+1+z1;
return Vector({data:this.data.slice(i0,i1),columns:i1-i0,dtn:this.dtn});
} if ((isAll(rx) || isRange(rx)) && (isAll(ry) || isRange(ry)) && isNumber(rz)) {
res = Matrix(this.rows,this.columns,{dtn:this.dtn});
x0=0,x1=this.rows-1;
if (isRange(rx)) x0=rx[0],x1=rx[1];
y0=0,y1=this.columns-1;
if (isRange(ry)) y0=ry[0],y1=ry[1];
z0=rz;
for(i=x0;i<x1;i++)
for(j=y0;j<y1;j++)
res.set(i,j, this.get(i,j,z0));
return res;
}
else todo('subMatrixRange 3.rx/ry')
}
}
Matrix.prototype.subMatrixRow = function (indices, startColumn, endColumn) {
this.checkMatrixDims(2);
if (startColumn === undefined) startColumn = 0;
if (endColumn === undefined) endColumn = this.columns - 1;
if ((startColumn > endColumn) || (startColumn < 0) || (startColumn >= this.columns) || (endColumn < 0) || (endColumn >= this.columns)) {
throw new RangeError('Argument out of range');
}
var newMatrix = Matrix(indices.length, endColumn - startColumn + 1, {dtn:this.dtn});
for (var i = 0; i < indices.length; i++) {
for (var j = startColumn; j <= endColumn; j++) {
if (indices[i] < 0 || indices[i] >= this.rows) {
throw new RangeError('Row index out of range: '+indices[i]);
}
newMatrix.set(i, j - startColumn, this.get(indices[i], j));
}
}
return newMatrix;
}
Matrix.prototype.subMatrixColumn = function (indices, startRow, endRow) {
this.checkMatrixDims(2);
if (startRow === undefined) startRow = 0;
if (endRow === undefined) endRow = this.rows - 1;
if ((startRow > endRow) || (startRow < 0) || (startRow >= this.rows) || (endRow < 0) || (endRow >= this.rows)) {
throw new RangeError('Argument out of range');
}
var newMatrix = Matrix(endRow - startRow + 1, indices.length, {dtn:this.dtn});
for (var i = 0; i < indices.length; i++) {
for (var j = startRow; j <= endRow; j++) {
if (indices[i] < 0 || indices[i] >= this.columns) {
throw new RangeError('Column index out of range: '+indices[i]);
}
newMatrix.set(j - startRow, i, this.get(j, indices[i]));
}
}
return newMatrix;
}
Matrix.prototype.subRowVector = function (vector) {
this.checkMatrixDims(2);
vector = this.checkRowVector(vector);
for (var i = 0; i < this.rows; i++) {
for (var j = 0; j < this.columns; j++) {
this.set(i, j, this.get(i, j) - vector[j]);
}
}
return this;
}
Matrix.prototype.setSubMatrix = function (matrix, startRow, startColumn) {
matrix = this.checkMatrix(matrix);
this.checkMatrixDims(2);
matrix.checkMatrixDims(2);
var endRow = startRow + matrix.rows - 1;
var endColumn = startColumn + matrix.columns - 1;
this.checkRange(startRow, endRow, startColumn, endColumn);
for (var i = 0; i < matrix.rows; i++) {
for (var j = 0; j < matrix.columns; j++) {
this.set(startRow + i,startColumn + j) = matrix.get(i, j);
}
}
return this;
}
Matrix.prototype.sum = function (by) {
var i,j,k,v=0;
switch (by) {
case 'row':
return this.sumByRow();
case 'column':
return this.sumByColumn();
default:
switch (this.dtn+this.dims) {
case 'Array1':
for (i = 0; i < this.columns; i++) {
v += this.data[i];
}
break;
case 'Array2':
for (i = 0; i < this.rows; i++) {
for (j = 0; j < this.columns; j++) {
v += this.data[i][j];
}
}
break;
case 'Array3':
for (i = 0; i < this.rows; i++) {
for (j = 0; j < this.columns; j++) {
for (k = 0; k < this.levels; k++) {
v += this.data[i][j][k];
}
}
}
break;
default:
for (i = 0; i < this.size; i++) v += this.data[i];
}
return v;
}
}
Matrix.prototype.sumByRow = function () {
var sum = Matrix.zeros(this.rows, 1);
for (var i = 0; i < this.rows; ++i) {
for (var j = 0; j < this.columns; ++j) {
sum.set(i, 0, sum.get(i, 0) + this.get(i, j));
}
}
return sum;
}
Matrix.prototype.sumByColumn = function() {
var sum = Matrix.zeros(1, this.columns);
for (var i = 0; i < this.rows; ++i) {
for (var j = 0; j < this.columns; ++j) {
sum.set(0, j, sum.get(0, j) + this.get(i, j));
}
}
return sum;
}
Matrix.prototype.toArray = function (rx,ry,rz) {
switch (this.dims) {
case 1: return Array.from(this.data);
case 2: todo('toArray 2')
case 3: todo('toArray 3')
}
}
Matrix.ALL=ALL;
Matrix.FOREACH=FOREACH;
Matrix.FORALL=FORALL;
module.exports = Matrix
};
BundleModuleCode['ui/app/app']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2006-2021 bLAB
** $CREATED: 14-08-17 by sbosse.
** $VERSION: 1.8.2
**
** $INFO:
**
** Termnial GUI toolkit supporting pages using blessed/curses library.
** UI layout is similar to mobile application programs.
** A set of styles can defined used as default styles for various elements.
**
** type styles = {
** button?:{fg?,bg?,border?},
** checkbox?:{},
** dialog?:{fg?,bg?},
** filemanager?:{fg?,bg?,arrows?:{fg?,bg?},border?,box:{bg?,border?},
** label?:{fg?,bg?},input?:{fg?,bg?}},
** input:{fg?,bg?,border?,label?},
** keyboard?:{bg?,border?,label?:{fg,bg}},
** label:{bold?,fg?,bg?},
**
** New: UI Builder from compact template
**
** Template (builder) Type Interface
** =================================
**
** (@: smybolic identifier, parameter name, or wildcard place holder, $: type macro, ?:optional, ..:more,
** st {a,b,..}: sub type of object type st)
**
** typeof @border = { type? : string = 'line'|'none'|.., fg:string is color, .. }
**
** typeof @styles = {
** button?: { fg?, bg?, border? },
** input?: { border? },
** tree?: { border? },
** ..,
** @customstyle : { .. },
** ..
** }
**
** Widget event handler:
**
** type handler = function (wname:string,wval:boolean|number|string)
** type handlerT = function (wname:string,label:string,path:string,data:{name:string,parent:node,..})
** type handlerL = function (wname:string,label:string,data:{content:string,..})
** type handler1 = function (wname:string) is check handler
** type handlerp = function (pname:string) is page handler
** type handlerd = function (wname:string,data:*)
**
** Widget styles and attributes:
**
** $pos = top:number | left:number | right:number | center:boolean
** $geom = width:number | hight:number
** $cstyle = fg:string | bg:string
**
** Widget descriptor types:
**
** type button = { type='button', $pos?, $geom?, content:string, $cstyle, .. , onclick?:handler }
** type label = { type='label', $pos?, $geom?,content:string, .. }
** type info = { type='info', $pos?, $geom?, label:string, value?:string, .. }
** type checkbox = { type='checkbox', $pos?, $geom?, text:string, value:string, .. , onclick?:handler, on?: on {check,uncheck} }
** type input = { type='input', $pos, $geom?, wrap?:boolean, label:string, value?:string, onchange?:handler, .. }
** type radiobutton = { type='radiobutton', $pos, $geom?, .. }
** type group = { type='group', name?:string, @but1:radiobutton, @but2:radiobutton, .., onclick?:handler }
** type tree = { type='tree', $pos, $geom, .. , onclick?:handlerT }
** type list = { type='list', depth?:number, $pos, $geom, .. , onclick?:handlerL }
**
** type widget = button | label | checkbox | input | group | tree | ..
**
** The builder creates widget objects from descriptors that can be accessed by UI.pages.@name.
**
** method (object of info).update (string)
** method (object of tree).update ({})
** method (object of info).update (string)
** method (object of info).setValue (string)
** method (object of input).getValue ()
** method (object of widget).setStyle ({})
**
** type on = { click?:handler|string, onclick?:handler|string,
** check?:handler1, uncheck?:handler1,
** keypress:{key: string | string [], handler: fucntion (@char,@key)] [],
** selected?:handlerd, change?:handler,
** show?:handlerp, hide?: handlerp }
**
** $pageparam = next:string | prev:string | on: on {show,hide} | show:function | hide:function
** $widget = @name : widget
**
** typeof content = {
** pages : { main: { $widget, $widget, .. , $pageparam, $pageparam, .. }, @p2: { @widget, .. }, .. }
** info?: {}
** }
**
** UI creation (w/o builder, programmatically):
** ============================================
**
** var ui = UI.UI({
** pages : number,
** styles : {},
** terminal: '',
** title : string
** });
** ui.pages[index]={
** id$ : ui.<constructor>(options),
** }
** ui.pages[index]["$id"].on(event,handler)
**
** ui.start(main?)
**
** Supported Widget Constructors
** =============================
**
** Button: button({left,right,top,width,height:number,content:string, action:function|navigation string, styles,....}) -> button
** Chat Dialog: chat({left,right,top,width,height:number,prompt:string,label:string,bot:style,user:style, styles,.. }}
** Checkbox: checkbox(left,right,top,width,height:number,content:string, value:boolean, action:function, styles,....})
** Info: info({left,right,top,bottom,width,height:number,multiline:boolean,wrap:boolean,label:string, styles,.. }}
** Input: input({left,right,top,width,height:number,multiline:boolean,wrap:boolean,label:string, styles,..}}
** Label: label({left,right,top,center:boolean, width,height:number,content:string, styles,..}}
** Radiobut: radiobutton(left,right,top,width,height:number,content:string, group:number|string, value:boolean, action:function, styles,....})
** Status: status({left,right,top,bottom,width,height:number,multiline:boolean,wrap:boolean,label:string, styles,..}}
** Text: text({left,right,top,width,height:number,multiline:boolean,wrap:boolean,scrollable:boolean,label:string, styles,..}}
** Terminal: terminal({left,right,top,width,height:number,prompt:string,label:string, styles,..}}
**
** type styles = {fg:string,bg:string, bold:boolean, ..}
** type navigation string = 'prev'|'next'|pageid
**
** type checkbox'event = select:boolean
** type radiobutton'event = select:boolean , check:object
** type chat'event = eval:string
**
** methods chat = { message:function (string), ask:function(string,options,callback), }
** methods input = { setValue:function (string), getValue:function(), }
**
**
** ---
**
** Examples (with template builder, see also ui/app/demo2.js)
**
** UIApp = Require('ui/app/app');
** UI = UIApp.UI({});
** UI.init();
** styles = {}
** content = { pages : {
** main: {
** lab1: {type:'label', center:true, top:1, content:'Menu'},
** but1: {type:'button', left:1, content:'QUIT', bg:'red', onclick: process.exit },
** but2: {type:'button', right:1, content:'SETUP', onclick:'setup' },
** },
** setup: {
** lab1: {type:'label', center:true, top:1, content:'Menu'},
** but1: {type:'button', left:1, content:'<< MENU', onclick:'main' },
** }
** }}
** UI.builder(styles,content)
** UI.start()
**
** ----------------------
**
** Examples (without builder, programmatically):
**
** ui=UIApp.UI({
** pages:7,
** terminal:this.options.terminal||'xterm-color',
** title:'JAMAPP (C) Stefan Bosse'
** });
** ui.init();
** page=ui.pages[1];
** page.b1= ui.button({left:1,content:'QUIT'});
** page.b1.on('press', function(data) {
** return process.exit(0);
** });
** page.l1 = ui.label({center:true,top:1,content:'Menu'});
** page.b2 = ui.button({right:1,content:'SETUP'});
** page.b2.on('press', function(data) {
** ui.pages.hide(1);
** ui.pages.show(2);
** });
** // or
** page.b2.on('press', function(data) {
** ui.pages.hide();
** ui.pages.show('next');
** });
**
** page.l2 = ui.label({center:true,top:1,content:'Setup'});
** page.i1 = ui.input({top:4,left:4,label:'Broker IP Address',value:'localhost'});
** page.i1.setValue('127.0.0.1');
** url=page.i1.getValue();
**
** page.i4 = ui.info({top:16,left:4,width:ui.screen.width-8,label:'JAM Status'});
**
** page.l2 = ui.label({left:4,top:16,content:'Protocol'});
** page.ch21 = ui.radiobutton({left:4,top:18,text:'HTTP',value:false,group:2});
** page.ch22 = ui.radiobutton({left:4,top:20,text:'TCPIP',value:true,group:2});
**
** page.l3 = ui.label({left:4,top:22,content:'Messages'});
** page.ch31 = ui.checkbox({left:4,top:24,text:'Agent ID',value:false});
** page.ch32 = ui.checkbox({left:4,top:26,text:'Parent ID',value:false});
**
** $ENDOFINFO
*/
var Io = Require('com/io');
var Comp = Require('com/compat');
var blessed = Require('term/blessed');
var options = {
version: '1.8.2'
}
/** pre-defined layouts based on screen rows and columns
*
*/
var LAYOUT = {
SMALL:'small',
NORMAL:'normal',
LARGE:'large',
XLARGE:'xlarge',
PORTRAIT:'portrait',
LANDSCAPE:'landscape',
from: function (screen) {
if (screen.width>screen.height) {
if(screen.width<70) return {small:true,landscape:true};
else return {normal:true,landscape:true};
} else {
if(screen.height<70) return {small:true,portrait:true};
else return {normal:true,portrait:true};
}
}
}
/** Main User Interface providing a multiple page view
* Events: pages -> 'load'
* typeof options = {pages,terminal,forceCursor,styles}
*/
function UI(options) {
var self=this,i;
if (!(this instanceof UI)) {
return new UI(options);
}
this.options=options||{};
if (!this.options.pages) this.options.pages=1;
if (!this.options.terminal) {
this.options.terminal=
(process.platform === 'win32' ? 'windows-ansi' : 'xterm-color');
}
if (this.options.forceCursor==undefined) this.options.forceCursor=true;
this.page = 1;
this.pages = []; // Pages
this.static = {}; // Top-level widegts visible on all pages
this.styles=options.styles||{};
for(i=0;i<=this.options.pages;i++) {
this.pages.push({});
}
this.pages.show = function (page) {
var thepage,p,current=self.page;
switch (page) {
case 'prev':
if (self.pages[self.page] && self.pages[self.page].prev) page=self.pages[self.page].prev;
else if (self.pages[self.page-1]) page=self.page-1;
break;
case 'next':
if (self.pages[self.page] && self.pages[self.page].next) page=self.pages[self.page].next;
else if (self.pages[self.page+1]) page=self.page+1;
break;
case 'this':
case undefined:
page=self.page; break;
}
thepage=self.pages[page];
if (self.events[page] && self.events[page]['load'])
self.events[page]['load']();
for (p in thepage) {
if (thepage[p] && thepage[p].show && !thepage[p].noshow) thepage[p].show();
}
self.screen.render();
self.page=page;
};
this.pages.hide = function (page) {
var thepage,p;
if (page=='this' || page==undefined) page=self.page;
thepage=self.pages[page];
for (p in thepage)
if (thepage[p] && thepage[p].hide) thepage[p].hide();
// hide cursor
if (self.options.terminal.indexOf('xterm') != -1 && self.options.forceCursor)
self.screen.program.hideCursor(true);
};
this.events=[];
this.pages.on = function(page,event,callback) {
if (!self.events[page]) self.events[page]=[];
self.events[page][event]=callback;
}
}
/** Button widget
* Methods: on
* Events: 'press'
* typeof @options ={
* width,content,center,left,right,top,
* fg is textcolor,bg is button color,border,
* click:function,
* action:function|string
* }
*/
UI.prototype.button = function(options) {
var self=this,width=options.width;
if (Comp.obj.isString(options.width)) {
// relative value in %!
width=Comp.pervasives.int_of_string(options.width);
width=this.screen.width*width/100;
}
var obj = blessed.button({
width: options.width||(options.content.length+4),
left: (options.center?int(this.screen.width/2-width/2):options.left),
right : options.right,
top: options.top||0,
height: 3,
align: 'center',
content: options.content||'?',
mouse:true,
focus:false,
border: options.border||this.getStyle('button.border',{
type: 'line'
}),
style: {
fg: options.fg||this.getStyle('button.fg','white'),
bg: options.bg||this.getStyle('button.bg','blue'),
bold:true,
border: {
fg: this.getStyle('button.border.fg','black'),
bg: this.getStyle('button.border.bg',undefined),
},
hover: {
border: {
fg: 'red'
}
}
}
});
obj.noshow=options.hidden;
this.screen.append(obj);
// generic handler
if (options.click) obj.on('press',options.click);
// dedicated action handlers with page navigation support
if (options.action) {
switch (typeof options.action) {
case 'function': obj.on('press',options.action); break;
case 'string':
if (options.action=='next' || options.action=='prev') {
obj.on('press',function () {
if (self.pages[self.page][options.action]) {
self.pages.hide('this');
self.pages.show(self.pages[self.page][options.action]);
}
});
} else {
obj.on('press',function () {
if (self.pages[options.action]) {
self.pages.hide('this');
self.pages.show(options.action);
}
});
}
break;
}
}
return obj;
}
/** Chat terminal shell widget
* typeof options = {top,left,right,width,height,label:string,bot:style,user:style,fg:style,bg:style}
*/
UI.prototype.chat = function(options) {
var self=this, width=options.width||(this.screen.width-(options.left*2||2));
options.scrollable=true;
options.scrollbar = {
ch: ' ',
track: {
bg: 'yellow'
},
style: {
fg: 'cyan',
inverse: true
}
};
options.mouse=true;
var obj = blessed.chat({
label: options.label||'My Text',
value: options.value||'',
//fg: 'blue',
bg: 'default',
barBg: 'default',
barFg: 'blue',
width: width,
height: options.height||8,
left: (options.center?int(this.screen.width/2-width/2):options.left),
right : options.right,
top: options.top||0,
keys: true,
vi: false,
mouse: true,
inputOnFocus: true,
tags:true,
focus:true,
wrap : options.wrap,
multiline : true,
scrollbar : options.scrollbar,
scrollable : options.scrollable,
//draggable:true,
prompt:options.prompt,
border: this.getStyle('input.border',{
type: 'line'
}),
style: {
fg: options.fg||this.getStyle('input.fg','blue'),
bg: options.bg||this.getStyle('input.bg',undefined),
user : options.user,
bot : options.bot,
border: {
fg: this.getStyle('input.border.fg','black'),
bg: this.getStyle('input.border.bg',undefined),
},
label:this.getStyle('input.label',undefined),
focus : {
border: {
fg: 'red'
}
}
}
});
obj.noshow=options.hidden;
this.screen.append(obj);
obj.on('focus',function () {
if (!self.options.keyboard) // show cursor
return self.options.terminal.indexOf('xterm')!=-1 && self.options.forceCursor?
self.screen.program._write('\x1b[?12;25h'):0;
if (!self._keyboard)
self._keyboard=self.keyboard({
width:self.screen.width<60?'100%':'80%',
height:self.layout.small?'100%':'90%',
compact:self.layout.small
});
self._keyboard.setCallback(function (v) {if (v) obj.setValue(v),obj.update();});
self._keyboard.setValue(obj.getValue());
self._keyboard.setLabel(obj.getLabel());
self._keyboard.show();
});
return obj;
}
/** Checkbox widget
* Methods: on
* Events: 'check','uncheck'
* typeof @options ={value?,left?,right?,top?,text,hidden?}
*/
UI.prototype.checkbox = function(options) {
var obj = blessed.checkbox({
checked: options.value||false,
left: options.left,
right : options.right,
top: options.top||0,
mouse: true,
inputOnFocus: true,
height: 1,
text:options.text||'empty'
});
obj.noshow=options.hidden;
this.screen.append(obj);
return obj;
}
/** Dialog pop-up window widget
*
* typeof @options = {width,height,center?,left?,right?,top?,okButton?,cancelButton}
*/
UI.prototype.dialog = function(options) {
var width=options.width,height=options.height;
if (Comp.obj.isString(options.width)) {
// relative value in %!
width=Comp.pervasives.int_of_string(options.width);
width=int(this.screen.width*width/100);
}
if (Comp.obj.isString(options.height)) {
// relative value in %!
height=Comp.pervasives.int_of_string(options.height);
height=int(this.screen.height*height/100);
}
var obj = blessed.Question({
width: width,
left: (options.center?int(this.screen.width/2-width/2):options.left),
right : options.right,
top: options.top||(options.center?int(this.screen.height/2-height/2):0),
height: height,
noshow:true,
okButton : options.okButton||'Okay',
cancelButton : options.cancelButton,
style: {
bg:this.getStyle('dialog.bg','red'),
fg:this.getStyle('dialog.fg','white'),
bold:true
}
});
this.screen.append(obj);
return obj;
}
/** File manager widget with buttons
*
* typeof @options={fg?,bg?,parent?,border?,label?,height?,width?,top?,left?,autohide?,
* okayBotton?,cancelButton?,input?,box?,arrows?}
*/
UI.prototype.fileManager = function(options) {
if (options.box) {
options.box.border=this.getStyle('filemanager.box.border',options.box.border);
options.box.bg=this.getStyle('filemanager.box.bg',options.box.bg);
options.input=this.getStyle('filemanager.input',options.input);
}
if (options.arrows) {
options.arrows.fg=this.getStyle('filemanager.arrows.fg',options.arrows.fg);
options.arrows.bg=this.getStyle('filemanager.arrows.bg',options.arrows.bg);
}
var obj = blessed.FileManager({
parent:options.parent,
border:options.border||this.getStyle('filemanager.border',{}),
style: {
fg: options.fg||this.getStyle('filemanager.fg',undefined),
bg: options.bg||this.getStyle('filemanager.bg',undefined),
label:options.label||this.getStyle('filemanager.label',undefined),
selected: {
bg: 'blue',
fg:'white'
},
focus: {
border: {
fg: 'red'
}
}
},
height: options.height||'half',
width: options.width||'half',
top: options.top||'center',
left: options.left||'center',
label: '%path',
cwd: options.cwd||process.env.PWD||process.env.CWD||process.env.HOME,
autohide:options.autohide,
hidden:options.hidden,
noshow:options.hidden, // no show on page load
keys: true,
vi: true,
scrollbar: {
bg: 'white',
ch: ' '
},
okayButton:options.okayButton||'OK',
cancelButton:options.cancelButton||'Cancel',
input:options.input,
arrows:options.arrows,
box:options.box,
border:this.getStyle('filemanager.border',undefined)
});
this.screen.append(obj);
return obj;
}
/** Filter supported widget options, transform special options (click,..). Used by page builder.
*
*/
UI.prototype.filterOptions = function(options,wname) {
var self=this,attr,wopts = {};
for (attr in options) {
switch (attr) {
case 'type':
case 'index':
break;
case 'on':
switch (options.type) {
case 'button' :
if (options.on.click && typeof options.on.click == 'function')
wopts.click=function () { options[attr](wname) };
if (options.on.onclick && typeof options.on.onclick == 'function')
wopts.click=function () { options[attr](wname) };
break;
}
break;
case 'click':
case 'onclick':
switch (options.type) {
case 'button' :
if (typeof options[attr] == 'string') // Its a page destination; show new page
wopts.click=function () {
if (!self.pages[options[attr]]) return;
self.pages.hide('this');
self.pages.show(options[attr]);
};
else
wopts.click=options[attr];
break;
}
break;
default:
wopts[attr]=options[attr];
}
}
return wopts;
}
/** getStyle
*
*/
UI.prototype.getStyle = function(attr,def) {
var path=attr.split('.'),elem,style=this.styles;
while(path.length && style) {
elem=path.shift();
style=style[elem];
}
return style!=undefined?style:def;
}
/** Information message widget
* Methods: setValue
* typeof options = {width,top,left,right, height,label,wrap,multiline,scrollable,color}
*/
UI.prototype.info = function(options) {
var width=options.width;
if (Comp.obj.isString(options.width)) {
// relative value in %!
width=Comp.pervasives.int_of_string(options.width);
width=this.screen.width*width/100;
}
if (options.scrollable && !options.scrollbar) {
options.scrollbar = {
ch: ' ',
track: {
bg: 'yellow'
},
style: {
fg: 'cyan',
inverse: true
}
};
options.mouse=true;
}
var obj = blessed.textbox({
top : options.top||1,
left : (options.center?int(this.screen.width/2-width/2):options.left||(options.right?undefined:1)),
right : options.right,
width : options.width||(this.screen.width-(options.left*2||2)),
height: options.height||3,
label : options.label,
value : options.value||'',
focus : true,
wrap : options.wrap,
multiline : options.multiline,
scrollbar : options.scrollbar,
scrollable : options.scrollable,
mouse : options.mouse,
//draggable:true,
border: this.getStyle('info.border',{
type: 'line'
}),
style: {
fg:options.fg||this.getStyle('info.fg','blue'),
bg: options.bg||this.getStyle('info.bg',undefined),
label:this.getStyle('info.label',undefined),
border: {
fg: this.getStyle('info.border.fg','black'),
bg: this.getStyle('info.border.bg',undefined),
},
}
});
obj.noshow=options.hidden;
this.screen.append(obj);
return obj;
}
/** Initialite APP and create screen
*
*/
UI.prototype.init = function () {
var self=this;
// Information bar visible on all pages
this.screen = blessed.screen({
smartCSR: false,
terminal: this.options.terminal,
forceCursor:this.options.forceCursor,
});
this.screen.title = this.options.title||'APP (C) Stefan Bosse';
this.screen.cursor.color='red';
this.layout=LAYOUT.from(this.screen);
// restore cursor
if (this.options.terminal.indexOf('xterm') != -1 && this.options.forceCursor)
process.on('exit',function () {self.screen.program._write('\x1b[?12;25h')});
}
/** Input field widget
* typeof options = {top,left,right,width,height,label,value}
* method getValue, setValue
* events: {'set content'}
*/
UI.prototype.input = function(options) {
var self=this, width=options.width||(this.screen.width-(options.left*2||2));
var obj = blessed.textbox({
label: options.label||'My Input',
value: options.value||'',
//fg: 'blue',
bg: 'default',
barBg: 'default',
barFg: 'blue',
width: width,
height: options.height||3,
left: (options.center?int(this.screen.width/2-width/2):options.left),
right : options.right,
top: options.top,
bottom: options.bottom,
keys: true,
mouse: true,
inputOnFocus: true,
focus:true,
wrap:options.wrap,
multiline:options.multiline,
//draggable:true,
border: this.getStyle('input.border',{
type: 'line'
}),
style: {
fg: options.fg||this.getStyle('input.fg','blue'),
bg: options.bg||this.getStyle('input.bg',undefined),
border: {
fg: this.getStyle('input.border.fg','black'),
bg: this.getStyle('input.border.bg',undefined),
},
label:this.getStyle('input.label',undefined),
focus : {
border: {
fg: 'red'
}
}
}
});
obj.noshow=options.hidden;
this.screen.append(obj);
obj.on('focus',function () {
if (!self.options.keyboard) // show cursor
return self.options.terminal.indexOf('xterm')!=-1 && self.options.forceCursor?
self.screen.program._write('\x1b[?12;25h'):0;
if (!self._keyboard)
self._keyboard=self.keyboard({
width:self.screen.width<60?'100%':'80%',
height:self.layout.small?'100%':'90%',
compact:self.layout.small
});
self._keyboard.setCallback(function (v) {if (v) obj.setValue(v),obj.update();});
self._keyboard.setValue(obj.getValue());
self._keyboard.setLabel(obj.getLabel());
self._keyboard.show();
});
return obj;
}
/** Software keyboard widget
*
* typeof options = { left,top, width, height, compact, okayButton, cancelButton, border, }
*/
UI.prototype.keyboard = function(options) {
var obj = blessed.keyboard({
parent:options.parent||this.screen,
border: 'line',
height: options.height||'half',
width: options.width||'half',
top: options.top||'center',
left: options.left||'center',
label: 'Keyboard',
hidden:options.hidden,
compact:options.compact,
okayButton:options.okayButton||'OK',
cancelButton:options.cancelButton||(this.layout.small?'CAN':'Cancel'),
delButton:'DEL',
shiftButton:'>>',
border:options.border||this.getStyle('keyboard.border',{}),
style:{
bg: options.bg||this.getStyle('keyboard.bg',undefined),
label:options.label||this.getStyle('keyboard.label',undefined),
}
});
this.screen.append(obj);
return obj;
}
/** Generic label widget
* method setValue(string)|mutable=true
* typeof options = {width?,left?,right?,top?,center?,mutable?,content}
*/
UI.prototype.label = function(options) {
var obj = blessed.text({
width: options.width||(options.content.length),
left: (options.center?int(this.screen.width/2-options.content.length/2):options.left),
right : options.right,
top: options.top||0,
height: options.height||1,
focus:false,
align: 'center',
content: options.content||'?',
style: {
bg:options.style?options.style.bg:this.getStyle('label.bg',undefined),
fg:options.style?options.style.fg:this.getStyle('label.fg',undefined),
bold:this.getStyle('label.bold',false)
}
});
if (options.mutable)
obj.setValue = function (content) {
obj.setContent('');
obj.position.left=(options.center?int(this.screen.width/2-content.length/2):options.left);
obj.setContent(content);
};
obj.noshow=options.hidden;
this.screen.append(obj);
return obj;
}
/** Generic list navigator widget with scrollbar
* typeof options = {top,left,width,height,label}
* method set(data:object|array)
*/
UI.prototype.list = function(options) {
var obj = blessed.list({
top: options.top,
left: options.left,
width: options.width||(this.screen.width-options.left*2),
height: options.height||(this.screen.height-options.top-4),
label: options.label||'Log',
focus:true,
mouse:true,
keys:true,
arrows:options.arrows,
border: this.getStyle('list.border',{
type: 'line'
}),
style: {
bg: options.bg||this.getStyle('list.bg',undefined),
selected:options.selected||{fg:'white',bg:'red',bold:true},
item:options.item||{bold:true},
border: {
fg: this.getStyle('list.border.fg','black')
},
label:this.getStyle('list.label',undefined),
hover: {
border: {
fg: 'red'
}
},
focus : {
border: {
fg: 'red'
}
}
}
});
obj.noshow=options.hidden;
obj.set = obj.update = function (data) {
var p,items=[];
obj.clearItems();
if (Comp.obj.isArray(data)) items=data;
else for (p in data) {
items.push(p);
}
obj.setItems(items);
obj.screen.render();
}
this.screen.append(obj);
return obj;
}
/** Log message widget with scrollbar
* typeof options = {left,top,width,height,label,scrollback,..}
*/
UI.prototype.log = function(options) {
if (options.top == undefined) options.top=2;
if (options.left == undefined && options.right==undefined) options.left=1;
var obj = blessed.log({
top: options.top,
left: options.left,
right: options.right,
width: options.width||(this.screen.width-options.left*2),
height: options.height||(this.screen.height-options.top-4),
label: options.label||'Log',
mouse:true,
keys:true,
scrollback:options.scrollback||100,
border: this.getStyle('log.border',{
type: 'line'
}),
scrollbar: {
ch: ' ',
track: {
bg: 'yellow'
},
style: {
fg: 'cyan',
inverse: true
}
},
alwaysScroll:true,
scrollOnInput:true,
style: {
fg: options.fg||this.getStyle('log.fg','white'),
bg: options.bg||this.getStyle('log.bg','black'),
label:this.getStyle('log.label',undefined),
border: {
fg: this.getStyle('log.border.fg','green'),
bg: this.getStyle('log.border.bg',undefined),
},
focus: {
border: {
fg: 'red'
}
}
},
arrows:options.arrows,
});
obj.noshow=options.hidden;
this.screen.append(obj);
return obj;
}
/** Apply post option actions (event handling)
*
*/
UI.prototype.postOptions = function(options,widget,wname) {
var self=this,attr;
for (attr in options) {
switch (attr) {
case 'on':
switch (options.type) {
case 'checkbox' :
if (options.on.click && typeof options.on.click == 'function')
widget.on('check',function () { options.on.click(wname,true) }),
widget.on('uncheck',function () { options.on.click(wname,false) });
if (options.on.onclick && typeof options.on.onclick == 'function')
widget.on('check',function () { options.on.onclick(wname,true) }),
widget.on('uncheck',function () { options.on.onclick(wname,false) });
if (options.on.check && typeof options.on.check == 'function')
widget.on('check',function () { options.on.check(wname,true) });
if (options.on.uncheck && typeof options.on.uncheck == 'function')
widget.on('uncheck',function () { options.on.uncheck(wname,false) });
if (options.on.selected && typeof options.on.selected == 'function')
widget.on('selected',function (data) { options.on.uncheck(wname,data) });
if (options.on.change && typeof options.on.change == 'function')
widget.on('change',function (data) { options.on.change(wname,data) });
break;
}
break;
case 'click':
case 'onclick':
switch (options.type) {
case 'checkbox' :
if (typeof options[attr] == 'function')
widget.on('check',function () { options[attr](wname,true) }),
widget.on('uncheck',function () { options[attr](wname,false) })
break;
case 'list':
if (typeof options[attr] == 'function')
widget.on('selected',function (data) { options[attr](wname,data.content,data) })
break;
case 'tree':
if (typeof options[attr] == 'function')
widget.on('selected',function (data) {
var _data=data,path=data.name;
data=data.parent;
while(data)
path=data.name+(data.name!='/'?'/':'')+path,
data=data.parent;
options[attr](wname,_data.name,path,_data)
})
break;
}
break;
case 'onchange':
switch (options.type) {
case 'input' :
if (typeof options[attr] == 'function')
widget.on('change',function (data) {
var content = widget.getContent();
options[attr](wname,content)
})
break;
}
break;
}
}
}
/** Radio button widget; can be grouped
*
*/
UI.prototype.radiobutton = function(options) {
var obj = blessed.radiobutton({
checked: options.value||false,
left: options.left,
right : options.right,
top: options.top||0,
group:options.group,
mouse: true,
inputOnFocus: true,
height: 1,
text:options.text||'empty'
});
obj.noshow=options.hidden;
this.screen.append(obj);
return obj;
}
UI.prototype.start = function (main) {
var self=this;
if (main==undefined) main=this.pages.main?'main':null;
Object.keys(this.pages).forEach(function (page) {
if (typeof self.pages[page] != 'object') return;
if (Object.keys(self.pages[page]).length==0) return;
self.pages.hide(page);
if (!main) main=page;
});
if (main) this.pages.show(main);
this.screen.render();
this.screen.program.hideCursor(this.options.terminal.indexOf('xterm') != -1 && this.options.forceCursor);
}
/** Status field widget
* typeof options = {top,bottom,left,right,width,height,label,value}
* method getvalue, setValue
* events: {'set content'}
*/
UI.prototype.status = function(options) {
var self=this, width=options.width||(this.screen.width-(options.left*2||2));
var obj = blessed.textbox({
label: options.label||'My Input',
value: options.value||'',
//fg: 'blue',
bg: 'default',
barBg: 'default',
barFg: 'blue',
width: width,
height: options.height||3,
left: (options.center?int(this.screen.width/2-width/2):options.left),
right : options.right,
top: options.top,
bottom: options.bottom,
wrap:options.wrap,
multiline:options.multiline,
//draggable:true,
border: this.getStyle('input.border',{
type: 'line'
}),
style: {
fg: options.fg||this.getStyle('input.fg','blue'),
bg: options.bg||this.getStyle('input.bg',undefined),
border: {
fg: this.getStyle('input.border.fg','black'),
bg: this.getStyle('input.border.bg',undefined),
},
label:this.getStyle('input.label',undefined),
focus : {
border: {
fg: 'red'
}
}
}
});
obj.noshow=options.hidden;
this.screen.append(obj);
return obj;
}
/** Table widget
* typeof options = {left,right,top,width,height,label,header,cell,data,..}
*/
UI.prototype.table = function(options) {
if (options.top == undefined) options.top=2;
if (options.left == undefined && options.right==undefined) options.left=1;
var obj = blessed.table({
top: options.top,
left: options.left,
right: options.right,
width: options.width||(this.screen.width-options.left*2),
height: options.height||(this.screen.height-options.top-4),
label: options.label||'Table',
data: options.data,
border: this.getStyle('table.border',{
type: 'line'
}),
style: {
fg: options.fg||this.getStyle('log.fg','white'),
bg: options.bg||this.getStyle('log.bg','black'),
label:this.getStyle('log.label',undefined),
border: {
fg: this.getStyle('log.border.fg','green'),
bg: this.getStyle('log.border.bg',undefined),
},
header : options.header,
cell : options.cell,
focus: {
border: {
fg: 'red'
}
}
},
});
obj.noshow=options.hidden;
this.screen.append(obj);
return obj;
}
/** Terminal shell widget
* typeof options = {top,left,right,width,height,label,header:style,cell:style}
*/
UI.prototype.terminal = function(options) {
var self=this, width=options.width||(this.screen.width-(options.left*2||2));
options.scrollable=true;
if (options.scrollable && !options.scrollbar) {
options.scrollbar = {
ch: ' ',
track: {
bg: 'yellow'
},
style: {
fg: 'cyan',
inverse: true
}
};
options.mouse=true;
}
var obj = blessed.terminal({
label: options.label||'My Text',
value: options.value||'',
//fg: 'blue',
bg: 'default',
barBg: 'default',
barFg: 'blue',
width: width,
height: options.height||8,
left: (options.center?int(this.screen.width/2-width/2):options.left),
right : options.right,
top: options.top||0,
keys: true,
vi: false,
mouse: true,
inputOnFocus: true,
focus:true,
wrap : options.wrap,
multiline : true,
scrollbar : options.scrollbar,
scrollable : options.scrollable,
//draggable:true,
prompt:options.prompt,
border: this.getStyle('input.border',{
type: 'line'
}),
style: {
fg: options.fg||this.getStyle('input.fg','blue'),
bg: options.bg||this.getStyle('input.bg',undefined),
border: {
fg: this.getStyle('input.border.fg','black'),
bg: this.getStyle('input.border.bg',undefined),
},
label:this.getStyle('input.label',undefined),
focus : {
border: {
fg: 'red'
}
}
}
});
obj.noshow=options.hidden;
this.screen.append(obj);
obj.on('focus',function () {
if (!self.options.keyboard) // show cursor
return self.options.terminal.indexOf('xterm')!=-1 && self.options.forceCursor?
self.screen.program._write('\x1b[?12;25h'):0;
if (!self._keyboard)
self._keyboard=self.keyboard({
width:self.screen.width<60?'100%':'80%',
height:self.layout.small?'100%':'90%',
compact:self.layout.small
});
self._keyboard.setCallback(function (v) {if (v) obj.setValue(v),obj.update();});
self._keyboard.setValue(obj.getValue());
self._keyboard.setLabel(obj.getLabel());
self._keyboard.show();
});
return obj;
}
/** Text field widget
* typeof options = {top,left,right,width,height,label,value}
* method getValue, setValue
* events: {'set content'}
*/
UI.prototype.text = function(options) {
var self=this, width=options.width||(this.screen.width-(options.left*2||2));
if (options.scrollable && !options.scrollbar) {
options.scrollbar = {
ch: ' ',
track: {
bg: 'yellow'
},
style: {
fg: 'cyan',
inverse: true
}
};
options.mouse=true;
}
var obj = blessed.textarea({
label: options.label||'My Text',
value: options.value||'',
//fg: 'blue',
bg: 'default',
barBg: 'default',
barFg: 'blue',
width: width,
height: options.height||8,
left: (options.center?int(this.screen.width/2-width/2):options.left),
right : options.right,
top: options.top||0,
keys: true,
vi: false,
mouse: true,
inputOnFocus: true,
focus:true,
wrap : options.wrap,
multiline : true,
scrollbar : options.scrollbar,
scrollable : options.scrollable,
//draggable:true,
border: this.getStyle('input.border',{
type: 'line'
}),
style: {
fg: options.fg||this.getStyle('input.fg','blue'),
bg: options.bg||this.getStyle('input.bg',undefined),
border: {
fg: this.getStyle('input.border.fg','black'),
bg: this.getStyle('input.border.bg',undefined),
},
label:this.getStyle('input.label',undefined),
focus : {
border: {
fg: 'red'
}
}
}
});
obj.noshow=options.hidden;
this.screen.append(obj);
obj.on('focus',function () {
if (!self.options.keyboard) // show cursor
return self.options.terminal.indexOf('xterm')!=-1 && self.options.forceCursor?
self.screen.program._write('\x1b[?12;25h'):0;
if (!self._keyboard)
self._keyboard=self.keyboard({
width:self.screen.width<60?'100%':'80%',
height:self.layout.small?'100%':'90%',
compact:self.layout.small
});
self._keyboard.setCallback(function (v) {if (v) obj.setValue(v),obj.update();});
self._keyboard.setValue(obj.getValue());
self._keyboard.setLabel(obj.getLabel());
self._keyboard.show();
});
return obj;
}
/** Generic data object tree navigator widget with scrollbar
* typeof options = {top,left,width,height,label,depth}
* method set(dats)/update(data)
*
* Data object can contain _update attributes (function) modifying the data content of elements
* before opening a tree branch. Root data _update must call self.update(new data)!
* Deeper _update functions have only to modify the object data passed as an argument.
* Scalar tree leafes can be updated before opening branch by a virtual object:
* {_virtual:string|number|boolean,_update:function (data) {data._value=<newval>}}
*/
UI.prototype.tree = function(options) {
var obj = blessed.tree({
top: options.top,
left: options.left,
width: options.width||(this.screen.width-options.left*2),
height: options.height||(this.screen.height-options.top-4),
label: options.label||'Log',
focus:true,
arrows:options.arrows,
border: this.getStyle('tree.border',{
type: 'line'
}),
style: {
bold:true,
border: {
fg: this.getStyle('tree.border.fg','black')
},
label:this.getStyle('tree.label',undefined),
hover: {
border: {
fg: 'red'
}
},
focus : {
border: {
fg: 'red'
}
}
}
});
function makeleaf (element,reference,data) {
var content,children,name,funpat,isfun,p;
children={};
name = element.toString();
funpat = /function[\s0-9a-zA-Z_$]*\(/i;
isfun=Comp.obj.isFunction(element)||funpat.test(name);
if (isfun) {
element=Comp.string.sub(name,0,name.indexOf('{'));
}
if (!isfun || (isfun && options.showfun)) {
children[element]={};
content={children : children,reference:reference,data:data};
}
return content;
}
function maketree (element,reference) {
var content,children,p;
children={};
if (element && (Comp.obj.isObject(element) || Comp.obj.isArray(element))) {
// console.log(element)
if (element._update != undefined) element._update(element);
if (element._value != undefined) return makeleaf(element._value,_,element);
for (p in element) {
if (p != '_update')
children[p]={};
}
content={
children : children,
data : element
}
} else if (element != undefined) {
return makeleaf(element,reference);
} else {
children[element]={};
content={children : children};
}
return content;
};
obj.noshow=options.hidden;
// Create sub-trees
obj.on('preselect',function(node){
var content,children,element,data,name;
if (node.name != '/' && !node.extended) {
// Io.out(node.extended);
data = node.data;
if (data != none && (Comp.obj.isObject(data) || Comp.obj.isArray(data))) {
node.children = {};
if (Comp.obj.isArray(data) && Comp.array.empty(data) && Comp.hashtbl.empty(data)) {
node.children={'[]' : {}};
} else {
if (data._update != undefined) data._update(data);
if (data._value != undefined) return node.children=makeleaf(data._value,_,data).children;
for (var p in data) {
if (p != '_update') {
element = data[p];
content=maketree(element,data);
if (content) node.children[p]=content;
}
}
}
} else if (data == none && node.reference) {
node.children = {};
element=node.reference[node.name];
name=element.toString();
var funpat = /function[\s0-9a-zA-Z_$]*\(/i;
var isfun=Comp.obj.isFunction(element)||funpat.test(name);
if (isfun) {
element=Comp.string.sub(name,0,name.indexOf('{'));
}
node.children[element]={};
}
} else if (node.name == '/' && node.extended) {
if (node.data && node.data._update) {
node.data._update()
}
}
});
obj.set = obj.update = function (data) {
obj.DATA = {
name:'/',
extended:true,
children: {},
data:data,
};
for (var p in data) {
var element=data[p];
var content=options.depth && options.depth==1?{}:maketree(element,data);
if (content) obj.DATA.children[p]=content;
}
obj.setData(obj.DATA);
};
obj.DATA = {
name:'/',
extended:true,
children: {},
};
obj.setData(obj.DATA);
this.screen.append(obj);
return obj;
}
/** Build an application GUI from compact styles and pages template.
*
* @typeof styles = {
* button?: { .. },
* input?: {..},
* tree?: { .. },
* ..,
* @customstyle : { .. },
* ..
* }
* @typeof content = {
* pages : { @p1: { .. }, @p2: { .. }, .. }
* static?: { w1: { .. }, .. }
* }
*
*
*
*
*/
UI.prototype.builder = function(styles,content,options) {
var self=this,pagename,page,pagedesc,entryname,entry,handler,o,childs,index;
// Update styles
this.styles=Comp.obj.inherit(this.styles,styles);
if (!content.pages) throw Error("UI.builder: empty or invalid pages template (missing pages)");
if (!content.pages.main) throw Error("UI.builder: no main page");
function addhandler(obj,event,handler,arg1,arg2) { obj.on(event,function () { handler(arg1,arg2) })}
function addhandlerP(page,event,handler,arg1,arg2) { self.pages.on(page,event,function () { handler(arg1,arg2) })}
for (pagename in content.pages) {
pagedesc=content.pages[pagename];
this.pages[pagename]=page = {};
for (entryname in pagedesc) {
entry=pagedesc[entryname];
// Information and control entries?
switch (entryname) {
case 'next':
page.next=entry;
continue;
break;
case 'prev':
page.prev=entry;
continue;
break;
case 'on':
if (pagedesc.on.show) addhandlerP(pagename,'load',pagedesc.on.show,pagename);
if (pagedesc.on.load) addhandlerP(pagename,'load',pagedesc.on.load,pagename);
continue;
break;
case 'onclick':
continue;
break;
}
// Supported widget class?
switch (entry.type) {
case 'label':
page[entryname]=this.label(this.filterOptions(entry,entryname));
break;
case 'button':
page[entryname]=this.button(this.filterOptions(entry,entryname));
break;
case 'input':
page[entryname]=this.input(this.filterOptions(entry,entryname));
this.postOptions(entry,page[entryname],entryname);
break;
case 'info':
page[entryname]=this.info(this.filterOptions(entry,entryname));
break;
case 'tree':
page[entryname]=this.tree(this.filterOptions(entry,entryname));
this.postOptions(entry,page[entryname],entryname);
break;
case 'list':
page[entryname]=this.list(this.filterOptions(entry,entryname));
this.postOptions(entry,page[entryname],entryname);
break;
case 'checkbox':
page[entryname]=this.checkbox(this.filterOptions(entry,entryname));
this.postOptions(entry,page[entryname],entryname);
break;
case 'radiobutton':
break;
case 'log':
page[entryname]=this.log(this.filterOptions(entry,entryname));
this.postOptions(entry,page[entryname],entryname);
break;
case 'group':
// Group of radiobuttons
childs={};
handler=undefined;
for (o in entry) {
switch (o) {
case 'type':
break;
case 'onclick':
handler=entry[o];
break;
default:
if (entry[o].type=='radiobutton') childs[o]=entry[o];
}
}
for (o in childs) {
options=this.filterOptions(childs[o],entryname);
options.group=entryname;
page[entryname+'.'+o]=this.radiobutton(options);
if (handler) {
if (childs[o].index != undefined) index=childs[o].index;
else index=childs[o].text;
addhandler(page[entryname+'.'+o],'check',handler,entryname,index);
}
}
break;
default: throw Error("UI.builder: invalid or unsupported widget type "+entry.type+" in "+entryname);
}
}
}
if (content.static) {
for(entryname in content.static) {
entry=content.static[entryname];
switch (entry.type) {
case 'info':
this.static[entryname]=this.info(this.filterOptions(entry,entryname));
break;
}
}
}
if (content.on && content.on.keypress && Comp.obj.isArray(content.on.keypress)) {
content.on.keypress.forEach(function (entry) {
if (typeof entry.key == 'string')
self.screen.key([entry.key], entry.handler);
else if (entry.key && Comp.obj.isArray(entry.key))
self.screen.key(entry.key, entry.handler);
});
}
for (page in this.pages) this.pages.hide(page);
this.pages.show('main');
}
module.exports = {
options:options,
LAYOUT:LAYOUT,
UI:UI
}
};
BundleModuleCode['term/blessed']=function (module,exports){
/**
* blessed - a high-level terminal interface library for node.js
* Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
* https://github.com/chjj/blessed
*/
/**
* Blessed
*/
function blessed() {
return blessed.program.apply(null, arguments);
}
blessed.program = blessed.Program = Require('term/program');
blessed.tput = blessed.Tput = Require('term/tput');
blessed.widget = Require('term/widget');
blessed.colors = Require('term/colors');
blessed.unicode = Require('term/unicode');
blessed.helpers = Require('term/helpers');
blessed.helpers.sprintf = blessed.tput.sprintf;
blessed.helpers.tryRead = blessed.tput.tryRead;
blessed.helpers.merge(blessed, blessed.helpers);
blessed.helpers.merge(blessed, blessed.widget);
/**
* Expose
*/
module.exports = blessed;
};
BundleModuleCode['term/program']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2016, Christopher Jeffrey and contributors
** $CREATED: sbosse on 28-3-15.
** $VERSION: 1.9.3
**
** $INFO:
*
* program.js - basic curses-like functionality for blessed.
*
** $ENDOFINFO
*/
var options = {
version:'1.9.3'
}
/**
* Modules
*/
var Comp = Require('com/compat');
var EventEmitter = Require('events').EventEmitter
, StringDecoder = Require('string_decoder').StringDecoder
, cp = Require('child_process')
, util = Require('util')
, fs = Require('fs');
var Tput = Require('term/tput')
, colors = Require('term/colors')
, slice = Array.prototype.slice;
var nextTick = global.setImmediate || process.nextTick.bind(process);
/**
* Program
*/
function Program(options) {
var self = this;
if (!instanceOf(this,Program)) {
return new Program(options);
}
Program.bind(this);
EventEmitter.call(this);
//if (!options || options.__proto__ !== Object.prototype) {
if (!options) {
options = {
input: arguments[0],
output: arguments[1]
};
}
this.options = options;
this.input = options.input || process.stdin;
this.output = options.output || process.stdout;
options.log = options.log || options.dump;
if (options.log) {
this._logger = fs.createWriteStream(options.log);
if (options.dump) this.setupDump();
}
this.zero = options.zero !== false;
this.useBuffer = options.buffer;
this.x = 0;
this.y = 0;
this.savedX = 0;
this.savedY = 0;
this.cols = this.output.columns || 80;
this.rows = this.output.rows || 20;
console.log('Terminal: '+this.cols + ' x ' + this.rows);
this.scrollTop = 0;
this.scrollBottom = this.rows - 1;
this._terminal = options.terminal
|| options.term
|| process.env.TERM
|| (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
this._terminal = this._terminal.toLowerCase();
// OSX
this.isOSXTerm = process.env.TERM_PROGRAM === 'Apple_Terminal';
this.isiTerm2 = process.env.TERM_PROGRAM === 'iTerm.app'
|| !!process.env.ITERM_SESSION_ID;
// VTE
// NOTE: lxterminal does not provide an env variable to check for.
// NOTE: gnome-terminal and sakura use a later version of VTE
// which provides VTE_VERSION as well as supports SGR events.
this.isXFCE = /xfce/i.test(process.env.COLORTERM);
this.isTerminator = !!process.env.TERMINATOR_UUID;
this.isLXDE = false;
this.isVTE = !!process.env.VTE_VERSION
|| this.isXFCE
|| this.isTerminator
|| this.isLXDE;
// xterm and rxvt - not accurate
this.isRxvt = /rxvt/i.test(process.env.COLORTERM);
this.isXterm = false;
this.tmux = !!process.env.TMUX;
this.tmuxVersion = (function() {
if (!self.tmux) return 2;
try {
var version = cp.execFileSync('tmux', ['-V'], { encoding: 'utf8' });
return +/^tmux ([\d.]+)/i.exec(version.trim().split('\n')[0])[1];
} catch (e) {
return 2;
}
})();
this._buf = '';
this._flush = this.flush.bind(this);
if (options.tput !== false) {
this.setupTput();
}
// console.log(Require('com/io').write_file('/tmp/LOG',Require('os/inspect')(this)));
this.listen();
if (process.platform == 'win32') {
process.winmouse.init(function (x,y,button,action) {
var key = {
name: 'mouse',
ctrl: false,
meta: false,
shift: false,
action: action,
x:x,
y:y
};
self.emit('mouse',key);
});
}
}
Program.global = null;
Program.total = 0;
Program.instances = [];
Program.bind = function(program) {
if (!Program.global) {
Program.global = program;
}
if (!~Program.instances.indexOf(program)) {
Program.instances.push(program);
program.index = Program.total;
Program.total++;
}
if (Program._bound) return;
Program._bound = true;
unshiftEvent(process, 'exit', Program._exitHandler = function() {
Program.instances.forEach(function(program) {
// Potentially reset window title on exit:
if (program._originalTitle) {
program.setTitle(program._originalTitle);
}
// Ensure the buffer is flushed (it should
// always be at this point, but who knows).
program.flush();
// Ensure _exiting is set (could technically
// use process._exiting).
program._exiting = true;
});
});
};
//Program.prototype.__proto__ = EventEmitter.prototype;
inheritPrototype(Program,EventEmitter);
Program.prototype.type = 'program';
Program.prototype.log = function() {
return this._log('LOG', util.format.apply(util, arguments));
};
Program.prototype.debug = function() {
if (!this.options.debug) return;
return this._log('DEBUG', util.format.apply(util, arguments));
};
Program.prototype._log = function(pre, msg) {
if (!this._logger) return;
return this._logger.write(pre + ': ' + msg + '\n-\n');
};
Program.prototype.setupDump = function() {
var self = this
, write = this.output.write
, decoder = new StringDecoder('utf8');
function stringify(data) {
return caret(data
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t'))
.replace(/[^ -~]/g, function(ch) {
if (ch.charCodeAt(0) > 0xff) return ch;
ch = ch.charCodeAt(0).toString(16);
if (ch.length > 2) {
if (ch.length < 4) ch = '0' + ch;
return '\\u' + ch;
}
if (ch.length < 2) ch = '0' + ch;
return '\\x' + ch;
});
}
function caret(data) {
return data.replace(/[\0\x80\x1b-\x1f\x7f\x01-\x1a]/g, function(ch) {
switch (ch) {
case '\0':
case '\200':
ch = '@';
break;
case '\x1b':
ch = '[';
break;
case '\x1c':
ch = '\\';
break;
case '\x1d':
ch = ']';
break;
case '\x1e':
ch = '^';
break;
case '\x1f':
ch = '_';
break;
case '\x7f':
ch = '?';
break;
default:
ch = ch.charCodeAt(0);
// From ('A' - 64) to ('Z' - 64).
if (ch >= 1 && ch <= 26) {
ch = String.fromCharCode(ch + 64);
} else {
return String.fromCharCode(ch);
}
break;
}
return '^' + ch;
});
}
this.input.on('data', function(data) {
self._log('IN', stringify(decoder.write(data)));
});
this.output.write = function(data) {
self._log('OUT', stringify(data));
return write.apply(this, arguments);
};
};
Program.prototype.setupTput = function() {
if (this._tputSetup) return;
this._tputSetup = true;
var self = this
, options = this.options
, write = this._write.bind(this);
var tput = this.tput = new Tput({
terminal: this.terminal,
padding: options.padding,
extended: options.extended,
printf: options.printf,
termcap: options.termcap,
forceUnicode: options.forceUnicode
});
if (tput.error) {
nextTick(function() {
self.emit('warning', tput.error.message);
});
}
if (tput.padding) {
nextTick(function() {
self.emit('warning', 'Terminfo padding has been enabled.');
});
}
this.put = function() {
var args = slice.call(arguments)
, cap = args.shift();
if (tput[cap]) {
return this._write(tput[cap].apply(tput, args));
}
};
Object.keys(tput).forEach(function(key) {
if (self[key] == null) {
self[key] = tput[key];
}
if (typeof tput[key] !== 'function') {
self.put[key] = tput[key];
return;
}
if (tput.padding) {
self.put[key] = function() {
return tput._print(tput[key].apply(tput, arguments), write);
};
} else {
self.put[key] = function() {
return self._write(tput[key].apply(tput, arguments));
};
}
});
};
/* Depricated:
Program.prototype.__defineGetter__('terminal', function() {
return this._terminal;
});
Program.prototype.__defineSetter__('terminal', function(terminal) {
this.setTerminal(terminal);
return this.terminal;
});
*/
Object.defineProperty(Program.prototype,'terminal', {
get: function () {return this._terminal;},
set: function (terminal) {
this.setTerminal(terminal);
return this.terminal;
}
});
Program.prototype.setTerminal = function(terminal) {
this._terminal = terminal.toLowerCase();
delete this._tputSetup;
this.setupTput();
};
Program.prototype.has = function(name) {
return this.tput
? this.tput.has(name)
: false;
};
Program.prototype.term = function(is) {
return this.terminal.indexOf(is) === 0;
};
Program.prototype.listen = function() {
var self = this;
// Potentially reset window title on exit:
// if (!this.isRxvt) {
// if (!this.isVTE) this.setTitleModeFeature(3);
// this.manipulateWindow(21, function(err, data) {
// if (err) return;
// self._originalTitle = data.text;
// });
// }
// Listen for keys/mouse on input
if (!this.input._blessedInput) {
this.input._blessedInput = 1;
this._listenInput();
} else {
this.input._blessedInput++;
}
this.on('newListener', this._newHandler = function fn(type) {
if (type === 'keypress' || type === 'mouse') {
self.removeListener('newListener', fn);
if (self.input.setRawMode && !self.input.isRaw) {
self.input.setRawMode(true);
self.input.resume();
}
}
});
this.on('newListener', function fn(type) {
if (type === 'mouse') {
self.removeListener('newListener', fn);
self.bindMouse();
}
});
// Listen for resize on output
if (!this.output._blessedOutput) {
this.output._blessedOutput = 1;
this._listenOutput();
} else {
this.output._blessedOutput++;
}
};
var _keys=Require('term/keys');
Program.prototype._listenInput = function() {
var keys = _keys,
self = this;
// Input
this.input.on('keypress', this.input._keypressHandler = function(ch, key) {
key = key || { ch: ch };
if (key.name === 'undefined'
&& (key.code === '[M' || key.code === '[I' || key.code === '[O')) {
// A mouse sequence. The `keys` module doesn't understand these.
return;
}
if (key.name === 'undefined') {
// Not sure what this is, but we should probably ignore it.
return;
}
if (key.name === 'enter' && key.sequence === '\n') {
key.name = 'linefeed';
}
if (key.name === 'return' && key.sequence === '\r') {
self.input.emit('keypress', ch, merge({}, key, { name: 'enter' }));
}
var name = (key.ctrl ? 'C-' : '')
+ (key.meta ? 'M-' : '')
+ (key.shift && key.name ? 'S-' : '')
+ (key.name || ch);
key.full = name;
Program.instances.forEach(function(program) {
if (program.input !== self.input) return;
program.emit('keypress', ch, key);
program.emit('key ' + name, ch, key);
});
});
this.input.on('data', this.input._dataHandler = function(data) {
Program.instances.forEach(function(program) {
if (program.input !== self.input) return;
program.emit('data', data);
});
});
keys.emitKeypressEvents(this.input);
};
Program.prototype._listenOutput = function() {
var self = this;
if (!this.output.isTTY) {
nextTick(function() {
self.emit('warning', 'Output is not a TTY');
});
}
// Output
function resize() {
Program.instances.forEach(function(program) {
if (program.output !== self.output) return;
program.cols = program.output.columns;
program.rows = program.output.rows;
program.emit('resize');
});
}
this.output.on('resize', this.output._resizeHandler = function() {
Program.instances.forEach(function(program) {
if (program.output !== self.output) return;
if (!program.options.resizeTimeout) {
return resize();
}
if (program._resizeTimer) {
clearTimeout(program._resizeTimer);
delete program._resizeTimer;
}
var time = typeof program.options.resizeTimeout === 'number'
? program.options.resizeTimeout
: 300;
program._resizeTimer = setTimeout(resize, time);
});
});
};
Program.prototype.destroy = function() {
var index = Program.instances.indexOf(this);
if (~index) {
Program.instances.splice(index, 1);
Program.total--;
this.flush();
this._exiting = true;
Program.global = Program.instances[0];
if (Program.total === 0) {
Program.global = null;
process.removeListener('exit', Program._exitHandler);
delete Program._exitHandler;
delete Program._bound;
}
this.input._blessedInput--;
this.output._blessedOutput--;
if (this.input._blessedInput === 0) {
this.input.removeListener('keypress', this.input._keypressHandler);
this.input.removeListener('data', this.input._dataHandler);
delete this.input._keypressHandler;
delete this.input._dataHandler;
if (this.input.setRawMode) {
if (this.input.isRaw) {
this.input.setRawMode(false);
}
if (!this.input.destroyed) {
this.input.pause();
}
}
}
if (this.output._blessedOutput === 0) {
this.output.removeListener('resize', this.output._resizeHandler);
delete this.output._resizeHandler;
}
this.removeListener('newListener', this._newHandler);
delete this._newHandler;
this.destroyed = true;
this.emit('destroy');
}
};
Program.prototype.key = function(key, listener) {
if (typeof key === 'string') key = key.split(/\s*,\s*/);
key.forEach(function(key) {
return this.on('key ' + key, listener);
}, this);
};
Program.prototype.onceKey = function(key, listener) {
if (typeof key === 'string') key = key.split(/\s*,\s*/);
key.forEach(function(key) {
return this.once('key ' + key, listener);
}, this);
};
Program.prototype.unkey =
Program.prototype.removeKey = function(key, listener) {
if (typeof key === 'string') key = key.split(/\s*,\s*/);
key.forEach(function(key) {
return this.removeListener('key ' + key, listener);
}, this);
};
// XTerm mouse events
// http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
// To better understand these
// the xterm code is very helpful:
// Relevant files:
// button.c, charproc.c, misc.c
// Relevant functions in xterm/button.c:
// BtnCode, EmitButtonCode, EditorButton, SendMousePosition
// send a mouse event:
// regular/utf8: ^[[M Cb Cx Cy
// urxvt: ^[[ Cb ; Cx ; Cy M
// sgr: ^[[ Cb ; Cx ; Cy M/m
// vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
// locator: CSI P e ; P b ; P r ; P c ; P p & w
// motion example of a left click:
// ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
// mouseup, mousedown, mousewheel
// left click: ^[[M 3<^[[M#3<
// mousewheel up: ^[[M`3>
Program.prototype.bindMouse = function() {
if (this._boundMouse) return;
this._boundMouse = true;
var decoder = new StringDecoder('utf8')
, self = this;
this.on('data', function(data) {
var text,seq=[data],_data,i,n=0,m=0;
if (data.length && data.indexOf(0x1b,1) > 0) {
// Split data
seq=[];
while(n<data.length) {
m=data.indexOf(0x1b,m+1);
if (m==-1) m=data.length;
// console.log(n,m);
seq.push(data.slice(n,m));
n=m;
}
}
for(i in seq) {
_data=seq[i];
text = decoder.write(_data);
if (!text) continue;
// console.log(_data);
self._bindMouse(text, _data);
}
});
};
Program.prototype._bindMouse = function(s, buf) {
var self = this
, key
, parts
, b
, x
, y
, mod
, params
, down
, page
, button;
key = {
name: undefined,
ctrl: false,
meta: false,
shift: false
};
if (Buffer.isBuffer(s)) {
if (s[0] > 127 && s[1] === undefined) {
s[0] -= 128;
s = '\x1b' + s.toString('utf-8');
} else {
s = s.toString('utf-8');
}
}
// if (this.8bit) {
// s = s.replace(/\233/g, '\x1b[');
// buf = new Buffer(s, 'utf8');
// }
// XTerm / X10 for buggy VTE
// VTE can only send unsigned chars and no unicode for coords. This limits
// them to 0xff. However, normally the x10 protocol does not allow a byte
// under 0x20, but since VTE can have the bytes overflow, we can consider
// bytes below 0x20 to be up to 0xff + 0x20. This gives a limit of 287. Since
// characters ranging from 223 to 248 confuse javascript's utf parser, we
// need to parse the raw binary. We can detect whether the terminal is using
// a bugged VTE version by examining the coordinates and seeing whether they
// are a value they would never otherwise be with a properly implemented x10
// protocol. This method of detecting VTE is only 99% reliable because we
// can't check if the coords are 0x00 (255) since that is a valid x10 coord
// technically.
var bx = s.charCodeAt(4);
var by = s.charCodeAt(5);
if (buf[0] === 0x1b && buf[1] === 0x5b && buf[2] === 0x4d
&& (this.isVTE
|| bx >= 65533 || by >= 65533
|| (bx > 0x00 && bx < 0x20)
|| (by > 0x00 && by < 0x20)
|| (buf[4] > 223 && buf[4] < 248 && buf.length === 6)
|| (buf[5] > 223 && buf[5] < 248 && buf.length === 6))) {
b = buf[3];
x = buf[4];
y = buf[5];
// unsigned char overflow.
if (x < 0x20) x += 0xff;
if (y < 0x20) y += 0xff;
// Convert the coordinates into a
// properly formatted x10 utf8 sequence.
s = '\x1b[M'
+ String.fromCharCode(b)
+ String.fromCharCode(x)
+ String.fromCharCode(y);
}
// XTerm / X10
if (parts = /^\x1b\[M([\x00\u0020-\ufffe]{3})/.exec(s)) {
b = parts[1].charCodeAt(0);
x = parts[1].charCodeAt(1);
y = parts[1].charCodeAt(2);
key.name = 'mouse';
key.type = 'X10';
key.raw = [b, x, y, parts[0]];
key.buf = buf;
key.x = x - 32;
key.y = y - 32;
if (this.zero) key.x--, key.y--;
if (x === 0) key.x = 255;
if (y === 0) key.y = 255;
mod = b >> 2;
key.shift = !!(mod & 1);
key.meta = !!((mod >> 1) & 1);
key.ctrl = !!((mod >> 2) & 1);
b -= 32;
if ((b >> 6) & 1) {
key.action = b & 1 ? 'wheeldown' : 'wheelup';
key.button = 'middle';
} else if (b === 3 && this._lastButton) {
// NOTE: x10 and urxvt have no way
// of telling which button mouseup used.
// Emit mouseup only if there was a mousedown
key.action = 'mouseup';
key.button = this._lastButton;
delete this._lastButton;
} else if ((b&3) < 3) {
// Emit mousedown only with valid button
key.action = 'mousedown';
button = b & 3;
key.button =
button === 0 ? 'left'
: button === 1 ? 'middle'
: button === 2 ? 'right'
: 'unknown';
this._lastButton = key.button;
}
//console.log(b,key)
// Probably a movement.
// The *newer* VTE gets mouse movements comepletely wrong.
// This presents a problem: older versions of VTE that get it right might
// be confused by the second conditional in the if statement.
// NOTE: Possibly just switch back to the if statement below.
// none, shift, ctrl, alt
// gnome: 32, 36, 48, 40
// xterm: 35, _, 51, _
// urxvt: 35, _, _, _
// if (key.action === 'mousedown' && key.button === 'unknown') {
if (b === 35 || b === 39 || b === 51 || b === 43
|| (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
delete key.button;
key.action = 'mousemove';
}
self.emit('mouse', key);
return;
}
// URxvt
if (parts = /^\x1b\[(\d+;\d+;\d+)M/.exec(s)) {
params = parts[1].split(';');
b = +params[0];
x = +params[1];
y = +params[2];
key.name = 'mouse';
key.type = 'urxvt';
key.raw = [b, x, y, parts[0]];
key.buf = buf;
key.x = x;
key.y = y;
if (this.zero) key.x--, key.y--;
mod = b >> 2;
key.shift = !!(mod & 1);
key.meta = !!((mod >> 1) & 1);
key.ctrl = !!((mod >> 2) & 1);
// XXX Bug in urxvt after wheelup/down on mousemove
// NOTE: This may be different than 128/129 depending
// on mod keys.
if (b === 128 || b === 129) {
b = 67;
}
b -= 32;
if ((b >> 6) & 1) {
key.action = b & 1 ? 'wheeldown' : 'wheelup';
key.button = 'middle';
} else if (b === 3) {
// NOTE: x10 and urxvt have no way
// of telling which button mouseup used.
key.action = 'mouseup';
key.button = this._lastButton || 'unknown';
delete this._lastButton;
} else {
key.action = 'mousedown';
button = b & 3;
key.button =
button === 0 ? 'left'
: button === 1 ? 'middle'
: button === 2 ? 'right'
: 'unknown';
// NOTE: 0/32 = mousemove, 32/64 = mousemove with left down
// if ((b >> 1) === 32)
this._lastButton = key.button;
}
// Probably a movement.
// The *newer* VTE gets mouse movements comepletely wrong.
// This presents a problem: older versions of VTE that get it right might
// be confused by the second conditional in the if statement.
// NOTE: Possibly just switch back to the if statement below.
// none, shift, ctrl, alt
// urxvt: 35, _, _, _
// gnome: 32, 36, 48, 40
// if (key.action === 'mousedown' && key.button === 'unknown') {
if (b === 35 || b === 39 || b === 51 || b === 43
|| (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
delete key.button;
key.action = 'mousemove';
}
self.emit('mouse', key);
return;
}
// SGR
if (parts = /^\x1b\[<(\d+;\d+;\d+)([mM])/.exec(s)) {
down = parts[2] === 'M';
params = parts[1].split(';');
b = +params[0];
x = +params[1];
y = +params[2];
key.name = 'mouse';
key.type = 'sgr';
key.raw = [b, x, y, parts[0]];
key.buf = buf;
key.x = x;
key.y = y;
if (this.zero) key.x--, key.y--;
mod = b >> 2;
key.shift = !!(mod & 1);
key.meta = !!((mod >> 1) & 1);
key.ctrl = !!((mod >> 2) & 1);
if ((b >> 6) & 1) {
key.action = b & 1 ? 'wheeldown' : 'wheelup';
key.button = 'middle';
} else {
key.action = down
? 'mousedown'
: 'mouseup';
button = b & 3;
key.button =
button === 0 ? 'left'
: button === 1 ? 'middle'
: button === 2 ? 'right'
: 'unknown';
}
// Probably a movement.
// The *newer* VTE gets mouse movements comepletely wrong.
// This presents a problem: older versions of VTE that get it right might
// be confused by the second conditional in the if statement.
// NOTE: Possibly just switch back to the if statement below.
// none, shift, ctrl, alt
// xterm: 35, _, 51, _
// gnome: 32, 36, 48, 40
// if (key.action === 'mousedown' && key.button === 'unknown') {
if (b === 35 || b === 39 || b === 51 || b === 43
|| (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
delete key.button;
key.action = 'mousemove';
}
self.emit('mouse', key);
return;
}
// DEC
// The xterm mouse documentation says there is a
// `<` prefix, the DECRQLP says there is no prefix.
if (parts = /^\x1b\[<(\d+;\d+;\d+;\d+)&w/.exec(s)) {
params = parts[1].split(';');
b = +params[0];
x = +params[1];
y = +params[2];
page = +params[3];
key.name = 'mouse';
key.type = 'dec';
key.raw = [b, x, y, parts[0]];
key.buf = buf;
key.x = x;
key.y = y;
key.page = page;
if (this.zero) key.x--, key.y--;
key.action = b === 3
? 'mouseup'
: 'mousedown';
key.button =
b === 2 ? 'left'
: b === 4 ? 'middle'
: b === 6 ? 'right'
: 'unknown';
self.emit('mouse', key);
return;
}
// vt300
if (parts = /^\x1b\[24([0135])~\[(\d+),(\d+)\]\r/.exec(s)) {
b = +parts[1];
x = +parts[2];
y = +parts[3];
key.name = 'mouse';
key.type = 'vt300';
key.raw = [b, x, y, parts[0]];
key.buf = buf;
key.x = x;
key.y = y;
if (this.zero) key.x--, key.y--;
key.action = 'mousedown';
key.button =
b === 1 ? 'left'
: b === 2 ? 'middle'
: b === 5 ? 'right'
: 'unknown';
self.emit('mouse', key);
return;
}
if (parts = /^\x1b\[(O|I)/.exec(s)) {
key.action = parts[1] === 'I'
? 'focus'
: 'blur';
self.emit('mouse', key);
self.emit(key.action);
return;
}
};
// gpm support for linux vc
Program.prototype.enableGpm = function() {
var self = this;
var gpmclient = Require('term/gpmclient');
if (this.gpm) return;
this.gpm = gpmclient();
this.gpm.on('btndown', function(btn, modifier, x, y) {
x--, y--;
var key = {
name: 'mouse',
type: 'GPM',
action: 'mousedown',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
this.gpm.on('btnup', function(btn, modifier, x, y) {
x--, y--;
var key = {
name: 'mouse',
type: 'GPM',
action: 'mouseup',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
this.gpm.on('move', function(btn, modifier, x, y) {
x--, y--;
var key = {
name: 'mouse',
type: 'GPM',
action: 'mousemove',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
this.gpm.on('drag', function(btn, modifier, x, y) {
x--, y--;
var key = {
name: 'mouse',
type: 'GPM',
action: 'mousemove',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
this.gpm.on('mousewheel', function(btn, modifier, x, y, dx, dy) {
var key = {
name: 'mouse',
type: 'GPM',
action: dy > 0 ? 'wheelup' : 'wheeldown',
button: self.gpm.ButtonName(btn),
raw: [btn, modifier, x, y, dx, dy],
x: x,
y: y,
shift: self.gpm.hasShiftKey(modifier),
meta: self.gpm.hasMetaKey(modifier),
ctrl: self.gpm.hasCtrlKey(modifier)
};
self.emit('mouse', key);
});
};
Program.prototype.disableGpm = function() {
if (this.gpm) {
this.gpm.stop();
delete this.gpm;
}
};
// All possible responses from the terminal
Program.prototype.bindResponse = function() {
if (this._boundResponse) return;
this._boundResponse = true;
var decoder = new StringDecoder('utf8')
, self = this;
this.on('data', function(data) {
data = decoder.write(data);
if (!data) return;
self._bindResponse(data);
});
};
Program.prototype._bindResponse = function(s) {
var out = {}
, parts;
if (Buffer.isBuffer(s)) {
if (s[0] > 127 && s[1] === undefined) {
s[0] -= 128;
s = '\x1b' + s.toString('utf-8');
} else {
s = s.toString('utf-8');
}
}
// CSI P s c
// Send Device Attributes (Primary DA).
// CSI > P s c
// Send Device Attributes (Secondary DA).
if (parts = /^\x1b\[(\?|>)(\d*(?:;\d*)*)c/.exec(s)) {
parts = parts[2].split(';').map(function(ch) {
return +ch || 0;
});
out.event = 'device-attributes';
out.code = 'DA';
if (parts[1] === '?') {
out.type = 'primary-attribute';
// VT100-style params:
if (parts[0] === 1 && parts[2] === 2) {
out.term = 'vt100';
out.advancedVideo = true;
} else if (parts[0] === 1 && parts[2] === 0) {
out.term = 'vt101';
} else if (parts[0] === 6) {
out.term = 'vt102';
} else if (parts[0] === 60
&& parts[1] === 1 && parts[2] === 2
&& parts[3] === 6 && parts[4] === 8
&& parts[5] === 9 && parts[6] === 15) {
out.term = 'vt220';
} else {
// VT200-style params:
parts.forEach(function(attr) {
switch (attr) {
case 1:
out.cols132 = true;
break;
case 2:
out.printer = true;
break;
case 6:
out.selectiveErase = true;
break;
case 8:
out.userDefinedKeys = true;
break;
case 9:
out.nationalReplacementCharsets = true;
break;
case 15:
out.technicalCharacters = true;
break;
case 18:
out.userWindows = true;
break;
case 21:
out.horizontalScrolling = true;
break;
case 22:
out.ansiColor = true;
break;
case 29:
out.ansiTextLocator = true;
break;
}
});
}
} else {
out.type = 'secondary-attribute';
switch (parts[0]) {
case 0:
out.term = 'vt100';
break;
case 1:
out.term = 'vt220';
break;
case 2:
out.term = 'vt240';
break;
case 18:
out.term = 'vt330';
break;
case 19:
out.term = 'vt340';
break;
case 24:
out.term = 'vt320';
break;
case 41:
out.term = 'vt420';
break;
case 61:
out.term = 'vt510';
break;
case 64:
out.term = 'vt520';
break;
case 65:
out.term = 'vt525';
break;
}
out.firmwareVersion = parts[1];
out.romCartridgeRegistrationNumber = parts[2];
}
// LEGACY
out.deviceAttributes = out;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
// CSI Ps n Device Status Report (DSR).
// Ps = 5 -> Status Report. Result (``OK'') is
// CSI 0 n
// CSI ? Ps n
// Device Status Report (DSR, DEC-specific).
// Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
// or CSI ? 1 1 n (not ready).
// Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
// or CSI ? 2 1 n (locked).
// Ps = 2 6 -> Report Keyboard status as
// CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
// The last two parameters apply to VT400 & up, and denote key-
// board ready and LK01 respectively.
// Ps = 5 3 -> Report Locator status as
// CSI ? 5 3 n Locator available, if compiled-in, or
// CSI ? 5 0 n No Locator, if not.
if (parts = /^\x1b\[(\?)?(\d+)(?:;(\d+);(\d+);(\d+))?n/.exec(s)) {
out.event = 'device-status';
out.code = 'DSR';
if (!parts[1] && parts[2] === '0' && !parts[3]) {
out.type = 'device-status';
out.status = 'OK';
// LEGACY
out.deviceStatus = out.status;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
if (parts[1] && (parts[2] === '10' || parts[2] === '11') && !parts[3]) {
out.type = 'printer-status';
out.status = parts[2] === '10'
? 'ready'
: 'not ready';
// LEGACY
out.printerStatus = out.status;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
if (parts[1] && (parts[2] === '20' || parts[2] === '21') && !parts[3]) {
out.type = 'udk-status';
out.status = parts[2] === '20'
? 'unlocked'
: 'locked';
// LEGACY
out.UDKStatus = out.status;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
if (parts[1]
&& parts[2] === '27'
&& parts[3] === '1'
&& parts[4] === '0'
&& parts[5] === '0') {
out.type = 'keyboard-status';
out.status = 'OK';
// LEGACY
out.keyboardStatus = out.status;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
if (parts[1] && (parts[2] === '53' || parts[2] === '50') && !parts[3]) {
out.type = 'locator-status';
out.status = parts[2] === '53'
? 'available'
: 'unavailable';
// LEGACY
out.locator = out.status;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
out.type = 'error';
out.text = 'Unhandled: ' + JSON.stringify(parts);
// LEGACY
out.error = out.text;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
// CSI Ps n Device Status Report (DSR).
// Ps = 6 -> Report Cursor Position (CPR) [row;column].
// Result is
// CSI r ; c R
// CSI ? Ps n
// Device Status Report (DSR, DEC-specific).
// Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
// ? r ; c R (assumes page is zero).
if (parts = /^\x1b\[(\?)?(\d+);(\d+)R/.exec(s)) {
out.event = 'device-status';
out.code = 'DSR';
out.type = 'cursor-status';
out.status = {
x: +parts[3],
y: +parts[2],
page: !parts[1] ? undefined : 0
};
out.x = out.status.x;
out.y = out.status.y;
out.page = out.status.page;
// LEGACY
out.cursor = out.status;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
// CSI Ps ; Ps ; Ps t
// Window manipulation (from dtterm, as well as extensions).
// These controls may be disabled using the allowWindowOps
// resource. Valid values for the first (and any additional
// parameters) are:
// Ps = 1 1 -> Report xterm window state. If the xterm window
// is open (non-iconified), it returns CSI 1 t . If the xterm
// window is iconified, it returns CSI 2 t .
// Ps = 1 3 -> Report xterm window position. Result is CSI 3
// ; x ; y t
// Ps = 1 4 -> Report xterm window in pixels. Result is CSI
// 4 ; height ; width t
// Ps = 1 8 -> Report the size of the text area in characters.
// Result is CSI 8 ; height ; width t
// Ps = 1 9 -> Report the size of the screen in characters.
// Result is CSI 9 ; height ; width t
if (parts = /^\x1b\[(\d+)(?:;(\d+);(\d+))?t/.exec(s)) {
out.event = 'window-manipulation';
out.code = '';
if ((parts[1] === '1' || parts[1] === '2') && !parts[2]) {
out.type = 'window-state';
out.state = parts[1] === '1'
? 'non-iconified'
: 'iconified';
// LEGACY
out.windowState = out.state;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
if (parts[1] === '3' && parts[2]) {
out.type = 'window-position';
out.position = {
x: +parts[2],
y: +parts[3]
};
out.x = out.position.x;
out.y = out.position.y;
// LEGACY
out.windowPosition = out.position;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
if (parts[1] === '4' && parts[2]) {
out.type = 'window-size-pixels';
out.size = {
height: +parts[2],
width: +parts[3]
};
out.height = out.size.height;
out.width = out.size.width;
// LEGACY
out.windowSizePixels = out.size;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
if (parts[1] === '8' && parts[2]) {
out.type = 'textarea-size';
out.size = {
height: +parts[2],
width: +parts[3]
};
out.height = out.size.height;
out.width = out.size.width;
// LEGACY
out.textAreaSizeCharacters = out.size;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
if (parts[1] === '9' && parts[2]) {
out.type = 'screen-size';
out.size = {
height: +parts[2],
width: +parts[3]
};
out.height = out.size.height;
out.width = out.size.width;
// LEGACY
out.screenSizeCharacters = out.size;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
out.type = 'error';
out.text = 'Unhandled: ' + JSON.stringify(parts);
// LEGACY
out.error = out.text;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
// rxvt-unicode does not support window manipulation
// Result Normal: OSC l/L 0xEF 0xBF 0xBD
// Result ASCII: OSC l/L 0x1c (file separator)
// Result UTF8->ASCII: OSC l/L 0xFD
// Test with:
// echo -ne '\ePtmux;\e\e[>3t\e\\'
// sleep 2 && echo -ne '\ePtmux;\e\e[21t\e\\' & cat -v
// -
// echo -ne '\e[>3t'
// sleep 2 && echo -ne '\e[21t' & cat -v
if (parts = /^\x1b\](l|L)([^\x07\x1b]*)$/.exec(s)) {
parts[2] = 'rxvt';
s = '\x1b]' + parts[1] + parts[2] + '\x1b\\';
}
// CSI Ps ; Ps ; Ps t
// Window manipulation (from dtterm, as well as extensions).
// These controls may be disabled using the allowWindowOps
// resource. Valid values for the first (and any additional
// parameters) are:
// Ps = 2 0 -> Report xterm window's icon label. Result is
// OSC L label ST
// Ps = 2 1 -> Report xterm window's title. Result is OSC l
// label ST
if (parts = /^\x1b\](l|L)([^\x07\x1b]*)(?:\x07|\x1b\\)/.exec(s)) {
out.event = 'window-manipulation';
out.code = '';
if (parts[1] === 'L') {
out.type = 'window-icon-label';
out.text = parts[2];
// LEGACY
out.windowIconLabel = out.text;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
if (parts[1] === 'l') {
out.type = 'window-title';
out.text = parts[2];
// LEGACY
out.windowTitle = out.text;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
out.type = 'error';
out.text = 'Unhandled: ' + JSON.stringify(parts);
// LEGACY
out.error = out.text;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
// CSI Ps ' |
// Request Locator Position (DECRQLP).
// -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w
// Parameters are [event;button;row;column;page].
// Valid values for the event:
// Pe = 0 -> locator unavailable - no other parameters sent.
// Pe = 1 -> request - xterm received a DECRQLP.
// Pe = 2 -> left button down.
// Pe = 3 -> left button up.
// Pe = 4 -> middle button down.
// Pe = 5 -> middle button up.
// Pe = 6 -> right button down.
// Pe = 7 -> right button up.
// Pe = 8 -> M4 button down.
// Pe = 9 -> M4 button up.
// Pe = 1 0 -> locator outside filter rectangle.
// ``button'' parameter is a bitmask indicating which buttons are
// pressed:
// Pb = 0 <- no buttons down.
// Pb & 1 <- right button down.
// Pb & 2 <- middle button down.
// Pb & 4 <- left button down.
// Pb & 8 <- M4 button down.
// ``row'' and ``column'' parameters are the coordinates of the
// locator position in the xterm window, encoded as ASCII deci-
// mal.
// The ``page'' parameter is not used by xterm, and will be omit-
// ted.
// NOTE:
// This is already implemented in the _bindMouse
// method, but it might make more sense here.
// The xterm mouse documentation says there is a
// `<` prefix, the DECRQLP says there is no prefix.
if (parts = /^\x1b\[(\d+(?:;\d+){4})&w/.exec(s)) {
parts = parts[1].split(';').map(function(ch) {
return +ch;
});
out.event = 'locator-position';
out.code = 'DECRQLP';
switch (parts[0]) {
case 0:
out.status = 'locator-unavailable';
break;
case 1:
out.status = 'request';
break;
case 2:
out.status = 'left-button-down';
break;
case 3:
out.status = 'left-button-up';
break;
case 4:
out.status = 'middle-button-down';
break;
case 5:
out.status = 'middle-button-up';
break;
case 6:
out.status = 'right-button-down';
break;
case 7:
out.status = 'right-button-up';
break;
case 8:
out.status = 'm4-button-down';
break;
case 9:
out.status = 'm4-button-up';
break;
case 10:
out.status = 'locator-outside';
break;
}
out.mask = parts[1];
out.row = parts[2];
out.col = parts[3];
out.page = parts[4];
// LEGACY
out.locatorPosition = out;
this.emit('response', out);
this.emit('response ' + out.event, out);
return;
}
// OSC Ps ; Pt BEL
// OSC Ps ; Pt ST
// Set Text Parameters
if (parts = /^\x1b\](\d+);([^\x07\x1b]+)(?:\x07|\x1b\\)/.exec(s)) {
out.event = 'text-params';
out.code = 'Set Text Parameters';
out.ps = +s[1];
out.pt = s[2];
this.emit('response', out);
this.emit('response ' + out.event, out);
}
};
Program.prototype.response = function(name, text, callback, noBypass) {
var self = this;
if (arguments.length === 2) {
callback = text;
text = name;
name = null;
}
if (!callback) {
callback = function() {};
}
this.bindResponse();
name = name
? 'response ' + name
: 'response';
var onresponse;
this.once(name, onresponse = function(event) {
if (timeout) clearTimeout(timeout);
if (event.type === 'error') {
return callback(new Error(event.event + ': ' + event.text));
}
return callback(null, event);
});
var timeout = setTimeout(function() {
self.removeListener(name, onresponse);
return callback(new Error('Timeout.'));
}, 2000);
return noBypass
? this._write(text)
: this._twrite(text);
};
Program.prototype._owrite =
Program.prototype.write = function(text) {
if (!this.output.writable) return;
return this.output.write(text);
};
Program.prototype._buffer = function(text) {
if (this._exiting) {
this.flush();
this._owrite(text);
return;
}
if (this._buf) {
this._buf += text;
return;
}
this._buf = text;
nextTick(this._flush);
return true;
};
Program.prototype.flush = function() {
if (!this._buf) return;
this._owrite(this._buf);
this._buf = '';
};
Program.prototype._write = function(text) {
if (this.ret) return text;
if (this.useBuffer) {
return this._buffer(text);
}
return this._owrite(text);
};
// Example: `DCS tmux; ESC Pt ST`
// Real: `DCS tmux; ESC Pt ESC \`
Program.prototype._twrite = function(data) {
var self = this
, iterations = 0
, timer;
if (this.tmux) {
// Replace all STs with BELs so they can be nested within the DCS code.
data = data.replace(/\x1b\\/g, '\x07');
// Wrap in tmux forward DCS:
data = '\x1bPtmux;\x1b' + data + '\x1b\\';
// If we've never even flushed yet, it means we're still in
// the normal buffer. Wait for alt screen buffer.
if (this.output.bytesWritten === 0) {
timer = setInterval(function() {
if (self.output.bytesWritten > 0 || ++iterations === 50) {
clearInterval(timer);
self.flush();
self._owrite(data);
}
}, 100);
return true;
}
// NOTE: Flushing the buffer is required in some cases.
// The DCS code must be at the start of the output.
this.flush();
// Write out raw now that the buffer is flushed.
return this._owrite(data);
}
return this._write(data);
};
Program.prototype.echo =
Program.prototype.print = function(text, attr) {
return attr
? this._write(this.text(text, attr))
: this._write(text);
};
Program.prototype._ncoords = function() {
if (this.x < 0) this.x = 0;
else if (this.x >= this.cols) this.x = this.cols - 1;
if (this.y < 0) this.y = 0;
else if (this.y >= this.rows) this.y = this.rows - 1;
};
Program.prototype.setx = function(x) {
return this.cursorCharAbsolute(x);
// return this.charPosAbsolute(x);
};
Program.prototype.sety = function(y) {
return this.linePosAbsolute(y);
};
Program.prototype.move = function(x, y) {
return this.cursorPos(y, x);
};
// TODO: Fix cud and cuu calls.
Program.prototype.omove = function(x, y) {
if (!this.zero) {
x = (x || 1) - 1;
y = (y || 1) - 1;
} else {
x = x || 0;
y = y || 0;
}
if (y === this.y && x === this.x) {
return;
}
if (y === this.y) {
if (x > this.x) {
this.cuf(x - this.x);
} else if (x < this.x) {
this.cub(this.x - x);
}
} else if (x === this.x) {
if (y > this.y) {
this.cud(y - this.y);
} else if (y < this.y) {
this.cuu(this.y - y);
}
} else {
if (!this.zero) x++, y++;
this.cup(y, x);
}
};
Program.prototype.rsetx = function(x) {
// return this.HPositionRelative(x);
if (!x) return;
return x > 0
? this.forward(x)
: this.back(-x);
};
Program.prototype.rsety = function(y) {
// return this.VPositionRelative(y);
if (!y) return;
return y > 0
? this.up(y)
: this.down(-y);
};
Program.prototype.rmove = function(x, y) {
this.rsetx(x);
this.rsety(y);
};
Program.prototype.simpleInsert = function(ch, i, attr) {
return this._write(this.repeat(ch, i), attr);
};
Program.prototype.repeat = function(ch, i) {
if (!i || i < 0) i = 0;
return Array(i + 1).join(ch);
};
/* Depricated:
Program.prototype.__defineGetter__('title', function() {
return this._title;
});
Program.prototype.__defineSetter__('title', function(title) {
this.setTitle(title);
return this._title;
});
*/
Object.defineProperty(Program.prototype,'title',{
get: function () { return this._title;},
set: function (title) {
this.setTitle(title);
return this._title;
}
});
// Specific to iTerm2, but I think it's really cool.
// Example:
// if (!screen.copyToClipboard(text)) {
// execClipboardProgram(text);
// }
Program.prototype.copyToClipboard = function(text) {
if (this.isiTerm2) {
this._twrite('\x1b]50;CopyToCliboard=' + text + '\x07');
return true;
}
return false;
};
// Only XTerm and iTerm2. If you know of any others, post them.
Program.prototype.cursorShape = function(shape, blink) {
if (this.isiTerm2) {
switch (shape) {
case 'block':
if (!blink) {
this._twrite('\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07');
} else {
this._twrite('\x1b]50;CursorShape=0;BlinkingCursorEnabled=1\x07');
}
break;
case 'underline':
if (!blink) {
// this._twrite('\x1b]50;CursorShape=n;BlinkingCursorEnabled=0\x07');
} else {
// this._twrite('\x1b]50;CursorShape=n;BlinkingCursorEnabled=1\x07');
}
break;
case 'line':
if (!blink) {
this._twrite('\x1b]50;CursorShape=1;BlinkingCursorEnabled=0\x07');
} else {
this._twrite('\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07');
}
break;
}
return true;
} else if (this.term('xterm') || this.term('xterm-color') || this.term('screen')) {
switch (shape) {
case 'block':
if (!blink) {
this._twrite('\x1b[0 q');
} else {
this._twrite('\x1b[1 q');
}
break;
case 'underline':
if (!blink) {
this._twrite('\x1b[2 q');
} else {
this._twrite('\x1b[3 q');
}
break;
case 'line':
if (!blink) {
this._twrite('\x1b[4 q');
} else {
this._twrite('\x1b[5 q');
}
break;
}
return true;
}
return false;
};
Program.prototype.cursorColor = function(color) {
if (this.term('xterm') || this.term('rxvt') || this.term('screen')) {
this._twrite('\x1b]12;' + color + '\x07');
return true;
}
return false;
};
Program.prototype.cursorReset =
Program.prototype.resetCursor = function() {
if (this.term('xterm') || this.term('xterm-color') || this.term('rxvt') || this.term('screen')) {
// XXX
// return this.resetColors();
this._twrite('\x1b[0 q');
this._twrite('\x1b]112\x07');
// urxvt doesnt support OSC 112
this._twrite('\x1b]12;white\x07');
return true;
}
return false;
};
Program.prototype.getTextParams = function(param, callback) {
return this.response('text-params', '\x1b]' + param + ';?\x07', function(err, data) {
if (err) return callback(err);
return callback(null, data.pt);
});
};
Program.prototype.getCursorColor = function(callback) {
return this.getTextParams(12, callback);
};
/**
* Normal
*/
//Program.prototype.pad =
Program.prototype.nul = function() {
//if (this.has('pad')) return this.put.pad();
return this._write('\200');
};
Program.prototype.bel =
Program.prototype.bell = function() {
if (this.has('bel')) return this.put.bel();
return this._write('\x07');
};
Program.prototype.vtab = function() {
this.y++;
this._ncoords();
return this._write('\x0b');
};
Program.prototype.ff =
Program.prototype.form = function() {
if (this.has('ff')) return this.put.ff();
return this._write('\x0c');
};
Program.prototype.kbs =
Program.prototype.backspace = function() {
this.x--;
this._ncoords();
if (this.has('kbs')) return this.put.kbs();
return this._write('\x08');
};
Program.prototype.ht =
Program.prototype.tab = function() {
this.x += 8;
this._ncoords();
if (this.has('ht')) return this.put.ht();
return this._write('\t');
};
Program.prototype.shiftOut = function() {
// if (this.has('S2')) return this.put.S2();
return this._write('\x0e');
};
Program.prototype.shiftIn = function() {
// if (this.has('S3')) return this.put.S3();
return this._write('\x0f');
};
Program.prototype.cr =
Program.prototype.return = function() {
this.x = 0;
if (this.has('cr')) return this.put.cr();
return this._write('\r');
};
Program.prototype.nel =
Program.prototype.newline =
Program.prototype.feed = function() {
if (this.tput && this.tput.bools.eat_newline_glitch && this.x >= this.cols) {
return;
}
this.x = 0;
this.y++;
this._ncoords();
if (this.has('nel')) return this.put.nel();
return this._write('\n');
};
/**
* Esc
*/
// ESC D Index (IND is 0x84).
Program.prototype.ind =
Program.prototype.index = function() {
this.y++;
this._ncoords();
if (this.tput) return this.put.ind();
return this._write('\x1bD');
};
// ESC M Reverse Index (RI is 0x8d).
Program.prototype.ri =
Program.prototype.reverse =
Program.prototype.reverseIndex = function() {
this.y--;
this._ncoords();
if (this.tput) return this.put.ri();
return this._write('\x1bM');
};
// ESC E Next Line (NEL is 0x85).
Program.prototype.nextLine = function() {
this.y++;
this.x = 0;
this._ncoords();
if (this.has('nel')) return this.put.nel();
return this._write('\x1bE');
};
// ESC c Full Reset (RIS).
Program.prototype.reset = function() {
this.x = this.y = 0;
if (this.has('rs1') || this.has('ris')) {
return this.has('rs1')
? this.put.rs1()
: this.put.ris();
}
return this._write('\x1bc');
};
// ESC H Tab Set (HTS is 0x88).
Program.prototype.tabSet = function() {
if (this.tput) return this.put.hts();
return this._write('\x1bH');
};
// ESC 7 Save Cursor (DECSC).
Program.prototype.sc =
Program.prototype.saveCursor = function(key) {
if (key) return this.lsaveCursor(key);
this.savedX = this.x || 0;
this.savedY = this.y || 0;
if (this.tput) return this.put.sc();
return this._write('\x1b7');
};
// ESC 8 Restore Cursor (DECRC).
Program.prototype.rc =
Program.prototype.restoreCursor = function(key, hide) {
if (key) return this.lrestoreCursor(key, hide);
this.x = this.savedX || 0;
this.y = this.savedY || 0;
if (this.tput) return this.put.rc();
return this._write('\x1b8');
};
// Save Cursor Locally
Program.prototype.lsaveCursor = function(key) {
key = key || 'local';
this._saved = this._saved || {};
this._saved[key] = this._saved[key] || {};
this._saved[key].x = this.x;
this._saved[key].y = this.y;
this._saved[key].hidden = this.cursorHidden;
};
// Restore Cursor Locally
Program.prototype.lrestoreCursor = function(key, hide) {
var pos;
key = key || 'local';
if (!this._saved || !this._saved[key]) return;
pos = this._saved[key];
//delete this._saved[key];
this.cup(pos.y, pos.x);
if (hide && pos.hidden !== this.cursorHidden) {
if (pos.hidden) {
this.hideCursor();
} else {
this.showCursor();
}
}
};
// ESC # 3 DEC line height/width
Program.prototype.lineHeight = function() {
return this._write('\x1b#');
};
// ESC (,),*,+,-,. Designate G0-G2 Character Set.
Program.prototype.charset = function(val, level) {
level = level || 0;
// See also:
// acs_chars / acsc / ac
// enter_alt_charset_mode / smacs / as
// exit_alt_charset_mode / rmacs / ae
// enter_pc_charset_mode / smpch / S2
// exit_pc_charset_mode / rmpch / S3
switch (level) {
case 0:
level = '(';
break;
case 1:
level = ')';
break;
case 2:
level = '*';
break;
case 3:
level = '+';
break;
}
var name = typeof val === 'string'
? val.toLowerCase()
: val;
switch (name) {
case 'acs':
case 'scld': // DEC Special Character and Line Drawing Set.
if (this.tput) return this.put.smacs();
val = '0';
break;
case 'uk': // UK
val = 'A';
break;
case 'us': // United States (USASCII).
case 'usascii':
case 'ascii':
if (this.tput) return this.put.rmacs();
val = 'B';
break;
case 'dutch': // Dutch
val = '4';
break;
case 'finnish': // Finnish
val = 'C';
val = '5';
break;
case 'french': // French
val = 'R';
break;
case 'frenchcanadian': // FrenchCanadian
val = 'Q';
break;
case 'german': // German
val = 'K';
break;
case 'italian': // Italian
val = 'Y';
break;
case 'norwegiandanish': // NorwegianDanish
val = 'E';
val = '6';
break;
case 'spanish': // Spanish
val = 'Z';
break;
case 'swedish': // Swedish
val = 'H';
val = '7';
break;
case 'swiss': // Swiss
val = '=';
break;
case 'isolatin': // ISOLatin (actually /A)
val = '/A';
break;
default: // Default
if (this.tput) return this.put.rmacs();
val = 'B';
break;
}
return this._write('\x1b(' + val);
};
Program.prototype.enter_alt_charset_mode =
Program.prototype.as =
Program.prototype.smacs = function() {
return this.charset('acs');
};
Program.prototype.exit_alt_charset_mode =
Program.prototype.ae =
Program.prototype.rmacs = function() {
return this.charset('ascii');
};
// ESC N
// Single Shift Select of G2 Character Set
// ( SS2 is 0x8e). This affects next character only.
// ESC O
// Single Shift Select of G3 Character Set
// ( SS3 is 0x8f). This affects next character only.
// ESC n
// Invoke the G2 Character Set as GL (LS2).
// ESC o
// Invoke the G3 Character Set as GL (LS3).
// ESC |
// Invoke the G3 Character Set as GR (LS3R).
// ESC }
// Invoke the G2 Character Set as GR (LS2R).
// ESC ~
// Invoke the G1 Character Set as GR (LS1R).
Program.prototype.setG = function(val) {
// if (this.tput) return this.put.S2();
// if (this.tput) return this.put.S3();
switch (val) {
case 1:
val = '~'; // GR
break;
case 2:
val = 'n'; // GL
val = '}'; // GR
val = 'N'; // Next Char Only
break;
case 3:
val = 'o'; // GL
val = '|'; // GR
val = 'O'; // Next Char Only
break;
}
return this._write('\x1b' + val);
};
/**
* OSC
*/
// OSC Ps ; Pt ST
// OSC Ps ; Pt BEL
// Set Text Parameters.
Program.prototype.setTitle = function(title) {
this._title = title;
// if (this.term('screen')) {
// // Tmux pane
// // if (this.tmux) {
// // return this._write('\x1b]2;' + title + '\x1b\\');
// // }
// return this._write('\x1bk' + title + '\x1b\\');
// }
return this._twrite('\x1b]0;' + title + '\x07');
};
// OSC Ps ; Pt ST
// OSC Ps ; Pt BEL
// Reset colors
Program.prototype.resetColors = function(param) {
if (this.has('Cr')) {
return this.put.Cr(param);
}
return this._twrite('\x1b]112\x07');
//return this._twrite('\x1b]112;' + param + '\x07');
};
// OSC Ps ; Pt ST
// OSC Ps ; Pt BEL
// Change dynamic colors
Program.prototype.dynamicColors = function(param) {
if (this.has('Cs')) {
return this.put.Cs(param);
}
return this._twrite('\x1b]12;' + param + '\x07');
};
// OSC Ps ; Pt ST
// OSC Ps ; Pt BEL
// Sel data
Program.prototype.selData = function(a, b) {
if (this.has('Ms')) {
return this.put.Ms(a, b);
}
return this._twrite('\x1b]52;' + a + ';' + b + '\x07');
};
/**
* CSI
*/
// CSI Ps A
// Cursor Up Ps Times (default = 1) (CUU).
Program.prototype.cuu =
Program.prototype.up =
Program.prototype.cursorUp = function(param) {
this.y -= param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_up_cursor) {
return this._write(this.repeat(this.tput.cuu1(), param));
}
return this.put.cuu(param);
}
return this._write('\x1b[' + (param || '') + 'A');
};
// CSI Ps B
// Cursor Down Ps Times (default = 1) (CUD).
Program.prototype.cud =
Program.prototype.down =
Program.prototype.cursorDown = function(param) {
this.y += param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_down_cursor) {
return this._write(this.repeat(this.tput.cud1(), param));
}
return this.put.cud(param);
}
return this._write('\x1b[' + (param || '') + 'B');
};
// CSI Ps C
// Cursor Forward Ps Times (default = 1) (CUF).
Program.prototype.cuf =
Program.prototype.right =
Program.prototype.forward =
Program.prototype.cursorForward = function(param) {
this.x += param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_right_cursor) {
return this._write(this.repeat(this.tput.cuf1(), param));
}
return this.put.cuf(param);
}
return this._write('\x1b[' + (param || '') + 'C');
};
// CSI Ps D
// Cursor Backward Ps Times (default = 1) (CUB).
Program.prototype.cub =
Program.prototype.left =
Program.prototype.back =
Program.prototype.cursorBackward = function(param) {
this.x -= param || 1;
this._ncoords();
if (this.tput) {
if (!this.tput.strings.parm_left_cursor) {
return this._write(this.repeat(this.tput.cub1(), param));
}
return this.put.cub(param);
}
return this._write('\x1b[' + (param || '') + 'D');
};
// CSI Ps ; Ps H
// Cursor Position [row;column] (default = [1,1]) (CUP).
Program.prototype.cup =
Program.prototype.pos =
Program.prototype.cursorPos = function(row, col) {
if (!this.zero) {
row = (row || 1) - 1;
col = (col || 1) - 1;
} else {
row = row || 0;
col = col || 0;
}
this.x = col;
this.y = row;
this._ncoords();
if (this.tput) return this.put.cup(row, col);
return this._write('\x1b[' + (row + 1) + ';' + (col + 1) + 'H');
};
// CSI Ps J Erase in Display (ED).
// Ps = 0 -> Erase Below (default).
// Ps = 1 -> Erase Above.
// Ps = 2 -> Erase All.
// Ps = 3 -> Erase Saved Lines (xterm).
// CSI ? Ps J
// Erase in Display (DECSED).
// Ps = 0 -> Selective Erase Below (default).
// Ps = 1 -> Selective Erase Above.
// Ps = 2 -> Selective Erase All.
Program.prototype.ed =
Program.prototype.eraseInDisplay = function(param) {
if (this.tput) {
switch (param) {
case 'above':
param = 1;
break;
case 'all':
param = 2;
break;
case 'saved':
param = 3;
break;
case 'below':
default:
param = 0;
break;
}
// extended tput.E3 = ^[[3;J
return this.put.ed(param);
}
switch (param) {
case 'above':
return this._write('\X1b[1J');
case 'all':
return this._write('\x1b[2J');
case 'saved':
return this._write('\x1b[3J');
case 'below':
default:
return this._write('\x1b[J');
}
};
Program.prototype.clear = function() {
this.x = 0;
this.y = 0;
if (this.tput) return this.put.clear();
return this._write('\x1b[H\x1b[J');
};
// CSI Ps K Erase in Line (EL).
// Ps = 0 -> Erase to Right (default).
// Ps = 1 -> Erase to Left.
// Ps = 2 -> Erase All.
// CSI ? Ps K
// Erase in Line (DECSEL).
// Ps = 0 -> Selective Erase to Right (default).
// Ps = 1 -> Selective Erase to Left.
// Ps = 2 -> Selective Erase All.
Program.prototype.el =
Program.prototype.eraseInLine = function(param) {
if (this.tput) {
//if (this.tput.back_color_erase) ...
switch (param) {
case 'left':
param = 1;
break;
case 'all':
param = 2;
break;
case 'right':
default:
param = 0;
break;
}
return this.put.el(param);
}
switch (param) {
case 'left':
return this._write('\x1b[1K');
case 'all':
return this._write('\x1b[2K');
case 'right':
default:
return this._write('\x1b[K');
}
};
// CSI Pm m Character Attributes (SGR).
// Ps = 0 -> Normal (default).
// Ps = 1 -> Bold.
// Ps = 4 -> Underlined.
// Ps = 5 -> Blink (appears as Bold).
// Ps = 7 -> Inverse.
// Ps = 8 -> Invisible, i.e., hidden (VT300).
// Ps = 2 2 -> Normal (neither bold nor faint).
// Ps = 2 4 -> Not underlined.
// Ps = 2 5 -> Steady (not blinking).
// Ps = 2 7 -> Positive (not inverse).
// Ps = 2 8 -> Visible, i.e., not hidden (VT300).
// Ps = 3 0 -> Set foreground color to Black.
// Ps = 3 1 -> Set foreground color to Red.
// Ps = 3 2 -> Set foreground color to Green.
// Ps = 3 3 -> Set foreground color to Yellow.
// Ps = 3 4 -> Set foreground color to Blue.
// Ps = 3 5 -> Set foreground color to Magenta.
// Ps = 3 6 -> Set foreground color to Cyan.
// Ps = 3 7 -> Set foreground color to White.
// Ps = 3 9 -> Set foreground color to default (original).
// Ps = 4 0 -> Set background color to Black.
// Ps = 4 1 -> Set background color to Red.
// Ps = 4 2 -> Set background color to Green.
// Ps = 4 3 -> Set background color to Yellow.
// Ps = 4 4 -> Set background color to Blue.
// Ps = 4 5 -> Set background color to Magenta.
// Ps = 4 6 -> Set background color to Cyan.
// Ps = 4 7 -> Set background color to White.
// Ps = 4 9 -> Set background color to default (original).
// If 16-color support is compiled, the following apply. Assume
// that xterm's resources are set so that the ISO color codes are
// the first 8 of a set of 16. Then the aixterm colors are the
// bright versions of the ISO colors:
// Ps = 9 0 -> Set foreground color to Black.
// Ps = 9 1 -> Set foreground color to Red.
// Ps = 9 2 -> Set foreground color to Green.
// Ps = 9 3 -> Set foreground color to Yellow.
// Ps = 9 4 -> Set foreground color to Blue.
// Ps = 9 5 -> Set foreground color to Magenta.
// Ps = 9 6 -> Set foreground color to Cyan.
// Ps = 9 7 -> Set foreground color to White.
// Ps = 1 0 0 -> Set background color to Black.
// Ps = 1 0 1 -> Set background color to Red.
// Ps = 1 0 2 -> Set background color to Green.
// Ps = 1 0 3 -> Set background color to Yellow.
// Ps = 1 0 4 -> Set background color to Blue.
// Ps = 1 0 5 -> Set background color to Magenta.
// Ps = 1 0 6 -> Set background color to Cyan.
// Ps = 1 0 7 -> Set background color to White.
// If xterm is compiled with the 16-color support disabled, it
// supports the following, from rxvt:
// Ps = 1 0 0 -> Set foreground and background color to
// default.
// If 88- or 256-color support is compiled, the following apply.
// Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second
// Ps.
// Ps = 4 8 ; 5 ; Ps -> Set background color to the second
// Ps.
Program.prototype.sgr =
Program.prototype.attr =
Program.prototype.charAttributes = function(param, val) {
return this._write(this._attr(param, val));
};
Program.prototype.text = function(text, attr) {
return this._attr(attr, true) + text + this._attr(attr, false);
};
// NOTE: sun-color may not allow multiple params for SGR.
Program.prototype._attr = function(param, val) {
var self = this
, parts
, color
, m;
if (Array.isArray(param)) {
parts = param;
param = parts[0] || 'normal';
} else {
param = param || 'normal';
parts = param.split(/\s*[,;]\s*/);
}
if (parts.length > 1) {
var used = {}
, out = [];
parts.forEach(function(part) {
part = self._attr(part, val).slice(2, -1);
if (part === '') return;
if (used[part]) return;
used[part] = true;
out.push(part);
});
return '\x1b[' + out.join(';') + 'm';
}
if (param.indexOf('no ') === 0) {
param = param.substring(3);
val = false;
} else if (param.indexOf('!') === 0) {
param = param.substring(1);
val = false;
}
switch (param) {
// attributes
case 'normal':
case 'default':
if (val === false) return '';
return '\x1b[m';
case 'bold':
return val === false
? '\x1b[22m'
: '\x1b[1m';
case 'ul':
case 'underline':
case 'underlined':
return val === false
? '\x1b[24m'
: '\x1b[4m';
case 'blink':
return val === false
? '\x1b[25m'
: '\x1b[5m';
case 'inverse':
return val === false
? '\x1b[27m'
: '\x1b[7m';
case 'invisible':
return val === false
? '\x1b[28m'
: '\x1b[8m';
// 8-color foreground
case 'black fg':
return val === false
? '\x1b[39m'
: '\x1b[30m';
case 'red fg':
return val === false
? '\x1b[39m'
: '\x1b[31m';
case 'green fg':
return val === false
? '\x1b[39m'
: '\x1b[32m';
case 'yellow fg':
return val === false
? '\x1b[39m'
: '\x1b[33m';
case 'blue fg':
return val === false
? '\x1b[39m'
: '\x1b[34m';
case 'magenta fg':
return val === false
? '\x1b[39m'
: '\x1b[35m';
case 'cyan fg':
return val === false
? '\x1b[39m'
: '\x1b[36m';
case 'white fg':
case 'light grey fg':
case 'light gray fg':
case 'bright grey fg':
case 'bright gray fg':
return val === false
? '\x1b[39m'
: '\x1b[37m';
case 'default fg':
if (val === false) return '';
return '\x1b[39m';
// 8-color background
case 'black bg':
return val === false
? '\x1b[49m'
: '\x1b[40m';
case 'red bg':
return val === false
? '\x1b[49m'
: '\x1b[41m';
case 'green bg':
return val === false
? '\x1b[49m'
: '\x1b[42m';
case 'yellow bg':
return val === false
? '\x1b[49m'
: '\x1b[43m';
case 'blue bg':
return val === false
? '\x1b[49m'
: '\x1b[44m';
case 'magenta bg':
return val === false
? '\x1b[49m'
: '\x1b[45m';
case 'cyan bg':
return val === false
? '\x1b[49m'
: '\x1b[46m';
case 'white bg':
case 'light grey bg':
case 'light gray bg':
case 'bright grey bg':
case 'bright gray bg':
return val === false
? '\x1b[49m'
: '\x1b[47m';
case 'default bg':
if (val === false) return '';
return '\x1b[49m';
// 16-color foreground
case 'light black fg':
case 'bright black fg':
case 'grey fg':
case 'gray fg':
return val === false
? '\x1b[39m'
: '\x1b[90m';
case 'light red fg':
case 'bright red fg':
return val === false
? '\x1b[39m'
: '\x1b[91m';
case 'light green fg':
case 'bright green fg':
return val === false
? '\x1b[39m'
: '\x1b[92m';
case 'light yellow fg':
case 'bright yellow fg':
return val === false
? '\x1b[39m'
: '\x1b[93m';
case 'light blue fg':
case 'bright blue fg':
return val === false
? '\x1b[39m'
: '\x1b[94m';
case 'light magenta fg':
case 'bright magenta fg':
return val === false
? '\x1b[39m'
: '\x1b[95m';
case 'light cyan fg':
case 'bright cyan fg':
return val === false
? '\x1b[39m'
: '\x1b[96m';
case 'light white fg':
case 'bright white fg':
return val === false
? '\x1b[39m'
: '\x1b[97m';
// 16-color background
case 'light black bg':
case 'bright black bg':
case 'grey bg':
case 'gray bg':
return val === false
? '\x1b[49m'
: '\x1b[100m';
case 'light red bg':
case 'bright red bg':
return val === false
? '\x1b[49m'
: '\x1b[101m';
case 'light green bg':
case 'bright green bg':
return val === false
? '\x1b[49m'
: '\x1b[102m';
case 'light yellow bg':
case 'bright yellow bg':
return val === false
? '\x1b[49m'
: '\x1b[103m';
case 'light blue bg':
case 'bright blue bg':
return val === false
? '\x1b[49m'
: '\x1b[104m';
case 'light magenta bg':
case 'bright magenta bg':
return val === false
? '\x1b[49m'
: '\x1b[105m';
case 'light cyan bg':
case 'bright cyan bg':
return val === false
? '\x1b[49m'
: '\x1b[106m';
case 'light white bg':
case 'bright white bg':
return val === false
? '\x1b[49m'
: '\x1b[107m';
// non-16-color rxvt default fg and bg
case 'default fg bg':
if (val === false) return '';
return this.term('rxvt')
? '\x1b[100m'
: '\x1b[39;49m';
default:
// 256-color fg and bg
if (param[0] === '#') {
param = param.replace(/#(?:[0-9a-f]{3}){1,2}/i, colors.match);
}
m = /^(-?\d+) (fg|bg)$/.exec(param);
if (m) {
color = +m[1];
if (val === false || color === -1) {
return this._attr('default ' + m[2]);
}
color = colors.reduce(color, this.tput.colors);
if (color < 16 || (this.tput && this.tput.colors <= 16)) {
if (m[2] === 'fg') {
if (color < 8) {
color += 30;
} else if (color < 16) {
color -= 8;
color += 90;
}
} else if (m[2] === 'bg') {
if (color < 8) {
color += 40;
} else if (color < 16) {
color -= 8;
color += 100;
}
}
return '\x1b[' + color + 'm';
}
if (m[2] === 'fg') {
return '\x1b[38;5;' + color + 'm';
}
if (m[2] === 'bg') {
return '\x1b[48;5;' + color + 'm';
}
}
if (/^[\d;]*$/.test(param)) {
return '\x1b[' + param + 'm';
}
return null;
}
};
Program.prototype.fg =
Program.prototype.setForeground = function(color, val) {
color = color.split(/\s*[,;]\s*/).join(' fg, ') + ' fg';
return this.attr(color, val);
};
Program.prototype.bg =
Program.prototype.setBackground = function(color, val) {
color = color.split(/\s*[,;]\s*/).join(' bg, ') + ' bg';
return this.attr(color, val);
};
// CSI Ps n Device Status Report (DSR).
// Ps = 5 -> Status Report. Result (``OK'') is
// CSI 0 n
// Ps = 6 -> Report Cursor Position (CPR) [row;column].
// Result is
// CSI r ; c R
// CSI ? Ps n
// Device Status Report (DSR, DEC-specific).
// Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
// ? r ; c R (assumes page is zero).
// Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
// or CSI ? 1 1 n (not ready).
// Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
// or CSI ? 2 1 n (locked).
// Ps = 2 6 -> Report Keyboard status as
// CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
// The last two parameters apply to VT400 & up, and denote key-
// board ready and LK01 respectively.
// Ps = 5 3 -> Report Locator status as
// CSI ? 5 3 n Locator available, if compiled-in, or
// CSI ? 5 0 n No Locator, if not.
Program.prototype.dsr =
Program.prototype.deviceStatus = function(param, callback, dec, noBypass) {
if (dec) {
return this.response('device-status',
'\x1b[?' + (param || '0') + 'n', callback, noBypass);
}
return this.response('device-status',
'\x1b[' + (param || '0') + 'n', callback, noBypass);
};
Program.prototype.getCursor = function(callback) {
return this.deviceStatus(6, callback, false, true);
};
Program.prototype.saveReportedCursor = function(callback) {
var self = this;
if (this.tput.strings.user7 === '\x1b[6n' || this.term('screen')) {
return this.getCursor(function(err, data) {
if (data) {
self._rx = data.status.x;
self._ry = data.status.y;
}
if (!callback) return;
return callback(err);
});
}
if (!callback) return;
return callback();
};
Program.prototype.restoreReportedCursor = function() {
if (this._rx == null) return;
return this.cup(this._ry, this._rx);
// return this.nel();
};
/**
* Additions
*/
// CSI Ps @
// Insert Ps (Blank) Character(s) (default = 1) (ICH).
Program.prototype.ich =
Program.prototype.insertChars = function(param) {
this.x += param || 1;
this._ncoords();
if (this.tput) return this.put.ich(param);
return this._write('\x1b[' + (param || 1) + '@');
};
// CSI Ps E
// Cursor Next Line Ps Times (default = 1) (CNL).
// same as CSI Ps B ?
Program.prototype.cnl =
Program.prototype.cursorNextLine = function(param) {
this.y += param || 1;
this._ncoords();
return this._write('\x1b[' + (param || '') + 'E');
};
// CSI Ps F
// Cursor Preceding Line Ps Times (default = 1) (CNL).
// reuse CSI Ps A ?
Program.prototype.cpl =
Program.prototype.cursorPrecedingLine = function(param) {
this.y -= param || 1;
this._ncoords();
return this._write('\x1b[' + (param || '') + 'F');
};
// CSI Ps G
// Cursor Character Absolute [column] (default = [row,1]) (CHA).
Program.prototype.cha =
Program.prototype.cursorCharAbsolute = function(param) {
if (!this.zero) {
param = (param || 1) - 1;
} else {
param = param || 0;
}
this.x = param;
this.y = 0;
this._ncoords();
if (this.tput) return this.put.hpa(param);
return this._write('\x1b[' + (param + 1) + 'G');
};
// CSI Ps L
// Insert Ps Line(s) (default = 1) (IL).
Program.prototype.il =
Program.prototype.insertLines = function(param) {
if (this.tput) return this.put.il(param);
return this._write('\x1b[' + (param || '') + 'L');
};
// CSI Ps M
// Delete Ps Line(s) (default = 1) (DL).
Program.prototype.dl =
Program.prototype.deleteLines = function(param) {
if (this.tput) return this.put.dl(param);
return this._write('\x1b[' + (param || '') + 'M');
};
// CSI Ps P
// Delete Ps Character(s) (default = 1) (DCH).
Program.prototype.dch =
Program.prototype.deleteChars = function(param) {
if (this.tput) return this.put.dch(param);
return this._write('\x1b[' + (param || '') + 'P');
};
// CSI Ps X
// Erase Ps Character(s) (default = 1) (ECH).
Program.prototype.ech =
Program.prototype.eraseChars = function(param) {
if (this.tput) return this.put.ech(param);
return this._write('\x1b[' + (param || '') + 'X');
};
// CSI Pm ` Character Position Absolute
// [column] (default = [row,1]) (HPA).
Program.prototype.hpa =
Program.prototype.charPosAbsolute = function(param) {
this.x = param || 0;
this._ncoords();
if (this.tput) {
return this.put.hpa.apply(this.put, arguments);
}
param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + '`');
};
// 141 61 a * HPR -
// Horizontal Position Relative
// reuse CSI Ps C ?
Program.prototype.hpr =
Program.prototype.HPositionRelative = function(param) {
if (this.tput) return this.cuf(param);
this.x += param || 1;
this._ncoords();
// Does not exist:
// if (this.tput) return this.put.hpr(param);
return this._write('\x1b[' + (param || '') + 'a');
};
// CSI Ps c Send Device Attributes (Primary DA).
// Ps = 0 or omitted -> request attributes from terminal. The
// response depends on the decTerminalID resource setting.
// -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'')
// -> CSI ? 1 ; 0 c (``VT101 with No Options'')
// -> CSI ? 6 c (``VT102'')
// -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'')
// The VT100-style response parameters do not mean anything by
// themselves. VT220 parameters do, telling the host what fea-
// tures the terminal supports:
// Ps = 1 -> 132-columns.
// Ps = 2 -> Printer.
// Ps = 6 -> Selective erase.
// Ps = 8 -> User-defined keys.
// Ps = 9 -> National replacement character sets.
// Ps = 1 5 -> Technical characters.
// Ps = 2 2 -> ANSI color, e.g., VT525.
// Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode).
// CSI > Ps c
// Send Device Attributes (Secondary DA).
// Ps = 0 or omitted -> request the terminal's identification
// code. The response depends on the decTerminalID resource set-
// ting. It should apply only to VT220 and up, but xterm extends
// this to VT100.
// -> CSI > Pp ; Pv ; Pc c
// where Pp denotes the terminal type
// Pp = 0 -> ``VT100''.
// Pp = 1 -> ``VT220''.
// and Pv is the firmware version (for xterm, this was originally
// the XFree86 patch number, starting with 95). In a DEC termi-
// nal, Pc indicates the ROM cartridge registration number and is
// always zero.
// More information:
// xterm/charproc.c - line 2012, for more information.
// vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
Program.prototype.da =
Program.prototype.sendDeviceAttributes = function(param, callback) {
return this.response('device-attributes',
'\x1b[' + (param || '') + 'c', callback);
};
// CSI Pm d
// Line Position Absolute [row] (default = [1,column]) (VPA).
// NOTE: Can't find in terminfo, no idea why it has multiple params.
Program.prototype.vpa =
Program.prototype.linePosAbsolute = function(param) {
this.y = param || 1;
this._ncoords();
if (this.tput) {
return this.put.vpa.apply(this.put, arguments);
}
param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + 'd');
};
// 145 65 e * VPR - Vertical Position Relative
// reuse CSI Ps B ?
Program.prototype.vpr =
Program.prototype.VPositionRelative = function(param) {
if (this.tput) return this.cud(param);
this.y += param || 1;
this._ncoords();
// Does not exist:
// if (this.tput) return this.put.vpr(param);
return this._write('\x1b[' + (param || '') + 'e');
};
// CSI Ps ; Ps f
// Horizontal and Vertical Position [row;column] (default =
// [1,1]) (HVP).
Program.prototype.hvp =
Program.prototype.HVPosition = function(row, col) {
if (!this.zero) {
row = (row || 1) - 1;
col = (col || 1) - 1;
} else {
row = row || 0;
col = col || 0;
}
this.y = row;
this.x = col;
this._ncoords();
// Does not exist (?):
// if (this.tput) return this.put.hvp(row, col);
if (this.tput) return this.put.cup(row, col);
return this._write('\x1b[' + (row + 1) + ';' + (col + 1) + 'f');
};
// CSI Pm h Set Mode (SM).
// Ps = 2 -> Keyboard Action Mode (AM).
// Ps = 4 -> Insert Mode (IRM).
// Ps = 1 2 -> Send/receive (SRM).
// Ps = 2 0 -> Automatic Newline (LNM).
// CSI ? Pm h
// DEC Private Mode Set (DECSET).
// Ps = 1 -> Application Cursor Keys (DECCKM).
// Ps = 2 -> Designate USASCII for character sets G0-G3
// (DECANM), and set VT100 mode.
// Ps = 3 -> 132 Column Mode (DECCOLM).
// Ps = 4 -> Smooth (Slow) Scroll (DECSCLM).
// Ps = 5 -> Reverse Video (DECSCNM).
// Ps = 6 -> Origin Mode (DECOM).
// Ps = 7 -> Wraparound Mode (DECAWM).
// Ps = 8 -> Auto-repeat Keys (DECARM).
// Ps = 9 -> Send Mouse X & Y on button press. See the sec-
// tion Mouse Tracking.
// Ps = 1 0 -> Show toolbar (rxvt).
// Ps = 1 2 -> Start Blinking Cursor (att610).
// Ps = 1 8 -> Print form feed (DECPFF).
// Ps = 1 9 -> Set print extent to full screen (DECPEX).
// Ps = 2 5 -> Show Cursor (DECTCEM).
// Ps = 3 0 -> Show scrollbar (rxvt).
// Ps = 3 5 -> Enable font-shifting functions (rxvt).
// Ps = 3 8 -> Enter Tektronix Mode (DECTEK).
// Ps = 4 0 -> Allow 80 -> 132 Mode.
// Ps = 4 1 -> more(1) fix (see curses resource).
// Ps = 4 2 -> Enable Nation Replacement Character sets (DECN-
// RCM).
// Ps = 4 4 -> Turn On Margin Bell.
// Ps = 4 5 -> Reverse-wraparound Mode.
// Ps = 4 6 -> Start Logging. This is normally disabled by a
// compile-time option.
// Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis-
// abled by the titeInhibit resource).
// Ps = 6 6 -> Application keypad (DECNKM).
// Ps = 6 7 -> Backarrow key sends backspace (DECBKM).
// Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
// release. See the section Mouse Tracking.
// Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
// Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
// Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
// Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
// Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
// Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt).
// Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt).
// Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit.
// (enables the eightBitInput resource).
// Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num-
// Lock keys. (This enables the numLock resource).
// Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This
// enables the metaSendsEscape resource).
// Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete
// key.
// Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This
// enables the altSendsEscape resource).
// Ps = 1 0 4 0 -> Keep selection even if not highlighted.
// (This enables the keepSelection resource).
// Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables
// the selectToClipboard resource).
// Ps = 1 0 4 2 -> Enable Urgency window manager hint when
// Control-G is received. (This enables the bellIsUrgent
// resource).
// Ps = 1 0 4 3 -> Enable raising of the window when Control-G
// is received. (enables the popOnBell resource).
// Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be
// disabled by the titeInhibit resource).
// Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis-
// abled by the titeInhibit resource).
// Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate
// Screen Buffer, clearing it first. (This may be disabled by
// the titeInhibit resource). This combines the effects of the 1
// 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based
// applications rather than the 4 7 mode.
// Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode.
// Ps = 1 0 5 1 -> Set Sun function-key mode.
// Ps = 1 0 5 2 -> Set HP function-key mode.
// Ps = 1 0 5 3 -> Set SCO function-key mode.
// Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6).
// Ps = 1 0 6 1 -> Set VT220 keyboard emulation.
// Ps = 2 0 0 4 -> Set bracketed paste mode.
// Modes:
// http://vt100.net/docs/vt220-rm/chapter4.html
Program.prototype.sm =
Program.prototype.setMode = function() {
var param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + 'h');
};
Program.prototype.decset = function() {
var param = slice.call(arguments).join(';');
return this.setMode('?' + param);
};
Program.prototype.dectcem =
Program.prototype.cnorm =
Program.prototype.cvvis =
Program.prototype.showCursor = function() {
this.cursorHidden = false;
// NOTE: In xterm terminfo:
// cnorm stops blinking cursor
// cvvis starts blinking cursor
if (this.tput) return this.put.cnorm();
//if (this.tput) return this.put.cvvis();
// return this._write('\x1b[?12l\x1b[?25h'); // cursor_normal
// return this._write('\x1b[?12;25h'); // cursor_visible
return this.setMode('?25');
};
Program.prototype.alternate =
Program.prototype.smcup =
Program.prototype.alternateBuffer = function() {
this.isAlt = true;
if (this.tput) return this.put.smcup();
if (this.term('vt') || this.term('linux')) return;
this.setMode('?47');
return this.setMode('?1049');
};
// CSI Pm l Reset Mode (RM).
// Ps = 2 -> Keyboard Action Mode (AM).
// Ps = 4 -> Replace Mode (IRM).
// Ps = 1 2 -> Send/receive (SRM).
// Ps = 2 0 -> Normal Linefeed (LNM).
// CSI ? Pm l
// DEC Private Mode Reset (DECRST).
// Ps = 1 -> Normal Cursor Keys (DECCKM).
// Ps = 2 -> Designate VT52 mode (DECANM).
// Ps = 3 -> 80 Column Mode (DECCOLM).
// Ps = 4 -> Jump (Fast) Scroll (DECSCLM).
// Ps = 5 -> Normal Video (DECSCNM).
// Ps = 6 -> Normal Cursor Mode (DECOM).
// Ps = 7 -> No Wraparound Mode (DECAWM).
// Ps = 8 -> No Auto-repeat Keys (DECARM).
// Ps = 9 -> Don't send Mouse X & Y on button press.
// Ps = 1 0 -> Hide toolbar (rxvt).
// Ps = 1 2 -> Stop Blinking Cursor (att610).
// Ps = 1 8 -> Don't print form feed (DECPFF).
// Ps = 1 9 -> Limit print to scrolling region (DECPEX).
// Ps = 2 5 -> Hide Cursor (DECTCEM).
// Ps = 3 0 -> Don't show scrollbar (rxvt).
// Ps = 3 5 -> Disable font-shifting functions (rxvt).
// Ps = 4 0 -> Disallow 80 -> 132 Mode.
// Ps = 4 1 -> No more(1) fix (see curses resource).
// Ps = 4 2 -> Disable Nation Replacement Character sets (DEC-
// NRCM).
// Ps = 4 4 -> Turn Off Margin Bell.
// Ps = 4 5 -> No Reverse-wraparound Mode.
// Ps = 4 6 -> Stop Logging. (This is normally disabled by a
// compile-time option).
// Ps = 4 7 -> Use Normal Screen Buffer.
// Ps = 6 6 -> Numeric keypad (DECNKM).
// Ps = 6 7 -> Backarrow key sends delete (DECBKM).
// Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
// release. See the section Mouse Tracking.
// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
// Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
// Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
// Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
// Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
// Ps = 1 0 1 0 -> Don't scroll to bottom on tty output
// (rxvt).
// Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt).
// Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables
// the eightBitInput resource).
// Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num-
// Lock keys. (This disables the numLock resource).
// Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key.
// (This disables the metaSendsEscape resource).
// Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad
// Delete key.
// Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key.
// (This disables the altSendsEscape resource).
// Ps = 1 0 4 0 -> Do not keep selection when not highlighted.
// (This disables the keepSelection resource).
// Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables
// the selectToClipboard resource).
// Ps = 1 0 4 2 -> Disable Urgency window manager hint when
// Control-G is received. (This disables the bellIsUrgent
// resource).
// Ps = 1 0 4 3 -> Disable raising of the window when Control-
// G is received. (This disables the popOnBell resource).
// Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen
// first if in the Alternate Screen. (This may be disabled by
// the titeInhibit resource).
// Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be
// disabled by the titeInhibit resource).
// Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor
// as in DECRC. (This may be disabled by the titeInhibit
// resource). This combines the effects of the 1 0 4 7 and 1 0
// 4 8 modes. Use this with terminfo-based applications rather
// than the 4 7 mode.
// Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode.
// Ps = 1 0 5 1 -> Reset Sun function-key mode.
// Ps = 1 0 5 2 -> Reset HP function-key mode.
// Ps = 1 0 5 3 -> Reset SCO function-key mode.
// Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6).
// Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.
// Ps = 2 0 0 4 -> Reset bracketed paste mode.
Program.prototype.rm =
Program.prototype.resetMode = function() {
var param = slice.call(arguments).join(';');
return this._write('\x1b[' + (param || '') + 'l');
};
Program.prototype.decrst = function() {
var param = slice.call(arguments).join(';');
return this.resetMode('?' + param);
};
Program.prototype.dectcemh =
Program.prototype.cursor_invisible =
Program.prototype.vi =
Program.prototype.civis =
Program.prototype.hideCursor = function(force) {
this.cursorHidden = true;
if (!force && this.tput) return this.put.civis();
return this.resetMode('?25');
};
Program.prototype.rmcup =
Program.prototype.normalBuffer = function() {
this.isAlt = false;
if (this.tput) return this.put.rmcup();
this.resetMode('?47');
return this.resetMode('?1049');
};
Program.prototype.enableMouse = function() {
if (process.env.BLESSED_FORCE_MODES) {
var modes = process.env.BLESSED_FORCE_MODES.split(',');
var options = {};
for (var n = 0; n < modes.length; ++n) {
var pair = modes[n].split('=');
var v = pair[1] !== '0';
switch (pair[0].toUpperCase()) {
case 'SGRMOUSE':
options.sgrMouse = v;
break;
case 'UTFMOUSE':
options.utfMouse = v;
break;
case 'VT200MOUSE':
options.vt200Mouse = v;
break;
case 'URXVTMOUSE':
options.urxvtMouse = v;
break;
case 'X10MOUSE':
options.x10Mouse = v;
break;
case 'DECMOUSE':
options.decMouse = v;
break;
case 'PTERMMOUSE':
options.ptermMouse = v;
break;
case 'JSBTERMMOUSE':
options.jsbtermMouse = v;
break;
case 'VT200HILITE':
options.vt200Hilite = v;
break;
case 'GPMMOUSE':
options.gpmMouse = v;
break;
case 'CELLMOTION':
options.cellMotion = v;
break;
case 'ALLMOTION':
options.allMotion = v;
break;
case 'SENDFOCUS':
options.sendFocus = v;
break;
}
}
return this.setMouse(options, true);
}
// NOTE:
// Cell Motion isn't normally need for anything below here, but we'll
// activate it for tmux (whether using it or not) in case our all-motion
// passthrough does not work. It can't hurt.
if (this.term('rxvt-unicode')) {
return this.setMouse({
urxvtMouse: true,
cellMotion: true,
allMotion: true
}, true);
}
// rxvt does not support the X10 UTF extensions
if (this.term('rxvt')) {
return this.setMouse({
vt200Mouse: true,
x10Mouse: true,
cellMotion: true,
allMotion: true
}, true);
}
// libvte is broken. Older versions do not support the
// X10 UTF extension. However, later versions do support
// SGR/URXVT.
if (this.isVTE) {
return this.setMouse({
// NOTE: Could also use urxvtMouse here.
sgrMouse: true,
cellMotion: true,
allMotion: true
}, true);
}
if (this.term('linux')) {
return this.setMouse({
vt200Mouse: true,
gpmMouse: true
}, true);
}
if (this.term('xterm')
|| this.term('screen')
|| (this.tput && this.tput.strings.key_mouse)) {
return this.setMouse({
vt200Mouse: true,
utfMouse: true,
cellMotion: true,
allMotion: true
}, true);
}
};
Program.prototype.disableMouse = function() {
if (!this._currentMouse) return;
var obj = {};
Object.keys(this._currentMouse).forEach(function(key) {
obj[key] = false;
});
return this.setMouse(obj, false);
};
// Set Mouse
Program.prototype.setMouse = function(opt, enable) {
if (opt.normalMouse != null) {
opt.vt200Mouse = opt.normalMouse;
opt.allMotion = opt.normalMouse;
}
if (opt.hiliteTracking != null) {
opt.vt200Hilite = opt.hiliteTracking;
}
if (enable === true) {
if (this._currentMouse) {
this.setMouse(opt);
Object.keys(opt).forEach(function(key) {
this._currentMouse[key] = opt[key];
}, this);
return;
}
this._currentMouse = opt;
this.mouseEnabled = true;
} else if (enable === false) {
delete this._currentMouse;
this.mouseEnabled = false;
}
// Ps = 9 -> Send Mouse X & Y on button press. See the sec-
// tion Mouse Tracking.
// Ps = 9 -> Don't send Mouse X & Y on button press.
// x10 mouse
if (opt.x10Mouse != null) {
if (opt.x10Mouse) this.setMode('?9');
else this.resetMode('?9');
}
// Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
// release. See the section Mouse Tracking.
// Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
// release. See the section Mouse Tracking.
// vt200 mouse
if (opt.vt200Mouse != null) {
if (opt.vt200Mouse) this.setMode('?1000');
else this.resetMode('?1000');
}
// Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
if (opt.vt200Hilite != null) {
if (opt.vt200Hilite) this.setMode('?1001');
else this.resetMode('?1001');
}
// Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
// Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
// button event mouse
if (opt.cellMotion != null) {
if (opt.cellMotion) this.setMode('?1002');
else this.resetMode('?1002');
}
// Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
// Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
// any event mouse
if (opt.allMotion != null) {
// NOTE: Latest versions of tmux seem to only support cellMotion (not
// allMotion). We pass all motion through to the terminal.
if (this.tmux && this.tmuxVersion >= 2) {
if (opt.allMotion) this._twrite('\x1b[?1003h');
else this._twrite('\x1b[?1003l');
} else {
if (opt.allMotion) this.setMode('?1003');
else this.resetMode('?1003');
}
}
// Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
// Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
if (opt.sendFocus != null) {
if (opt.sendFocus) this.setMode('?1004');
else this.resetMode('?1004');
}
// Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
// Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
if (opt.utfMouse != null) {
if (opt.utfMouse) this.setMode('?1005');
else this.resetMode('?1005');
}
// sgr mouse
if (opt.sgrMouse != null) {
if (opt.sgrMouse) this.setMode('?1006');
else this.resetMode('?1006');
}
// urxvt mouse
if (opt.urxvtMouse != null) {
if (opt.urxvtMouse) this.setMode('?1015');
else this.resetMode('?1015');
}
// dec mouse
if (opt.decMouse != null) {
if (opt.decMouse) this._write('\x1b[1;2\'z\x1b[1;3\'{');
else this._write('\x1b[\'z');
}
// pterm mouse
if (opt.ptermMouse != null) {
if (opt.ptermMouse) this._write('\x1b[>1h\x1b[>6h\x1b[>7h\x1b[>1h\x1b[>9l');
else this._write('\x1b[>1l\x1b[>6l\x1b[>7l\x1b[>1l\x1b[>9h');
}
// jsbterm mouse
if (opt.jsbtermMouse != null) {
// + = advanced mode
if (opt.jsbtermMouse) this._write('\x1b[0~ZwLMRK+1Q\x1b\\');
else this._write('\x1b[0~ZwQ\x1b\\');
}
// gpm mouse
if (opt.gpmMouse != null) {
if (opt.gpmMouse) this.enableGpm();
else this.disableGpm();
}
};
// CSI Ps ; Ps r
// Set Scrolling Region [top;bottom] (default = full size of win-
// dow) (DECSTBM).
// CSI ? Pm r
Program.prototype.decstbm =
Program.prototype.csr =
Program.prototype.setScrollRegion = function(top, bottom) {
if (!this.zero) {
top = (top || 1) - 1;
bottom = (bottom || this.rows) - 1;
} else {
top = top || 0;
bottom = bottom || (this.rows - 1);
}
this.scrollTop = top;
this.scrollBottom = bottom;
this.x = 0;
this.y = 0;
this._ncoords();
if (this.tput) return this.put.csr(top, bottom);
return this._write('\x1b[' + (top + 1) + ';' + (bottom + 1) + 'r');
};
// CSI s
// Save cursor (ANSI.SYS).
Program.prototype.scA =
Program.prototype.saveCursorA = function() {
this.savedX = this.x;
this.savedY = this.y;
if (this.tput) return this.put.sc();
return this._write('\x1b[s');
};
// CSI u
// Restore cursor (ANSI.SYS).
Program.prototype.rcA =
Program.prototype.restoreCursorA = function() {
this.x = this.savedX || 0;
this.y = this.savedY || 0;
if (this.tput) return this.put.rc();
return this._write('\x1b[u');
};
/**
* Lesser Used
*/
// CSI Ps I
// Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
Program.prototype.cht =
Program.prototype.cursorForwardTab = function(param) {
this.x += 8;
this._ncoords();
if (this.tput) return this.put.tab(param);
return this._write('\x1b[' + (param || 1) + 'I');
};
// CSI Ps S Scroll up Ps lines (default = 1) (SU).
Program.prototype.su =
Program.prototype.scrollUp = function(param) {
this.y -= param || 1;
this._ncoords();
if (this.tput) return this.put.parm_index(param);
return this._write('\x1b[' + (param || 1) + 'S');
};
// CSI Ps T Scroll down Ps lines (default = 1) (SD).
Program.prototype.sd =
Program.prototype.scrollDown = function(param) {
this.y += param || 1;
this._ncoords();
if (this.tput) return this.put.parm_rindex(param);
return this._write('\x1b[' + (param || 1) + 'T');
};
// CSI Ps ; Ps ; Ps ; Ps ; Ps T
// Initiate highlight mouse tracking. Parameters are
// [func;startx;starty;firstrow;lastrow]. See the section Mouse
// Tracking.
Program.prototype.initMouseTracking = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + 'T');
};
// CSI > Ps; Ps T
// Reset one or more features of the title modes to the default
// value. Normally, "reset" disables the feature. It is possi-
// ble to disable the ability to reset features by compiling a
// different default for the title modes into xterm.
// Ps = 0 -> Do not set window/icon labels using hexadecimal.
// Ps = 1 -> Do not query window/icon labels using hexadeci-
// mal.
// Ps = 2 -> Do not set window/icon labels using UTF-8.
// Ps = 3 -> Do not query window/icon labels using UTF-8.
// (See discussion of "Title Modes").
Program.prototype.resetTitleModes = function() {
return this._write('\x1b[>' + slice.call(arguments).join(';') + 'T');
};
// CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
Program.prototype.cbt =
Program.prototype.cursorBackwardTab = function(param) {
this.x -= 8;
this._ncoords();
if (this.tput) return this.put.cbt(param);
return this._write('\x1b[' + (param || 1) + 'Z');
};
// CSI Ps b Repeat the preceding graphic character Ps times (REP).
Program.prototype.rep =
Program.prototype.repeatPrecedingCharacter = function(param) {
this.x += param || 1;
this._ncoords();
if (this.tput) return this.put.rep(param);
return this._write('\x1b[' + (param || 1) + 'b');
};
// CSI Ps g Tab Clear (TBC).
// Ps = 0 -> Clear Current Column (default).
// Ps = 3 -> Clear All.
// Potentially:
// Ps = 2 -> Clear Stops on Line.
// http://vt100.net/annarbor/aaa-ug/section6.html
Program.prototype.tbc =
Program.prototype.tabClear = function(param) {
if (this.tput) return this.put.tbc(param);
return this._write('\x1b[' + (param || 0) + 'g');
};
// CSI Pm i Media Copy (MC).
// Ps = 0 -> Print screen (default).
// Ps = 4 -> Turn off printer controller mode.
// Ps = 5 -> Turn on printer controller mode.
// CSI ? Pm i
// Media Copy (MC, DEC-specific).
// Ps = 1 -> Print line containing cursor.
// Ps = 4 -> Turn off autoprint mode.
// Ps = 5 -> Turn on autoprint mode.
// Ps = 1 0 -> Print composed display, ignores DECPEX.
// Ps = 1 1 -> Print all pages.
Program.prototype.mc =
Program.prototype.mediaCopy = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + 'i');
};
Program.prototype.print_screen =
Program.prototype.ps =
Program.prototype.mc0 = function() {
if (this.tput) return this.put.mc0();
return this.mc('0');
};
Program.prototype.prtr_on =
Program.prototype.po =
Program.prototype.mc5 = function() {
if (this.tput) return this.put.mc5();
return this.mc('5');
};
Program.prototype.prtr_off =
Program.prototype.pf =
Program.prototype.mc4 = function() {
if (this.tput) return this.put.mc4();
return this.mc('4');
};
Program.prototype.prtr_non =
Program.prototype.pO =
Program.prototype.mc5p = function() {
if (this.tput) return this.put.mc5p();
return this.mc('?5');
};
// CSI > Ps; Ps m
// Set or reset resource-values used by xterm to decide whether
// to construct escape sequences holding information about the
// modifiers pressed with a given key. The first parameter iden-
// tifies the resource to set/reset. The second parameter is the
// value to assign to the resource. If the second parameter is
// omitted, the resource is reset to its initial value.
// Ps = 1 -> modifyCursorKeys.
// Ps = 2 -> modifyFunctionKeys.
// Ps = 4 -> modifyOtherKeys.
// If no parameters are given, all resources are reset to their
// initial values.
Program.prototype.setResources = function() {
return this._write('\x1b[>' + slice.call(arguments).join(';') + 'm');
};
// CSI > Ps n
// Disable modifiers which may be enabled via the CSI > Ps; Ps m
// sequence. This corresponds to a resource value of "-1", which
// cannot be set with the other sequence. The parameter identi-
// fies the resource to be disabled:
// Ps = 1 -> modifyCursorKeys.
// Ps = 2 -> modifyFunctionKeys.
// Ps = 4 -> modifyOtherKeys.
// If the parameter is omitted, modifyFunctionKeys is disabled.
// When modifyFunctionKeys is disabled, xterm uses the modifier
// keys to make an extended sequence of functions rather than
// adding a parameter to each function key to denote the modi-
// fiers.
Program.prototype.disableModifiers = function(param) {
return this._write('\x1b[>' + (param || '') + 'n');
};
// CSI > Ps p
// Set resource value pointerMode. This is used by xterm to
// decide whether to hide the pointer cursor as the user types.
// Valid values for the parameter:
// Ps = 0 -> never hide the pointer.
// Ps = 1 -> hide if the mouse tracking mode is not enabled.
// Ps = 2 -> always hide the pointer. If no parameter is
// given, xterm uses the default, which is 1 .
Program.prototype.setPointerMode = function(param) {
return this._write('\x1b[>' + (param || '') + 'p');
};
// CSI ! p Soft terminal reset (DECSTR).
// http://vt100.net/docs/vt220-rm/table4-10.html
Program.prototype.decstr =
Program.prototype.rs2 =
Program.prototype.softReset = function() {
//if (this.tput) return this.put.init_2string();
//if (this.tput) return this.put.reset_2string();
if (this.tput) return this.put.rs2();
//return this._write('\x1b[!p');
//return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // init
return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // reset
};
// CSI Ps$ p
// Request ANSI mode (DECRQM). For VT300 and up, reply is
// CSI Ps; Pm$ y
// where Ps is the mode number as in RM, and Pm is the mode
// value:
// 0 - not recognized
// 1 - set
// 2 - reset
// 3 - permanently set
// 4 - permanently reset
Program.prototype.decrqm =
Program.prototype.requestAnsiMode = function(param) {
return this._write('\x1b[' + (param || '') + '$p');
};
// CSI ? Ps$ p
// Request DEC private mode (DECRQM). For VT300 and up, reply is
// CSI ? Ps; Pm$ p
// where Ps is the mode number as in DECSET, Pm is the mode value
// as in the ANSI DECRQM.
Program.prototype.decrqmp =
Program.prototype.requestPrivateMode = function(param) {
return this._write('\x1b[?' + (param || '') + '$p');
};
// CSI Ps ; Ps " p
// Set conformance level (DECSCL). Valid values for the first
// parameter:
// Ps = 6 1 -> VT100.
// Ps = 6 2 -> VT200.
// Ps = 6 3 -> VT300.
// Valid values for the second parameter:
// Ps = 0 -> 8-bit controls.
// Ps = 1 -> 7-bit controls (always set for VT100).
// Ps = 2 -> 8-bit controls.
Program.prototype.decscl =
Program.prototype.setConformanceLevel = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '"p');
};
// CSI Ps q Load LEDs (DECLL).
// Ps = 0 -> Clear all LEDS (default).
// Ps = 1 -> Light Num Lock.
// Ps = 2 -> Light Caps Lock.
// Ps = 3 -> Light Scroll Lock.
// Ps = 2 1 -> Extinguish Num Lock.
// Ps = 2 2 -> Extinguish Caps Lock.
// Ps = 2 3 -> Extinguish Scroll Lock.
Program.prototype.decll =
Program.prototype.loadLEDs = function(param) {
return this._write('\x1b[' + (param || '') + 'q');
};
// CSI Ps SP q
// Set cursor style (DECSCUSR, VT520).
// Ps = 0 -> blinking block.
// Ps = 1 -> blinking block (default).
// Ps = 2 -> steady block.
// Ps = 3 -> blinking underline.
// Ps = 4 -> steady underline.
Program.prototype.decscusr =
Program.prototype.setCursorStyle = function(param) {
switch (param) {
case 'blinking block':
param = 1;
break;
case 'block':
case 'steady block':
param = 2;
break;
case 'blinking underline':
param = 3;
break;
case 'underline':
case 'steady underline':
param = 4;
break;
case 'blinking bar':
param = 5;
break;
case 'bar':
case 'steady bar':
param = 6;
break;
}
if (param === 2 && this.has('Se')) {
return this.put.Se();
}
if (this.has('Ss')) {
return this.put.Ss(param);
}
return this._write('\x1b[' + (param || 1) + ' q');
};
// CSI Ps " q
// Select character protection attribute (DECSCA). Valid values
// for the parameter:
// Ps = 0 -> DECSED and DECSEL can erase (default).
// Ps = 1 -> DECSED and DECSEL cannot erase.
// Ps = 2 -> DECSED and DECSEL can erase.
Program.prototype.decsca =
Program.prototype.setCharProtectionAttr = function(param) {
return this._write('\x1b[' + (param || 0) + '"q');
};
// CSI ? Pm r
// Restore DEC Private Mode Values. The value of Ps previously
// saved is restored. Ps values are the same as for DECSET.
Program.prototype.restorePrivateValues = function() {
return this._write('\x1b[?' + slice.call(arguments).join(';') + 'r');
};
// CSI Pt; Pl; Pb; Pr; Ps$ r
// Change Attributes in Rectangular Area (DECCARA), VT400 and up.
// Pt; Pl; Pb; Pr denotes the rectangle.
// Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7.
// NOTE: xterm doesn't enable this code by default.
Program.prototype.deccara =
Program.prototype.setAttrInRectangle = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$r');
};
// CSI ? Pm s
// Save DEC Private Mode Values. Ps values are the same as for
// DECSET.
Program.prototype.savePrivateValues = function() {
return this._write('\x1b[?' + slice.call(arguments).join(';') + 's');
};
// CSI Ps ; Ps ; Ps t
// Window manipulation (from dtterm, as well as extensions).
// These controls may be disabled using the allowWindowOps
// resource. Valid values for the first (and any additional
// parameters) are:
// Ps = 1 -> De-iconify window.
// Ps = 2 -> Iconify window.
// Ps = 3 ; x ; y -> Move window to [x, y].
// Ps = 4 ; height ; width -> Resize the xterm window to
// height and width in pixels.
// Ps = 5 -> Raise the xterm window to the front of the stack-
// ing order.
// Ps = 6 -> Lower the xterm window to the bottom of the
// stacking order.
// Ps = 7 -> Refresh the xterm window.
// Ps = 8 ; height ; width -> Resize the text area to
// [height;width] in characters.
// Ps = 9 ; 0 -> Restore maximized window.
// Ps = 9 ; 1 -> Maximize window (i.e., resize to screen
// size).
// Ps = 1 0 ; 0 -> Undo full-screen mode.
// Ps = 1 0 ; 1 -> Change to full-screen.
// Ps = 1 1 -> Report xterm window state. If the xterm window
// is open (non-iconified), it returns CSI 1 t . If the xterm
// window is iconified, it returns CSI 2 t .
// Ps = 1 3 -> Report xterm window position. Result is CSI 3
// ; x ; y t
// Ps = 1 4 -> Report xterm window in pixels. Result is CSI
// 4 ; height ; width t
// Ps = 1 8 -> Report the size of the text area in characters.
// Result is CSI 8 ; height ; width t
// Ps = 1 9 -> Report the size of the screen in characters.
// Result is CSI 9 ; height ; width t
// Ps = 2 0 -> Report xterm window's icon label. Result is
// OSC L label ST
// Ps = 2 1 -> Report xterm window's title. Result is OSC l
// label ST
// Ps = 2 2 ; 0 -> Save xterm icon and window title on
// stack.
// Ps = 2 2 ; 1 -> Save xterm icon title on stack.
// Ps = 2 2 ; 2 -> Save xterm window title on stack.
// Ps = 2 3 ; 0 -> Restore xterm icon and window title from
// stack.
// Ps = 2 3 ; 1 -> Restore xterm icon title from stack.
// Ps = 2 3 ; 2 -> Restore xterm window title from stack.
// Ps >= 2 4 -> Resize to Ps lines (DECSLPP).
Program.prototype.manipulateWindow = function() {
var args = slice.call(arguments);
var callback = typeof args[args.length - 1] === 'function'
? args.pop()
: function() {};
return this.response('window-manipulation',
'\x1b[' + args.join(';') + 't', callback);
};
Program.prototype.getWindowSize = function(callback) {
return this.manipulateWindow(18, callback);
};
// CSI Pt; Pl; Pb; Pr; Ps$ t
// Reverse Attributes in Rectangular Area (DECRARA), VT400 and
// up.
// Pt; Pl; Pb; Pr denotes the rectangle.
// Ps denotes the attributes to reverse, i.e., 1, 4, 5, 7.
// NOTE: xterm doesn't enable this code by default.
Program.prototype.decrara =
Program.prototype.reverseAttrInRectangle = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$t');
};
// CSI > Ps; Ps t
// Set one or more features of the title modes. Each parameter
// enables a single feature.
// Ps = 0 -> Set window/icon labels using hexadecimal.
// Ps = 1 -> Query window/icon labels using hexadecimal.
// Ps = 2 -> Set window/icon labels using UTF-8.
// Ps = 3 -> Query window/icon labels using UTF-8. (See dis-
// cussion of "Title Modes")
// XXX VTE bizarelly echos this:
Program.prototype.setTitleModeFeature = function() {
return this._twrite('\x1b[>' + slice.call(arguments).join(';') + 't');
};
// CSI Ps SP t
// Set warning-bell volume (DECSWBV, VT520).
// Ps = 0 or 1 -> off.
// Ps = 2 , 3 or 4 -> low.
// Ps = 5 , 6 , 7 , or 8 -> high.
Program.prototype.decswbv =
Program.prototype.setWarningBellVolume = function(param) {
return this._write('\x1b[' + (param || '') + ' t');
};
// CSI Ps SP u
// Set margin-bell volume (DECSMBV, VT520).
// Ps = 1 -> off.
// Ps = 2 , 3 or 4 -> low.
// Ps = 0 , 5 , 6 , 7 , or 8 -> high.
Program.prototype.decsmbv =
Program.prototype.setMarginBellVolume = function(param) {
return this._write('\x1b[' + (param || '') + ' u');
};
// CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v
// Copy Rectangular Area (DECCRA, VT400 and up).
// Pt; Pl; Pb; Pr denotes the rectangle.
// Pp denotes the source page.
// Pt; Pl denotes the target location.
// Pp denotes the target page.
// NOTE: xterm doesn't enable this code by default.
Program.prototype.deccra =
Program.prototype.copyRectangle = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$v');
};
// CSI Pt ; Pl ; Pb ; Pr ' w
// Enable Filter Rectangle (DECEFR), VT420 and up.
// Parameters are [top;left;bottom;right].
// Defines the coordinates of a filter rectangle and activates
// it. Anytime the locator is detected outside of the filter
// rectangle, an outside rectangle event is generated and the
// rectangle is disabled. Filter rectangles are always treated
// as "one-shot" events. Any parameters that are omitted default
// to the current locator position. If all parameters are omit-
// ted, any locator motion will be reported. DECELR always can-
// cels any prevous rectangle definition.
Program.prototype.decefr =
Program.prototype.enableFilterRectangle = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'w');
};
// CSI Ps x Request Terminal Parameters (DECREQTPARM).
// if Ps is a "0" (default) or "1", and xterm is emulating VT100,
// the control sequence elicits a response of the same form whose
// parameters describe the terminal:
// Ps -> the given Ps incremented by 2.
// Pn = 1 <- no parity.
// Pn = 1 <- eight bits.
// Pn = 1 <- 2 8 transmit 38.4k baud.
// Pn = 1 <- 2 8 receive 38.4k baud.
// Pn = 1 <- clock multiplier.
// Pn = 0 <- STP flags.
Program.prototype.decreqtparm =
Program.prototype.requestParameters = function(param) {
return this._write('\x1b[' + (param || 0) + 'x');
};
// CSI Ps x Select Attribute Change Extent (DECSACE).
// Ps = 0 -> from start to end position, wrapped.
// Ps = 1 -> from start to end position, wrapped.
// Ps = 2 -> rectangle (exact).
Program.prototype.decsace =
Program.prototype.selectChangeExtent = function(param) {
return this._write('\x1b[' + (param || 0) + 'x');
};
// CSI Pc; Pt; Pl; Pb; Pr$ x
// Fill Rectangular Area (DECFRA), VT420 and up.
// Pc is the character to use.
// Pt; Pl; Pb; Pr denotes the rectangle.
// NOTE: xterm doesn't enable this code by default.
Program.prototype.decfra =
Program.prototype.fillRectangle = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$x');
};
// CSI Ps ; Pu ' z
// Enable Locator Reporting (DECELR).
// Valid values for the first parameter:
// Ps = 0 -> Locator disabled (default).
// Ps = 1 -> Locator enabled.
// Ps = 2 -> Locator enabled for one report, then disabled.
// The second parameter specifies the coordinate unit for locator
// reports.
// Valid values for the second parameter:
// Pu = 0 <- or omitted -> default to character cells.
// Pu = 1 <- device physical pixels.
// Pu = 2 <- character cells.
Program.prototype.decelr =
Program.prototype.enableLocatorReporting = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'z');
};
// CSI Pt; Pl; Pb; Pr$ z
// Erase Rectangular Area (DECERA), VT400 and up.
// Pt; Pl; Pb; Pr denotes the rectangle.
// NOTE: xterm doesn't enable this code by default.
Program.prototype.decera =
Program.prototype.eraseRectangle = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '$z');
};
// CSI Pm ' {
// Select Locator Events (DECSLE).
// Valid values for the first (and any additional parameters)
// are:
// Ps = 0 -> only respond to explicit host requests (DECRQLP).
// (This is default). It also cancels any filter
// rectangle.
// Ps = 1 -> report button down transitions.
// Ps = 2 -> do not report button down transitions.
// Ps = 3 -> report button up transitions.
// Ps = 4 -> do not report button up transitions.
Program.prototype.decsle =
Program.prototype.setLocatorEvents = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'{');
};
// CSI Pt; Pl; Pb; Pr$ {
// Selective Erase Rectangular Area (DECSERA), VT400 and up.
// Pt; Pl; Pb; Pr denotes the rectangle.
Program.prototype.decsera =
Program.prototype.selectiveEraseRectangle = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + '${');
};
// CSI Ps ' |
// Request Locator Position (DECRQLP).
// Valid values for the parameter are:
// Ps = 0 , 1 or omitted -> transmit a single DECLRP locator
// report.
// If Locator Reporting has been enabled by a DECELR, xterm will
// respond with a DECLRP Locator Report. This report is also
// generated on button up and down events if they have been
// enabled with a DECSLE, or when the locator is detected outside
// of a filter rectangle, if filter rectangles have been enabled
// with a DECEFR.
// -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w
// Parameters are [event;button;row;column;page].
// Valid values for the event:
// Pe = 0 -> locator unavailable - no other parameters sent.
// Pe = 1 -> request - xterm received a DECRQLP.
// Pe = 2 -> left button down.
// Pe = 3 -> left button up.
// Pe = 4 -> middle button down.
// Pe = 5 -> middle button up.
// Pe = 6 -> right button down.
// Pe = 7 -> right button up.
// Pe = 8 -> M4 button down.
// Pe = 9 -> M4 button up.
// Pe = 1 0 -> locator outside filter rectangle.
// ``button'' parameter is a bitmask indicating which buttons are
// pressed:
// Pb = 0 <- no buttons down.
// Pb & 1 <- right button down.
// Pb & 2 <- middle button down.
// Pb & 4 <- left button down.
// Pb & 8 <- M4 button down.
// ``row'' and ``column'' parameters are the coordinates of the
// locator position in the xterm window, encoded as ASCII deci-
// mal.
// The ``page'' parameter is not used by xterm, and will be omit-
// ted.
Program.prototype.decrqlp =
Program.prototype.req_mouse_pos =
Program.prototype.reqmp =
Program.prototype.requestLocatorPosition = function(param, callback) {
// See also:
// get_mouse / getm / Gm
// mouse_info / minfo / Mi
// Correct for tput?
if (this.has('req_mouse_pos')) {
var code = this.tput.req_mouse_pos(param);
return this.response('locator-position', code, callback);
}
return this.response('locator-position',
'\x1b[' + (param || '') + '\'|', callback);
};
// CSI P m SP }
// Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
// NOTE: xterm doesn't enable this code by default.
Program.prototype.decic =
Program.prototype.insertColumns = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + ' }');
};
// CSI P m SP ~
// Delete P s Column(s) (default = 1) (DECDC), VT420 and up
// NOTE: xterm doesn't enable this code by default.
Program.prototype.decdc =
Program.prototype.deleteColumns = function() {
return this._write('\x1b[' + slice.call(arguments).join(';') + ' ~');
};
Program.prototype.out = function(name) {
var args = Array.prototype.slice.call(arguments, 1);
this.ret = true;
var out = this[name].apply(this, args);
this.ret = false;
return out;
};
Program.prototype.sigtstp = function(callback) {
var resume = this.pause();
process.once('SIGCONT', function() {
resume();
if (callback) callback();
});
process.kill(process.pid, 'SIGTSTP');
};
Program.prototype.pause = function(callback) {
var self = this
, isAlt = this.isAlt
, mouseEnabled = this.mouseEnabled;
this.lsaveCursor('pause');
//this.csr(0, screen.height - 1);
if (isAlt) this.normalBuffer();
this.showCursor();
if (mouseEnabled) this.disableMouse();
var write = this.output.write;
this.output.write = function() {};
if (this.input.setRawMode) {
this.input.setRawMode(false);
}
this.input.pause();
return this._resume = function() {
delete self._resume;
if (self.input.setRawMode) {
self.input.setRawMode(true);
}
self.input.resume();
self.output.write = write;
if (isAlt) self.alternateBuffer();
//self.csr(0, screen.height - 1);
if (mouseEnabled) self.enableMouse();
self.lrestoreCursor('pause', true);
if (callback) callback();
};
};
Program.prototype.resume = function() {
if (this._resume) return this._resume();
};
/**
* Helpers
*/
// We could do this easier by just manipulating the _events object, or for
// older versions of node, manipulating the array returned by listeners(), but
// neither of these methods are guaranteed to work in future versions of node.
function unshiftEvent(obj, event, listener) {
var listeners;
if (obj.listeners) {
listeners = obj.listeners(event);
obj.removeAllListeners(event);
obj.on(event, listener);
listeners.forEach(function(listener) {
obj.on(event, listener);
});
} else {
if (obj == process) process.on(event,listener);
}
}
function merge(out) {
slice.call(arguments, 1).forEach(function(obj) {
Object.keys(obj).forEach(function(key) {
out[key] = obj[key];
});
});
return out;
}
/**
* Expose
*/
module.exports = Program;
};
BundleModuleCode['term/tput']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors (MIT License)
** $REVESIO: 1.1.5
**
** $INFO:
*
* tput.js - parse and compile terminfo caps to javascript.
* Modification: Embedded file support.
*
* $ENDINFO
*/
// Resources:
// $ man term
// $ man terminfo
// http://invisible-island.net/ncurses/man/term.5.html
// https://en.wikipedia.org/wiki/Terminfo
// Todo:
// - xterm's XT (set-title capability?) value should
// be true (at least tmux thinks it should).
// It's not parsed as true. Investigate.
// - Possibly switch to other method of finding the
// extended data string table: i += h.symOffsetCount * 2;
/**
* Modules
*/
var assert = Require('assert')
, path = Require('path')
, fs = Require('fs')
, cp = Require('child_process');
/**
* Tput
*/
FileEmbedd('term/def/xterm');
FileEmbedd('term/def/windows-ansi');
function Tput(options) {
if (!(this instanceof Tput)) {
return new Tput(options);
}
options = options || {};
if (typeof options === 'string') {
options = { terminal: options };
}
this.options = options;
this.terminal = options.terminal
|| options.term
|| process.env.TERM
|| (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
this.terminal = this.terminal.toLowerCase();
this.debug = options.debug;
this.padding = options.padding;
this.extended = options.extended;
this.printf = options.printf;
this.termcap = options.termcap;
this.error = null;
this.terminfoPrefix = options.terminfoPrefix;
this.terminfoFile = options.terminfoFile;
this.termcapFile = options.termcapFile;
if (options.terminal || options.term) {
this.setup();
}
}
Tput.prototype.setup = function() {
this.error = null;
try {
if (this.termcap) {
try {
this.injectTermcap();
} catch (e) {
if (this.debug) throw e;
this.error = new Error('Termcap parse error.');
this._useInternalCap(this.terminal);
}
} else {
try {
this.injectTerminfo();
} catch (e) {
if (this.debug) throw e;
this.error = new Error('Terminfo parse error.');
this._useInternalInfo(this.terminal);
}
}
} catch (e) {
// If there was an error, fallback
// to an internally stored terminfo/cap.
if (this.debug) throw e;
this.error = new Error('Terminfo not found.');
this._useXtermInfo();
}
};
Tput.prototype.term = function(is) {
return this.terminal.indexOf(is) === 0;
};
Tput.prototype._debug = function() {
if (!this.debug) return;
return console.log.apply(console, arguments);
};
/**
* Fallback
*/
Tput.prototype._useVt102Cap = function() {
return this.injectTermcap('vt102');
};
Tput.prototype._useXtermCap = function() {
return this.injectTermcap('term/def/xterm.termcap');
};
Tput.prototype._useXtermInfo = function() {
return this.injectTerminfo('term/def/xterm');
};
Tput.prototype._useInternalInfo = function(name) {
name = path.basename(name);
return this.injectTerminfo('term/def/' + name);
};
Tput.prototype._useInternalCap = function(name) {
name = path.basename(name);
return this.injectTermcap('term/def/' + name + '.termcap');
};
/**
* Terminfo
*/
Tput.ipaths = [
__dirname+'/..',
process.env.TERMINFO || '',
(process.env.TERMINFO_DIRS || '').split(':'),
(process.env.HOME || '') + '/.terminfo',
'/usr/share/terminfo',
'/usr/share/lib/terminfo',
'/usr/lib/terminfo',
'/usr/local/share/terminfo',
'/usr/local/share/lib/terminfo',
'/usr/local/lib/terminfo',
'/usr/local/ncurses/lib/terminfo',
'/lib/terminfo'
];
Tput.prototype.readTerminfo = function(term) {
var data
, file
, info;
term = term || this.terminal;
try {data = FileEmbedded(term);} catch (e) {};
if (!data) {
file = path.normalize(this._prefix(term));
console.log('Reading '+file);
data = fs.readFileSync(file);
} else file=term;
info = this.parseTerminfo(data, file);
if (this.debug) {
this._terminfo = info;
}
return info;
};
Tput._prefix =
Tput.prototype._prefix = function(term) {
// If we have a terminfoFile, or our
// term looks like a filename, use it.
if (term) {
if (term.indexOf(path.sep)==0) {
return term;
}
if (this.terminfoFile) {
return this.terminfoFile;
}
}
var paths = Tput.ipaths.slice()
, file;
if (this.terminfoPrefix) {
paths.unshift(this.terminfoPrefix);
}
// Try exact matches.
file = this._tprefix(paths, term);
if (file) return file;
// Try similar matches.
file = this._tprefix(paths, term, true);
if (file) return file;
// Not found.
throw new Error('Terminfo directory not found.');
};
Tput._tprefix =
Tput.prototype._tprefix = function(prefix, term, soft) {
if (!prefix) return;
var file
, dir
, i
, sdiff
, sfile
, list;
try {
file=prefix+'/'+term;
fs.statSync(file);
return file;
} catch (e) {
;
}
if (Array.isArray(prefix)) {
for (i = 0; i < prefix.length; i++) {
file = this._tprefix(prefix[i], term, soft);
if (file) return file;
}
return;
}
var find = function(word) {
var file, ch;
file = path.resolve(prefix, word[0]);
try {
fs.statSync(file);
return file;
} catch (e) {
;
}
ch = word[0].charCodeAt(0).toString(16);
if (ch.length < 2) ch = '0' + ch;
file = path.resolve(prefix, ch);
try {
fs.statSync(file);
return file;
} catch (e) {
;
}
};
if (!term) {
// Make sure the directory's sub-directories
// are all one-letter, or hex digits.
// return find('x') ? prefix : null;
try {
dir = fs.readdirSync(prefix).filter(function(file) {
return file.length !== 1 && !/^[0-9a-fA-F]{2}$/.test(file);
});
if (!dir.length) {
return prefix;
}
} catch (e) {
;
}
return;
}
term = path.basename(term);
dir = find(term);
if (!dir) return;
if (soft) {
try {
list = fs.readdirSync(dir);
} catch (e) {
return;
}
list.forEach(function(file) {
if (file.indexOf(term) === 0) {
var diff = file.length - term.length;
if (!sfile || diff < sdiff) {
sdiff = diff;
sfile = file;
}
}
});
return sfile && (soft || sdiff === 0)
? path.resolve(dir, sfile)
: null;
}
file = path.resolve(dir, term);
try {
fs.statSync(file);
return file;
} catch (e) {
;
}
};
/**
* Terminfo Parser
* All shorts are little-endian
*/
Tput.prototype.parseTerminfo = function(data, file) {
var info = {}
, extended
, l = data.length
, i = 0
, v
, o;
var h = info.header = {
dataSize: data.length,
headerSize: 12,
magicNumber: (data[1] << 8) | data[0],
namesSize: (data[3] << 8) | data[2],
boolCount: (data[5] << 8) | data[4],
numCount: (data[7] << 8) | data[6],
strCount: (data[9] << 8) | data[8],
strTableSize: (data[11] << 8) | data[10]
};
console.log('parseTerminfo '+file+':'+data.length)
h.total = h.headerSize
+ h.namesSize
+ h.boolCount
+ h.numCount * 2
+ h.strCount * 2
+ h.strTableSize;
i += h.headerSize;
// Names Section
var names = data.toString('ascii', i, i + h.namesSize - 1)
, parts = names.split('|')
, name = parts[0]
, desc = parts.pop();
info.name = name;
info.names = parts;
info.desc = desc;
info.dir = path.resolve(file, '..', '..');
info.file = file;
i += h.namesSize - 1;
// Names is nul-terminated.
assert.equal(data[i], 0);
i++;
// Booleans Section
// One byte for each flag
// Same order as <term.h>
info.bools = {};
l = i + h.boolCount;
o = 0;
for (; i < l; i++) {
v = Tput.bools[o++];
info.bools[v] = data[i] === 1;
}
// Null byte in between to make sure numbers begin on an even byte.
if (i % 2) {
assert.equal(data[i], 0);
i++;
}
// Numbers Section
info.numbers = {};
l = i + h.numCount * 2;
o = 0;
for (; i < l; i += 2) {
v = Tput.numbers[o++];
if (data[i + 1] === 0377 && data[i] === 0377) {
info.numbers[v] = -1;
} else {
info.numbers[v] = (data[i + 1] << 8) | data[i];
}
}
// Strings Section
info.strings = {};
l = i + h.strCount * 2;
o = 0;
for (; i < l; i += 2) {
v = Tput.strings[o++];
if (data[i + 1] === 0377 && data[i] === 0377) {
info.strings[v] = -1;
} else {
info.strings[v] = (data[i + 1] << 8) | data[i];
}
}
// String Table
Object.keys(info.strings).forEach(function(key) {
if (info.strings[key] === -1) {
delete info.strings[key];
return;
}
// Workaround: fix an odd bug in the screen-256color terminfo where it tries
// to set -1, but it appears to have {0xfe, 0xff} instead of {0xff, 0xff}.
// TODO: Possibly handle errors gracefully below, as well as in the
// extended info. Also possibly do: `if (info.strings[key] >= data.length)`.
if (info.strings[key] === 65534) {
delete info.strings[key];
return;
}
var s = i + info.strings[key]
, j = s;
while (data[j]) j++;
assert(j < data.length);
info.strings[key] = data.toString('ascii', s, j);
});
// Extended Header
if (this.extended !== false) {
i--;
i += h.strTableSize;
if (i % 2) {
assert.equal(data[i], 0);
i++;
}
l = data.length;
if (i < l - 1) {
try {
extended = this.parseExtended(data.slice(i));
} catch (e) {
if (this.debug) {
throw e;
}
return info;
}
info.header.extended = extended.header;
['bools', 'numbers', 'strings'].forEach(function(key) {
merge(info[key], extended[key]);
});
}
}
return info;
};
/**
* Extended Parsing
*/
// Some data to help understand:
// For xterm, non-extended header:
// { dataSize: 3270,
// headerSize: 12,
// magicNumber: 282,
// namesSize: 48,
// boolCount: 38,
// numCount: 15,
// strCount: 413,
// strTableSize: 1388,
// total: 2342 }
// For xterm, header:
// Offset: 2342
// { header:
// { dataSize: 928,
// headerSize: 10,
// boolCount: 2,
// numCount: 1,
// strCount: 57,
// strTableSize: 117,
// lastStrTableOffset: 680,
// total: 245 },
// For xterm, layout:
// { header: '0 - 10', // length: 10
// bools: '10 - 12', // length: 2
// numbers: '12 - 14', // length: 2
// strings: '14 - 128', // length: 114 (57 short)
// symoffsets: '128 - 248', // length: 120 (60 short)
// stringtable: '248 - 612', // length: 364
// sym: '612 - 928' } // length: 316
//
// How lastStrTableOffset works:
// data.length - h.lastStrTableOffset === 248
// (sym-offset end, string-table start)
// 364 + 316 === 680 (lastStrTableOffset)
// How strTableSize works:
// h.strCount + [symOffsetCount] === h.strTableSize
// 57 + 60 === 117 (strTableSize)
// symOffsetCount doesn't actually exist in the header. it's just implied.
// Getting the number of sym offsets:
// h.symOffsetCount = h.strTableSize - h.strCount;
// h.symOffsetSize = (h.strTableSize - h.strCount) * 2;
Tput.prototype.parseExtended = function(data) {
var info = {}
, l = data.length
, i = 0;
var h = info.header = {
dataSize: data.length,
headerSize: 10,
boolCount: (data[i + 1] << 8) | data[i + 0],
numCount: (data[i + 3] << 8) | data[i + 2],
strCount: (data[i + 5] << 8) | data[i + 4],
strTableSize: (data[i + 7] << 8) | data[i + 6],
lastStrTableOffset: (data[i + 9] << 8) | data[i + 8]
};
// h.symOffsetCount = h.strTableSize - h.strCount;
h.total = h.headerSize
+ h.boolCount
+ h.numCount * 2
+ h.strCount * 2
+ h.strTableSize;
i += h.headerSize;
// Booleans Section
// One byte for each flag
var _bools = [];
l = i + h.boolCount;
for (; i < l; i++) {
_bools.push(data[i] === 1);
}
// Null byte in between to make sure numbers begin on an even byte.
if (i % 2) {
assert.equal(data[i], 0);
i++;
}
// Numbers Section
var _numbers = [];
l = i + h.numCount * 2;
for (; i < l; i += 2) {
if (data[i + 1] === 0377 && data[i] === 0377) {
_numbers.push(-1);
} else {
_numbers.push((data[i + 1] << 8) | data[i]);
}
}
// Strings Section
var _strings = [];
l = i + h.strCount * 2;
for (; i < l; i += 2) {
if (data[i + 1] === 0377 && data[i] === 0377) {
_strings.push(-1);
} else {
_strings.push((data[i + 1] << 8) | data[i]);
}
}
// Pass over the sym offsets and get to the string table.
i = data.length - h.lastStrTableOffset;
// Might be better to do this instead if the file has trailing bytes:
// i += h.symOffsetCount * 2;
// String Table
var high = 0;
_strings.forEach(function(offset, k) {
if (offset === -1) {
_strings[k] = '';
return;
}
var s = i + offset
, j = s;
while (data[j]) j++;
assert(j < data.length);
// Find out where the string table ends by
// getting the highest string length.
if (high < j - i) {
high = j - i;
}
_strings[k] = data.toString('ascii', s, j);
});
// Symbol Table
// Add one to the highest string length because we didn't count \0.
i += high + 1;
l = data.length;
var sym = []
, j;
for (; i < l; i++) {
j = i;
while (data[j]) j++;
sym.push(data.toString('ascii', i, j));
i = j;
}
// Identify by name
j = 0;
info.bools = {};
_bools.forEach(function(bool) {
info.bools[sym[j++]] = bool;
});
info.numbers = {};
_numbers.forEach(function(number) {
info.numbers[sym[j++]] = number;
});
info.strings = {};
_strings.forEach(function(string) {
info.strings[sym[j++]] = string;
});
// Should be the very last bit of data.
assert.equal(i, data.length);
return info;
};
Tput.prototype.compileTerminfo = function(term) {
return this.compile(this.readTerminfo(term));
};
Tput.prototype.injectTerminfo = function(term) {
return this.inject(this.compileTerminfo(term));
};
/**
* Compiler - terminfo cap->javascript
*/
Tput.prototype.compile = function(info) {
var self = this;
if (!info) {
throw new Error('Terminal not found.');
}
this.detectFeatures(info);
this._debug(info);
info.all = {};
info.methods = {};
['bools', 'numbers', 'strings'].forEach(function(type) {
Object.keys(info[type]).forEach(function(key) {
info.all[key] = info[type][key];
info.methods[key] = self._compile(info, key, info.all[key]);
});
});
Tput.bools.forEach(function(key) {
if (info.methods[key] == null) info.methods[key] = false;
});
Tput.numbers.forEach(function(key) {
if (info.methods[key] == null) info.methods[key] = -1;
});
Tput.strings.forEach(function(key) {
if (!info.methods[key]) info.methods[key] = noop;
});
Object.keys(info.methods).forEach(function(key) {
if (!Tput.alias[key]) return;
Tput.alias[key].forEach(function(alias) {
info.methods[alias] = info.methods[key];
});
// Could just use:
// Object.keys(Tput.aliasMap).forEach(function(key) {
// info.methods[key] = info.methods[Tput.aliasMap[key]];
// });
});
return info;
};
Tput.prototype.inject = function(info) {
var self = this
, methods = info.methods || info;
Object.keys(methods).forEach(function(key) {
if (typeof methods[key] !== 'function') {
self[key] = methods[key];
return;
}
self[key] = function() {
var args = Array.prototype.slice.call(arguments);
return methods[key].call(self, args);
};
});
this.info = info;
this.all = info.all;
this.methods = info.methods;
this.bools = info.bools;
this.numbers = info.numbers;
this.strings = info.strings;
if (!~info.names.indexOf(this.terminal)) {
this.terminal = info.name;
}
this.features = info.features;
Object.keys(info.features).forEach(function(key) {
if (key === 'padding') {
if (!info.features.padding && self.options.padding !== true) {
self.padding = false;
}
return;
}
self[key] = info.features[key];
});
};
// See:
// ~/ncurses/ncurses/tinfo/lib_tparm.c
// ~/ncurses/ncurses/tinfo/comp_scan.c
Tput.prototype._compile = function(info, key, str) {
var v;
this._debug('Compiling %s: %s', key, JSON.stringify(str));
switch (typeof str) {
case 'boolean':
return str;
case 'number':
return str;
case 'string':
break;
default:
return noop;
}
if (!str) {
return noop;
}
// See:
// ~/ncurses/progs/tput.c - tput() - L149
// ~/ncurses/progs/tset.c - set_init() - L992
if (key === 'init_file' || key === 'reset_file') {
try {
str = fs.readFileSync(str, 'utf8');
if (this.debug) {
v = ('return ' + JSON.stringify(str) + ';')
.replace(/\x1b/g, '\\x1b')
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n');
process.stdout.write(v + '\n');
}
return function() { return str; };
} catch (e) {
return noop;
}
}
var tkey = info.name + '.' + key
, header = 'var v, dyn = {}, stat = {}, stack = [], out = [];'
, footer = ';return out.join("");'
, code = header
, val = str
, buff = ''
, cap
, ch
, fi
, then
, els
, end;
function read(regex, no) {
cap = regex.exec(val);
if (!cap) return;
val = val.substring(cap[0].length);
ch = cap[1];
if (!no) clear();
return cap;
}
function stmt(c) {
if (code[code.length - 1] === ',') {
code = code.slice(0, -1);
}
code += c;
}
function expr(c) {
code += c + ',';
}
function echo(c) {
if (c === '""') return;
expr('out.push(' + c + ')');
}
function print(c) {
buff += c;
}
function clear() {
if (buff) {
echo(JSON.stringify(buff).replace(/\\u00([0-9a-fA-F]{2})/g, '\\x$1'));
buff = '';
}
}
while (val) {
// Ignore newlines
if (read(/^\n /, true)) {
continue;
}
// '^A' -> ^A
if (read(/^\^(.)/i, true)) {
if (!(ch >= ' ' && ch <= '~')) {
this._debug('%s: bad caret char.', tkey);
// NOTE: ncurses appears to simply
// continue in this situation, but
// I could be wrong.
print(cap[0]);
continue;
}
if (ch === '?') {
ch = '\x7f';
} else {
ch = ch.charCodeAt(0) & 31;
if (ch === 0) ch = 128;
ch = String.fromCharCode(ch);
}
print(ch);
continue;
}
// 3 octal digits -> character
if (read(/^\\([0-7]{3})/, true)) {
print(String.fromCharCode(parseInt(ch, 8)));
continue;
}
// '\e' -> ^[
// '\n' -> \n
// '\r' -> \r
// '\0' -> \200 (special case)
if (read(/^\\([eEnlrtbfs\^\\,:0]|.)/, true)) {
switch (ch) {
case 'e':
case 'E':
ch = '\x1b';
break;
case 'n':
ch = '\n';
break;
case 'l':
ch = '\x85';
break;
case 'r':
ch = '\r';
break;
case 't':
ch = '\t';
break;
case 'b':
ch = '\x08';
break;
case 'f':
ch = '\x0c';
break;
case 's':
ch = ' ';
break;
case '^':
ch = '^';
break;
case '\\':
ch = '\\';
break;
case ',':
ch = ',';
break;
case ':':
ch = ':';
break;
case '0':
ch = '\200';
break;
case 'a':
ch = '\x07';
break;
default:
this._debug('%s: bad backslash char.', tkey);
ch = cap[0];
break;
}
print(ch);
continue;
}
// $<5> -> padding
// e.g. flash_screen: '\u001b[?5h$<100/>\u001b[?5l',
if (read(/^\$<(\d+)([*\/]{0,2})>/, true)) {
if (this.padding) print(cap[0]);
continue;
}
// %% outputs `%'
if (read(/^%%/, true)) {
print('%');
continue;
}
// %[[:]flags][width[.precision]][doxXs]
// as in printf, flags are [-+#] and space. Use a `:' to allow the
// next character to be a `-' flag, avoiding interpreting "%-" as an
// operator.
// %c print pop() like %c in printf
// Example from screen terminfo:
// S0: "\u001b(%p1%c"
// %d print pop()
// "Print (e.g., "%d") is a special case."
// %s print pop() like %s in printf
if (read(/^%((?::-|[+# ]){1,4})?(\d+(?:\.\d+)?)?([doxXsc])/)) {
if (this.printf || cap[1] || cap[2] || ~'oxX'.indexOf(cap[3])) {
echo('sprintf("'+ cap[0].replace(':-', '-') + '", stack.pop())');
} else if (cap[3] === 'c') {
echo('(v = stack.pop(), isFinite(v) '
+ '? String.fromCharCode(v || 0200) : "")');
} else {
echo('stack.pop()');
}
continue;
}
// %p[1-9]
// push i'th parameter
if (read(/^%p([1-9])/)) {
expr('(stack.push(v = params[' + (ch - 1) + ']), v)');
continue;
}
// %P[a-z]
// set dynamic variable [a-z] to pop()
if (read(/^%P([a-z])/)) {
expr('dyn.' + ch + ' = stack.pop()');
continue;
}
// %g[a-z]
// get dynamic variable [a-z] and push it
if (read(/^%g([a-z])/)) {
expr('(stack.push(dyn.' + ch + '), dyn.' + ch + ')');
continue;
}
// %P[A-Z]
// set static variable [a-z] to pop()
if (read(/^%P([A-Z])/)) {
expr('stat.' + ch + ' = stack.pop()');
continue;
}
// %g[A-Z]
// get static variable [a-z] and push it
// The terms "static" and "dynamic" are misleading. Historically,
// these are simply two different sets of variables, whose values are
// not reset between calls to tparm. However, that fact is not
// documented in other implementations. Relying on it will adversely
// impact portability to other implementations.
if (read(/^%g([A-Z])/)) {
expr('(stack.push(v = stat.' + ch + '), v)');
continue;
}
// %'c' char constant c
// NOTE: These are stored as c chars, exemplified by:
// cursor_address: "\u001b=%p1%' '%+%c%p2%' '%+%c"
if (read(/^%'(.)'/)) {
expr('(stack.push(v = ' + ch.charCodeAt(0) + '), v)');
continue;
}
// %{nn}
// integer constant nn
if (read(/^%\{(\d+)\}/)) {
expr('(stack.push(v = ' + ch + '), v)');
continue;
}
// %l push strlen(pop)
if (read(/^%l/)) {
expr('(stack.push(v = (stack.pop() || "").length || 0), v)');
continue;
}
// %+ %- %* %/ %m
// arithmetic (%m is mod): push(pop() op pop())
// %& %| %^
// bit operations (AND, OR and exclusive-OR): push(pop() op pop())
// %= %> %<
// logical operations: push(pop() op pop())
if (read(/^%([+\-*\/m&|\^=><])/)) {
if (ch === '=') ch = '===';
else if (ch === 'm') ch = '%';
expr('(v = stack.pop(),'
+ ' stack.push(v = (stack.pop() ' + ch + ' v) || 0),'
+ ' v)');
continue;
}
// %A, %O
// logical AND and OR operations (for conditionals)
if (read(/^%([AO])/)) {
// Are we supposed to store the result on the stack?
expr('(stack.push(v = (stack.pop() '
+ (ch === 'A' ? '&&' : '||')
+ ' stack.pop())), v)');
continue;
}
// %! %~
// unary operations (logical and bit complement): push(op pop())
if (read(/^%([!~])/)) {
expr('(stack.push(v = ' + ch + 'stack.pop()), v)');
continue;
}
// %i add 1 to first two parameters (for ANSI terminals)
if (read(/^%i/)) {
// Are these supposed to go on the stack in certain situations?
// ncurses doesn't seem to put them on the stack, but xterm.user6
// seems to assume they're on the stack for some reason. Could
// just be a bad terminfo string.
// user6: "\u001b[%i%d;%dR" - possibly a termcap-style string.
// expr('(params[0] |= 0, params[1] |= 0, params[0]++, params[1]++)');
expr('(params[0]++, params[1]++)');
continue;
}
// %? expr %t thenpart %e elsepart %;
// This forms an if-then-else. The %e elsepart is optional. Usually
// the %? expr part pushes a value onto the stack, and %t pops it from
// the stack, testing if it is nonzero (true). If it is zero (false),
// control passes to the %e (else) part.
// It is possible to form else-if's a la Algol 68:
// %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e %;
// where ci are conditions, bi are bodies.
if (read(/^%\?/)) {
end = -1;
stmt(';if (');
continue;
}
if (read(/^%t/)) {
end = -1;
// Technically this is supposed to pop everything off the stack that was
// pushed onto the stack after the if statement, see man terminfo.
// Right now, we don't pop anything off. This could cause compat issues.
// Perhaps implement a "pushed" counter from the time the if statement
// is added, to the time the then statement is added, and pop off
// the appropriate number of elements.
// while (pushed--) expr('stack.pop()');
stmt(') {');
continue;
}
// Terminfo does elseif's like
// this: %?[expr]%t...%e[expr]%t...%;
if (read(/^%e/)) {
fi = val.indexOf('%?');
then = val.indexOf('%t');
els = val.indexOf('%e');
end = val.indexOf('%;');
if (end === -1) end = Infinity;
if (then !== -1 && then < end
&& (fi === -1 || then < fi)
&& (els === -1 || then < els)) {
stmt('} else if (');
} else {
stmt('} else {');
}
continue;
}
if (read(/^%;/)) {
end = null;
stmt('}');
continue;
}
buff += val[0];
val = val.substring(1);
}
// Clear the buffer of any remaining text.
clear();
// Some terminfos (I'm looking at you, atari-color), don't end an if
// statement. It's assumed terminfo will automatically end it for
// them, because they are a bunch of lazy bastards.
if (end != null) {
stmt('}');
}
// Add the footer.
stmt(footer);
// Optimize and cleanup generated code.
v = code.slice(header.length, -footer.length);
if (!v.length) {
code = 'return "";';
} else if (v = /^out\.push\(("(?:[^"]|\\")+")\)$/.exec(v)) {
code = 'return ' + v[1] + ';';
} else {
// Turn `(stack.push(v = params[0]), v),out.push(stack.pop())`
// into `out.push(params[0])`.
code = code.replace(
/\(stack\.push\(v = params\[(\d+)\]\), v\),out\.push\(stack\.pop\(\)\)/g,
'out.push(params[$1])');
// Remove unnecessary variable initializations.
v = code.slice(header.length, -footer.length);
if (!~v.indexOf('v = ')) code = code.replace('v, ', '');
if (!~v.indexOf('dyn')) code = code.replace('dyn = {}, ', '');
if (!~v.indexOf('stat')) code = code.replace('stat = {}, ', '');
if (!~v.indexOf('stack')) code = code.replace('stack = [], ', '');
// Turn `var out = [];out.push("foo"),` into `var out = ["foo"];`.
code = code.replace(
/out = \[\];out\.push\(("(?:[^"]|\\")+")\),/,
'out = [$1];');
}
// Terminfos `wyse350-vb`, and `wy350-w`
// seem to have a few broken strings.
if (str === '\u001b%?') {
code = 'return "\\x1b";';
}
if (this.debug) {
v = code
.replace(/\x1b/g, '\\x1b')
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n');
process.stdout.write(v + '\n');
}
try {
if (this.options.stringify && code.indexOf('return ') === 0) {
return new Function('', code)();
}
return this.printf || ~code.indexOf('sprintf(')
? new Function('sprintf, params', code).bind(null, sprintf)
: new Function('params', code);
} catch (e) {
console.error('');
console.error('Error on %s:', tkey);
console.error(JSON.stringify(str));
console.error('');
console.error(code.replace(/(,|;)/g, '$1\n'));
e.stack = e.stack.replace(/\x1b/g, '\\x1b');
throw e;
}
};
// See: ~/ncurses/ncurses/tinfo/lib_tputs.c
Tput.prototype._print = function(code, print, done) {
var xon = !this.bools.needs_xon_xoff || this.bools.xon_xoff;
print = print || write;
done = done || noop;
if (!this.padding) {
print(code);
return done();
}
var parts = code.split(/(?=\$<[\d.]+[*\/]{0,2}>)/)
, i = 0;
(function next() {
if (i === parts.length) {
return done();
}
var part = parts[i++]
, padding = /^\$<([\d.]+)([*\/]{0,2})>/.exec(part)
, amount
, suffix;
// , affect;
if (!padding) {
print(part);
return next();
}
part = part.substring(padding[0].length);
amount = +padding[1];
suffix = padding[2];
// A `/' suffix indicates that the padding is mandatory and forces a
// delay of the given number of milliseconds even on devices for which xon
// is present to indicate flow control.
if (xon && !~suffix.indexOf('/')) {
print(part);
return next();
}
// A `*' indicates that the padding required is proportional to the number
// of lines affected by the operation, and the amount given is the
// per-affected-unit padding required. (In the case of insert character,
// the factor is still the number of lines affected.) Normally, padding is
// advisory if the device has the xon capability; it is used for cost
// computation but does not trigger delays.
if (~suffix.indexOf('*')) {
// XXX Disable this for now.
amount = amount;
// if (affect = /\x1b\[(\d+)[LM]/.exec(part)) {
// amount *= +affect[1];
// }
// The above is a huge workaround. In reality, we need to compile
// `_print` into the string functions and check the cap name and
// params.
// if (cap === 'insert_line' || cap === 'delete_line') {
// amount *= params[0];
// }
// if (cap === 'clear_screen') {
// amount *= process.stdout.rows;
// }
}
return setTimeout(function() {
print(part);
return next();
}, amount);
})();
};
// A small helper function if we want
// to easily output text with setTimeouts.
Tput.print = function() {
var fake = {
padding: true,
bools: { needs_xon_xoff: true, xon_xoff: false }
};
return Tput.prototype._print.apply(fake, arguments);
};
/**
* Termcap
*/
Tput.cpaths = [
process.env.TERMCAP || '',
(process.env.TERMPATH || '').split(/[: ]/),
(process.env.HOME || '') + '/.termcap',
'/usr/share/misc/termcap',
'/etc/termcap'
];
Tput.prototype.readTermcap = function(term) {
var self = this
, terms
, term_
, root
, paths;
term = term || this.terminal;
// Termcap has a bunch of terminals usually stored in one file/string,
// so we need to find the one containing our desired terminal.
if (~term.indexOf(path.sep) && (terms = this._tryCap(path.resolve(term)))) {
term_ = path.basename(term).split('.')[0];
if (terms[process.env.TERM]) {
term = process.env.TERM;
} else if (terms[term_]) {
term = term_;
} else {
term = Object.keys(terms)[0];
}
} else {
paths = Tput.cpaths.slice();
if (this.termcapFile) {
paths.unshift(this.termcapFile);
}
paths.push(Tput.termcap);
terms = this._tryCap(paths, term);
}
if (!terms) {
throw new Error('Cannot find termcap for: ' + term);
}
root = terms[term];
if (this.debug) {
this._termcap = terms;
}
(function tc(term) {
if (term && term.strings.tc) {
root.inherits = root.inherits || [];
root.inherits.push(term.strings.tc);
var names = terms[term.strings.tc]
? terms[term.strings.tc].names
: [term.strings.tc];
self._debug('%s inherits from %s.',
term.names.join('/'), names.join('/'));
var inherit = tc(terms[term.strings.tc]);
if (inherit) {
['bools', 'numbers', 'strings'].forEach(function(type) {
merge(term[type], inherit[type]);
});
}
}
return term;
})(root);
// Translate termcap names to terminfo-style names.
root = this.translateTermcap(root);
return root;
};
Tput.prototype._tryCap = function(file, term) {
if (!file) return;
var terms
, data
, i;
if (Array.isArray(file)) {
for (i = 0; i < file.length; i++) {
data = this._tryCap(file[i], term);
if (data) return data;
}
return;
}
// If the termcap string starts with `/`,
// ncurses considers it a filename.
data = file[0] === '/'
? tryRead(file)
: file;
if (!data) return;
terms = this.parseTermcap(data, file);
if (term && !terms[term]) {
return;
}
return terms;
};
/**
* Termcap Parser
* http://en.wikipedia.org/wiki/Termcap
* http://www.gnu.org/software
* /termutils/manual/termcap-1.3/html_mono/termcap.html
* http://www.gnu.org/software
* /termutils/manual/termcap-1.3/html_mono/termcap.html#SEC17
* http://tldp.org/HOWTO/Text-Terminal-HOWTO.html#toc16
* man termcap
*/
// Example:
// vt102|dec vt102:\
// :do=^J:co#80:li#24:cl=50\E[;H\E[2J:\
// :le=^H:bs:cm=5\E[%i%d;%dH:nd=2\E[C:up=2\E[A:\
// :ce=3\E[K:cd=50\E[J:so=2\E[7m:se=2\E[m:us=2\E[4m:ue=2\E[m:\
// :md=2\E[1m:mr=2\E[7m:mb=2\E[5m:me=2\E[m:is=\E[1;24r\E[24;1H:\
// :rs=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:ks=\E[?1h\E=:ke=\E[?1l\E>:\
// :ku=\EOA:kd=\EOB:kr=\EOC:kl=\EOD:kb=^H:\
// :ho=\E[H:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:pt:sr=5\EM:vt#3:\
// :sc=\E7:rc=\E8:cs=\E[%i%d;%dr:vs=\E[?7l:ve=\E[?7h:\
// :mi:al=\E[L:dc=\E[P:dl=\E[M:ei=\E[4l:im=\E[4h:
Tput.prototype.parseTermcap = function(data, file) {
var terms = {}
, parts
, term
, entries
, fields
, field
, names
, i
, j
, k;
// remove escaped newlines
data = data.replace(/\\\n[ \t]*/g, '');
// remove comments
data = data.replace(/^#[^\n]+/gm, '');
// split entries
entries = data.trim().split(/\n+/);
for (i = 0; i < entries.length; i++) {
fields = entries[i].split(/:+/);
for (j = 0; j < fields.length; j++) {
field = fields[j].trim();
if (!field) continue;
if (j === 0) {
names = field.split('|');
term = {
name: names[0],
names: names,
desc: names.pop(),
file: ~file.indexOf(path.sep)
? path.resolve(file)
: file,
termcap: true
};
for (k = 0; k < names.length; k++) {
terms[names[k]] = term;
}
term.bools = {};
term.numbers = {};
term.strings = {};
continue;
}
if (~field.indexOf('=')) {
parts = field.split('=');
term.strings[parts[0]] = parts.slice(1).join('=');
} else if (~field.indexOf('#')) {
parts = field.split('#');
term.numbers[parts[0]] = +parts.slice(1).join('#');
} else {
term.bools[field] = true;
}
}
}
return terms;
};
/**
* Termcap Compiler
* man termcap
*/
Tput.prototype.translateTermcap = function(info) {
var self = this
, out = {};
if (!info) return;
this._debug(info);
['name', 'names', 'desc', 'file', 'termcap'].forEach(function(key) {
out[key] = info[key];
});
// Separate aliases for termcap
var map = (function() {
var out = {};
Object.keys(Tput.alias).forEach(function(key) {
var aliases = Tput.alias[key];
out[aliases.termcap] = key;
});
return out;
})();
// Translate termcap cap names to terminfo cap names.
// e.g. `up` -> `cursor_up`
['bools', 'numbers', 'strings'].forEach(function(key) {
out[key] = {};
Object.keys(info[key]).forEach(function(cap) {
if (key === 'strings') {
info.strings[cap] = self._captoinfo(cap, info.strings[cap], 1);
}
if (map[cap]) {
out[key][map[cap]] = info[key][cap];
} else {
// NOTE: Possibly include all termcap names
// in a separate alias.js file. Some are
// missing from the terminfo alias.js file
// which is why we have to do this:
// See: $ man termcap
out[key][cap] = info[key][cap];
}
});
});
return out;
};
Tput.prototype.compileTermcap = function(term) {
return this.compile(this.readTermcap(term));
};
Tput.prototype.injectTermcap = function(term) {
return this.inject(this.compileTermcap(term));
};
/**
* _nc_captoinfo - ported to javascript directly from ncurses.
* Copyright (c) 1998-2009,2010 Free Software Foundation, Inc.
* See: ~/ncurses/ncurses/tinfo/captoinfo.c
*
* Convert a termcap string to terminfo format.
* 'cap' is the relevant terminfo capability index.
* 's' is the string value of the capability.
* 'parameterized' tells what type of translations to do:
* % translations if 1
* pad translations if >=0
*/
Tput.prototype._captoinfo = function(cap, s, parameterized) {
var self = this;
var capstart;
if (parameterized == null) {
parameterized = 0;
}
var MAX_PUSHED = 16
, stack = [];
var stackptr = 0
, onstack = 0
, seenm = 0
, seenn = 0
, seenr = 0
, param = 1
, i = 0
, out = '';
function warn() {
var args = Array.prototype.slice.call(arguments);
args[0] = 'captoinfo: ' + (args[0] || '');
return self._debug.apply(self, args);
}
function isdigit(ch) {
return ch >= '0' && ch <= '9';
}
function isgraph(ch) {
return ch > ' ' && ch <= '~';
}
// convert a character to a terminfo push
function cvtchar(sp) {
var c = '\0'
, len;
var j = i;
switch (sp[j]) {
case '\\':
switch (sp[++j]) {
case '\'':
case '$':
case '\\':
case '%':
c = sp[j];
len = 2;
break;
case '\0':
c = '\\';
len = 1;
break;
case '0':
case '1':
case '2':
case '3':
len = 1;
while (isdigit(sp[j])) {
c = String.fromCharCode(8 * c.charCodeAt(0)
+ (sp[j++].charCodeAt(0) - '0'.charCodeAt(0)));
len++;
}
break;
default:
c = sp[j];
len = 2;
break;
}
break;
case '^':
c = String.fromCharCode(sp[++j].charCodeAt(0) & 0x1f);
len = 2;
break;
default:
c = sp[j];
len = 1;
}
if (isgraph(c) && c !== ',' && c !== '\'' && c !== '\\' && c !== ':') {
out += '%\'';
out += c;
out += '\'';
} else {
out += '%{';
if (c.charCodeAt(0) > 99) {
out += String.fromCharCode(
(c.charCodeAt(0) / 100 | 0) + '0'.charCodeAt(0));
}
if (c.charCodeAt(0) > 9) {
out += String.fromCharCode(
(c.charCodeAt(0) / 10 | 0) % 10 + '0'.charCodeAt(0));
}
out += String.fromCharCode(
c.charCodeAt(0) % 10 + '0'.charCodeAt(0));
out += '}';
}
return len;
}
// push n copies of param on the terminfo stack if not already there
function getparm(parm, n) {
if (seenr) {
if (parm === 1) {
parm = 2;
} else if (parm === 2) {
parm = 1;
}
}
if (onstack === parm) {
if (n > 1) {
warn('string may not be optimal');
out += '%Pa';
while (n--) {
out += '%ga';
}
}
return;
}
if (onstack !== 0) {
push();
}
onstack = parm;
while (n--) {
out += '%p';
out += String.fromCharCode('0'.charCodeAt(0) + parm);
}
if (seenn && parm < 3) {
out += '%{96}%^';
}
if (seenm && parm < 3) {
out += '%{127}%^';
}
}
// push onstack on to the stack
function push() {
if (stackptr >= MAX_PUSHED) {
warn('string too complex to convert');
} else {
stack[stackptr++] = onstack;
}
}
// pop the top of the stack into onstack
function pop() {
if (stackptr === 0) {
if (onstack === 0) {
warn('I\'m confused');
} else {
onstack = 0;
}
} else {
onstack = stack[--stackptr];
}
param++;
}
function see03() {
getparm(param, 1);
out += '%3d';
pop();
}
function invalid() {
out += '%';
i--;
warn('unknown %% code %s (%#x) in %s',
JSON.stringify(s[i]), s[i].charCodeAt(0), cap);
}
// skip the initial padding (if we haven't been told not to)
capstart = null;
if (s == null) s = '';
if (parameterized >= 0 && isdigit(s[i])) {
for (capstart = i;; i++) {
if (!(isdigit(s[i]) || s[i] === '*' || s[i] === '.')) {
break;
}
}
}
while (s[i]) {
switch (s[i]) {
case '%':
i++;
if (parameterized < 1) {
out += '%';
break;
}
switch (s[i++]) {
case '%':
out += '%';
break;
case 'r':
if (seenr++ === 1) {
warn('saw %%r twice in %s', cap);
}
break;
case 'm':
if (seenm++ === 1) {
warn('saw %%m twice in %s', cap);
}
break;
case 'n':
if (seenn++ === 1) {
warn('saw %%n twice in %s', cap);
}
break;
case 'i':
out += '%i';
break;
case '6':
case 'B':
getparm(param, 1);
out += '%{10}%/%{16}%*';
getparm(param, 1);
out += '%{10}%m%+';
break;
case '8':
case 'D':
getparm(param, 2);
out += '%{2}%*%-';
break;
case '>':
getparm(param, 2);
// %?%{x}%>%t%{y}%+%;
out += '%?';
i += cvtchar(s);
out += '%>%t';
i += cvtchar(s);
out += '%+%;';
break;
case 'a':
if ((s[i] === '=' || s[i] === '+' || s[i] === '-'
|| s[i] === '*' || s[i] === '/')
&& (s[i + 1] === 'p' || s[i + 1] === 'c')
&& s[i + 2] !== '\0' && s[i + 2]) {
var l;
l = 2;
if (s[i] !== '=') {
getparm(param, 1);
}
if (s[i + 1] === 'p') {
getparm(param + s[i + 2].charCodeAt(0) - '@'.charCodeAt(0), 1);
if (param !== onstack) {
pop();
param--;
}
l++;
} else {
i += 2, l += cvtchar(s), i -= 2;
}
switch (s[i]) {
case '+':
out += '%+';
break;
case '-':
out += '%-';
break;
case '*':
out += '%*';
break;
case '/':
out += '%/';
break;
case '=':
if (seenr) {
if (param === 1) {
onstack = 2;
} else if (param === 2) {
onstack = 1;
} else {
onstack = param;
}
} else {
onstack = param;
}
break;
}
i += l;
break;
}
getparm(param, 1);
i += cvtchar(s);
out += '%+';
break;
case '+':
getparm(param, 1);
i += cvtchar(s);
out += '%+%c';
pop();
break;
case 's':
// #ifdef WATERLOO
// i += cvtchar(s);
// getparm(param, 1);
// out += '%-';
// #else
getparm(param, 1);
out += '%s';
pop();
// #endif /* WATERLOO */
break;
case '-':
i += cvtchar(s);
getparm(param, 1);
out += '%-%c';
pop();
break;
case '.':
getparm(param, 1);
out += '%c';
pop();
break;
case '0': // not clear any of the historical termcaps did this
if (s[i] === '3') {
see03(); // goto
break;
} else if (s[i] !== '2') {
invalid(); // goto
break;
}
// FALLTHRU
case '2':
getparm(param, 1);
out += '%2d';
pop();
break;
case '3':
see03();
break;
case 'd':
getparm(param, 1);
out += '%d';
pop();
break;
case 'f':
param++;
break;
case 'b':
param--;
break;
case '\\':
out += '%\\';
break;
default:
invalid();
break;
}
break;
// #ifdef REVISIBILIZE
// case '\\':
// out += s[i++];
// out += s[i++];
// break;
// case '\n':
// out += '\\n';
// i++;
// break;
// case '\t':
// out += '\\t';
// i++;
// break;
// case '\r':
// out += '\\r';
// i++;
// break;
// case '\200':
// out += '\\0';
// i++;
// break;
// case '\f':
// out += '\\f';
// i++;
// break;
// case '\b':
// out += '\\b';
// i++;
// break;
// case ' ':
// out += '\\s';
// i++;
// break;
// case '^':
// out += '\\^';
// i++;
// break;
// case ':':
// out += '\\:';
// i++;
// break;
// case ',':
// out += '\\,';
// i++;
// break;
// default:
// if (s[i] === '\033') {
// out += '\\E';
// i++;
// } else if (s[i].charCodeAt(0) > 0 && s[i].charCodeAt(0) < 32) {
// out += '^';
// out += String.fromCharCode(s[i].charCodeAt(0) + '@'.charCodeAt(0));
// i++;
// } else if (s[i].charCodeAt(0) <= 0 || s[i].charCodeAt(0) >= 127) {
// out += '\\';
// out += String.fromCharCode(
// ((s[i].charCodeAt(0) & 0300) >> 6) + '0'.charCodeAt(0));
// out += String.fromCharCode(
// ((s[i].charCodeAt(0) & 0070) >> 3) + '0'.charCodeAt(0));
// out += String.fromCharCode(
// (s[i].charCodeAt(0) & 0007) + '0'.charCodeAt(0));
// i++;
// } else {
// out += s[i++];
// }
// break;
// #else
default:
out += s[i++];
break;
// #endif
}
}
// Now, if we stripped off some leading padding, add it at the end
// of the string as mandatory padding.
if (capstart != null) {
out += '$<';
for (i = capstart;; i++) {
if (isdigit(s[i]) || s[i] === '*' || s[i] === '.') {
out += s[i];
} else {
break;
}
}
out += '/>';
}
if (s !== out) {
warn('Translating %s from %s to %s.',
cap, JSON.stringify(s), JSON.stringify(out));
}
return out;
};
/**
* Compile All Terminfo
*/
Tput.prototype.getAll = function() {
var dir = this._prefix()
, list = asort(fs.readdirSync(dir))
, infos = [];
list.forEach(function(letter) {
var terms = asort(fs.readdirSync(path.resolve(dir, letter)));
infos.push.apply(infos, terms);
});
function asort(obj) {
return obj.sort(function(a, b) {
a = a.toLowerCase().charCodeAt(0);
b = b.toLowerCase().charCodeAt(0);
return a - b;
});
}
return infos;
};
Tput.prototype.compileAll = function(start) {
var self = this
, all = {};
this.getAll().forEach(function(name) {
if (start && name !== start) {
return;
} else {
start = null;
}
all[name] = self.compileTerminfo(name);
});
return all;
};
/**
* Detect Features / Quirks
*/
Tput.prototype.detectFeatures = function(info) {
var data = this.parseACS(info);
info.features = {
unicode: this.detectUnicode(info),
brokenACS: this.detectBrokenACS(info),
PCRomSet: this.detectPCRomSet(info),
magicCookie: this.detectMagicCookie(info),
padding: this.detectPadding(info),
setbuf: this.detectSetbuf(info),
acsc: data.acsc,
acscr: data.acscr
};
return info.features;
};
Tput.prototype.detectUnicode = function() {
if (this.options.forceUnicode != null) {
return this.options.forceUnicode;
}
var LANG = process.env.LANG
+ ':' + process.env.LANGUAGE
+ ':' + process.env.LC_ALL
+ ':' + process.env.LC_CTYPE;
return /utf-?8/i.test(LANG) || (this.GetConsoleCP() === 65001);
};
// For some reason TERM=linux has smacs/rmacs, but it maps to `^[[11m`
// and it does not switch to the DEC SCLD character set. What the hell?
// xterm: \x1b(0, screen: \x0e, linux: \x1b[11m (doesn't work)
// `man console_codes` says:
// 11 select null mapping, set display control flag, reset tog
// gle meta flag (ECMA-48 says "first alternate font").
// See ncurses:
// ~/ncurses/ncurses/base/lib_set_term.c
// ~/ncurses/ncurses/tinfo/lib_acs.c
// ~/ncurses/ncurses/tinfo/tinfo_driver.c
// ~/ncurses/ncurses/tinfo/lib_setup.c
Tput.prototype.detectBrokenACS = function(info) {
// ncurses-compatible env variable.
if (process.env.NCURSES_NO_UTF8_ACS != null) {
return !!+process.env.NCURSES_NO_UTF8_ACS;
}
// If the terminal supports unicode, we don't need ACS.
if (info.numbers.U8 >= 0) {
return !!info.numbers.U8;
}
// The linux console is just broken for some reason.
// Apparently the Linux console does not support ACS,
// but it does support the PC ROM character set.
if (info.name === 'linux') {
return true;
}
// PC alternate charset
// if (acsc.indexOf('+\x10,\x11-\x18.\x190') === 0) {
if (this.detectPCRomSet(info)) {
return true;
}
// screen termcap is bugged?
if (this.termcap
&& info.name.indexOf('screen') === 0
&& process.env.TERMCAP
&& ~process.env.TERMCAP.indexOf('screen')
&& ~process.env.TERMCAP.indexOf('hhII00')) {
if (~info.strings.enter_alt_charset_mode.indexOf('\016')
|| ~info.strings.enter_alt_charset_mode.indexOf('\017')
|| ~info.strings.set_attributes.indexOf('\016')
|| ~info.strings.set_attributes.indexOf('\017')) {
return true;
}
}
return false;
};
// If enter_pc_charset is the same as enter_alt_charset,
// the terminal does not support SCLD as ACS.
// See: ~/ncurses/ncurses/tinfo/lib_acs.c
Tput.prototype.detectPCRomSet = function(info) {
var s = info.strings;
if (s.enter_pc_charset_mode && s.enter_alt_charset_mode
&& s.enter_pc_charset_mode === s.enter_alt_charset_mode
&& s.exit_pc_charset_mode === s.exit_alt_charset_mode) {
return true;
}
return false;
};
Tput.prototype.detectMagicCookie = function() {
return process.env.NCURSES_NO_MAGIC_COOKIE == null;
};
Tput.prototype.detectPadding = function() {
return process.env.NCURSES_NO_PADDING == null;
};
Tput.prototype.detectSetbuf = function() {
return process.env.NCURSES_NO_SETBUF == null;
};
Tput.prototype.parseACS = function(info) {
var data = {};
data.acsc = {};
data.acscr = {};
// Possibly just return an empty object, as done here, instead of
// specifically saying ACS is "broken" above. This would be more
// accurate to ncurses logic. But it doesn't really matter.
if (this.detectPCRomSet(info)) {
return data;
}
// See: ~/ncurses/ncurses/tinfo/lib_acs.c: L208
Object.keys(Tput.acsc).forEach(function(ch) {
var acs_chars = info.strings.acs_chars || ''
, i = acs_chars.indexOf(ch)
, next = acs_chars[i + 1];
if (!next || i === -1 || !Tput.acsc[next]) {
return;
}
data.acsc[ch] = Tput.acsc[next];
data.acscr[Tput.acsc[next]] = ch;
});
return data;
};
Tput.prototype.GetConsoleCP = function() {
var ccp;
if (process.platform !== 'win32') {
return -1;
}
// Allow unicode on all windows consoles for now:
if (+process.env.NCURSES_UNICODE !== 0) {
return 65001;
}
// cp.execSync('chcp 65001', { stdio: 'ignore', timeout: 1500 });
try {
// Produces something like: 'Active code page: 437\n\n'
ccp = cp.execFileSync(process.env.WINDIR + '\\system32\\chcp.com', [], {
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'ascii',
timeout: 1500
});
// ccp = cp.execSync('chcp', {
// stdio: ['ignore', 'pipe', 'ignore'],
// encoding: 'ascii',
// timeout: 1500
// });
} catch (e) {
;
}
ccp = /\d+/.exec(ccp);
if (!ccp) {
return -1;
}
ccp = +ccp[0];
return ccp;
};
/**
* Helpers
*/
function noop() {
return '';
}
noop.unsupported = true;
function merge(a, b) {
Object.keys(b).forEach(function(key) {
a[key] = b[key];
});
return a;
}
function write(data) {
return process.stdout.write(data);
}
function tryRead(file) {
if (Array.isArray(file)) {
for (var i = 0; i < file.length; i++) {
var data = tryRead(file[i]);
if (data) return data;
}
return '';
}
if (!file) return '';
file = path.resolve.apply(path, arguments);
try {
return fs.readFileSync(file, 'utf8');
} catch (e) {
return '';
}
}
/**
* sprintf
* http://www.cplusplus.com/reference/cstdio/printf/
*/
function sprintf(src) {
var params = Array.prototype.slice.call(arguments, 1)
, rule = /%([\-+# ]{1,4})?(\d+(?:\.\d+)?)?([doxXsc])/g
, i = 0;
return src.replace(rule, function(_, flag, width, type) {
var flags = (flag || '').split('')
, param = params[i] != null ? params[i] : ''
, initial = param
// , width = +width
, opt = {}
, pre = '';
i++;
switch (type) {
case 'd': // signed int
param = (+param).toString(10);
break;
case 'o': // unsigned octal
param = (+param).toString(8);
break;
case 'x': // unsigned hex int
param = (+param).toString(16);
break;
case 'X': // unsigned hex int uppercase
param = (+param).toString(16).toUppercase();
break;
case 's': // string
break;
case 'c': // char
param = isFinite(param)
? String.fromCharCode(param || 0200)
: '';
break;
}
flags.forEach(function(flag) {
switch (flag) {
// left-justify by width
case '-':
opt.left = true;
break;
// always precede numbers with their signs
case '+':
opt.signs = true;
break;
// used with o, x, X - value is preceded with 0, 0x, or 0X respectively.
// used with a, A, e, E, f, F, g, G - forces written output to contain
// a decimal point even if no more digits follow
case '#':
opt.hexpoint = true;
break;
// if no sign is going to be written, black space in front of the value
case ' ':
opt.space = true;
break;
}
});
width = +width.split('.')[0];
// Should this be for opt.left too?
// Example: %2.2X - turns 0 into 00
if (width && !opt.left) {
param = param + '';
while (param.length < width) {
param = '0' + param;
}
}
if (opt.signs) {
if (+initial >= 0) {
pre += '+';
}
}
if (opt.space) {
if (!opt.signs && +initial >= 0) {
pre += ' ';
}
}
if (opt.hexpoint) {
switch (type) {
case 'o': // unsigned octal
pre += '0';
break;
case 'x': // unsigned hex int
pre += '0x';
break;
case 'X': // unsigned hex int uppercase
pre += '0X';
break;
}
}
if (opt.left) {
if (width > (pre.length + param.length)) {
width -= pre.length + param.length;
pre = Array(width + 1).join(' ') + pre;
}
}
return pre + param;
});
}
/**
* Aliases
*/
Tput._alias = Require('term/alias');
Tput.alias = {};
['bools', 'numbers', 'strings'].forEach(function(type) {
Object.keys(Tput._alias[type]).forEach(function(key) {
var aliases = Tput._alias[type][key];
Tput.alias[key] = [aliases[0]];
Tput.alias[key].terminfo = aliases[0];
Tput.alias[key].termcap = aliases[1];
});
});
// Bools
Tput.alias.no_esc_ctlc.push('beehive_glitch');
Tput.alias.dest_tabs_magic_smso.push('teleray_glitch');
// Numbers
Tput.alias.micro_col_size.push('micro_char_size');
/**
* Feature Checking
*/
Tput.aliasMap = {};
Object.keys(Tput.alias).forEach(function(key) {
Tput.aliasMap[key] = key;
Tput.alias[key].forEach(function(k) {
Tput.aliasMap[k] = key;
});
});
Tput.prototype.has = function(name) {
name = Tput.aliasMap[name];
var val = this.all[name];
if (!name) return false;
if (typeof val === 'number') {
return val !== -1;
}
return !!val;
};
/**
* Fallback Termcap Entry
*/
Tput.termcap = ''
+ 'vt102|dec vt102:'
+ ':do=^J:co#80:li#24:cl=50\\E[;H\\E[2J:'
+ ':le=^H:bs:cm=5\\E[%i%d;%dH:nd=2\\E[C:up=2\\E[A:'
+ ':ce=3\\E[K:cd=50\\E[J:so=2\\E[7m:se=2\\E[m:us=2\\E[4m:ue=2\\E[m:'
+ ':md=2\\E[1m:mr=2\\E[7m:mb=2\\E[5m:me=2\\E[m:is=\\E[1;24r\\E[24;1H:'
+ ':rs=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:ks=\\E[?1h\\E=:ke=\\E[?1l\\E>:'
+ ':ku=\\EOA:kd=\\EOB:kr=\\EOC:kl=\\EOD:kb=^H:\\\n'
+ ':ho=\\E[H:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:pt:sr=5\\EM:vt#3:'
+ ':sc=\\E7:rc=\\E8:cs=\\E[%i%d;%dr:vs=\\E[?7l:ve=\\E[?7h:'
+ ':mi:al=\\E[L:dc=\\E[P:dl=\\E[M:ei=\\E[4l:im=\\E[4h:';
/**
* Terminfo Data
*/
Tput.bools = [
'auto_left_margin',
'auto_right_margin',
'no_esc_ctlc',
'ceol_standout_glitch',
'eat_newline_glitch',
'erase_overstrike',
'generic_type',
'hard_copy',
'has_meta_key',
'has_status_line',
'insert_null_glitch',
'memory_above',
'memory_below',
'move_insert_mode',
'move_standout_mode',
'over_strike',
'status_line_esc_ok',
'dest_tabs_magic_smso',
'tilde_glitch',
'transparent_underline',
'xon_xoff',
'needs_xon_xoff',
'prtr_silent',
'hard_cursor',
'non_rev_rmcup',
'no_pad_char',
'non_dest_scroll_region',
'can_change',
'back_color_erase',
'hue_lightness_saturation',
'col_addr_glitch',
'cr_cancels_micro_mode',
'has_print_wheel',
'row_addr_glitch',
'semi_auto_right_margin',
'cpi_changes_res',
'lpi_changes_res',
// #ifdef __INTERNAL_CAPS_VISIBLE
'backspaces_with_bs',
'crt_no_scrolling',
'no_correctly_working_cr',
'gnu_has_meta_key',
'linefeed_is_newline',
'has_hardware_tabs',
'return_does_clr_eol'
];
Tput.numbers = [
'columns',
'init_tabs',
'lines',
'lines_of_memory',
'magic_cookie_glitch',
'padding_baud_rate',
'virtual_terminal',
'width_status_line',
'num_labels',
'label_height',
'label_width',
'max_attributes',
'maximum_windows',
'max_colors',
'max_pairs',
'no_color_video',
'buffer_capacity',
'dot_vert_spacing',
'dot_horz_spacing',
'max_micro_address',
'max_micro_jump',
'micro_col_size',
'micro_line_size',
'number_of_pins',
'output_res_char',
'output_res_line',
'output_res_horz_inch',
'output_res_vert_inch',
'print_rate',
'wide_char_size',
'buttons',
'bit_image_entwining',
'bit_image_type',
// #ifdef __INTERNAL_CAPS_VISIBLE
'magic_cookie_glitch_ul',
'carriage_return_delay',
'new_line_delay',
'backspace_delay',
'horizontal_tab_delay',
'number_of_function_keys'
];
Tput.strings = [
'back_tab',
'bell',
'carriage_return',
'change_scroll_region',
'clear_all_tabs',
'clear_screen',
'clr_eol',
'clr_eos',
'column_address',
'command_character',
'cursor_address',
'cursor_down',
'cursor_home',
'cursor_invisible',
'cursor_left',
'cursor_mem_address',
'cursor_normal',
'cursor_right',
'cursor_to_ll',
'cursor_up',
'cursor_visible',
'delete_character',
'delete_line',
'dis_status_line',
'down_half_line',
'enter_alt_charset_mode',
'enter_blink_mode',
'enter_bold_mode',
'enter_ca_mode',
'enter_delete_mode',
'enter_dim_mode',
'enter_insert_mode',
'enter_secure_mode',
'enter_protected_mode',
'enter_reverse_mode',
'enter_standout_mode',
'enter_underline_mode',
'erase_chars',
'exit_alt_charset_mode',
'exit_attribute_mode',
'exit_ca_mode',
'exit_delete_mode',
'exit_insert_mode',
'exit_standout_mode',
'exit_underline_mode',
'flash_screen',
'form_feed',
'from_status_line',
'init_1string',
'init_2string',
'init_3string',
'init_file',
'insert_character',
'insert_line',
'insert_padding',
'key_backspace',
'key_catab',
'key_clear',
'key_ctab',
'key_dc',
'key_dl',
'key_down',
'key_eic',
'key_eol',
'key_eos',
'key_f0',
'key_f1',
'key_f10',
'key_f2',
'key_f3',
'key_f4',
'key_f5',
'key_f6',
'key_f7',
'key_f8',
'key_f9',
'key_home',
'key_ic',
'key_il',
'key_left',
'key_ll',
'key_npage',
'key_ppage',
'key_right',
'key_sf',
'key_sr',
'key_stab',
'key_up',
'keypad_local',
'keypad_xmit',
'lab_f0',
'lab_f1',
'lab_f10',
'lab_f2',
'lab_f3',
'lab_f4',
'lab_f5',
'lab_f6',
'lab_f7',
'lab_f8',
'lab_f9',
'meta_off',
'meta_on',
'newline',
'pad_char',
'parm_dch',
'parm_delete_line',
'parm_down_cursor',
'parm_ich',
'parm_index',
'parm_insert_line',
'parm_left_cursor',
'parm_right_cursor',
'parm_rindex',
'parm_up_cursor',
'pkey_key',
'pkey_local',
'pkey_xmit',
'print_screen',
'prtr_off',
'prtr_on',
'repeat_char',
'reset_1string',
'reset_2string',
'reset_3string',
'reset_file',
'restore_cursor',
'row_address',
'save_cursor',
'scroll_forward',
'scroll_reverse',
'set_attributes',
'set_tab',
'set_window',
'tab',
'to_status_line',
'underline_char',
'up_half_line',
'init_prog',
'key_a1',
'key_a3',
'key_b2',
'key_c1',
'key_c3',
'prtr_non',
'char_padding',
'acs_chars',
'plab_norm',
'key_btab',
'enter_xon_mode',
'exit_xon_mode',
'enter_am_mode',
'exit_am_mode',
'xon_character',
'xoff_character',
'ena_acs',
'label_on',
'label_off',
'key_beg',
'key_cancel',
'key_close',
'key_command',
'key_copy',
'key_create',
'key_end',
'key_enter',
'key_exit',
'key_find',
'key_help',
'key_mark',
'key_message',
'key_move',
'key_next',
'key_open',
'key_options',
'key_previous',
'key_print',
'key_redo',
'key_reference',
'key_refresh',
'key_replace',
'key_restart',
'key_resume',
'key_save',
'key_suspend',
'key_undo',
'key_sbeg',
'key_scancel',
'key_scommand',
'key_scopy',
'key_screate',
'key_sdc',
'key_sdl',
'key_select',
'key_send',
'key_seol',
'key_sexit',
'key_sfind',
'key_shelp',
'key_shome',
'key_sic',
'key_sleft',
'key_smessage',
'key_smove',
'key_snext',
'key_soptions',
'key_sprevious',
'key_sprint',
'key_sredo',
'key_sreplace',
'key_sright',
'key_srsume',
'key_ssave',
'key_ssuspend',
'key_sundo',
'req_for_input',
'key_f11',
'key_f12',
'key_f13',
'key_f14',
'key_f15',
'key_f16',
'key_f17',
'key_f18',
'key_f19',
'key_f20',
'key_f21',
'key_f22',
'key_f23',
'key_f24',
'key_f25',
'key_f26',
'key_f27',
'key_f28',
'key_f29',
'key_f30',
'key_f31',
'key_f32',
'key_f33',
'key_f34',
'key_f35',
'key_f36',
'key_f37',
'key_f38',
'key_f39',
'key_f40',
'key_f41',
'key_f42',
'key_f43',
'key_f44',
'key_f45',
'key_f46',
'key_f47',
'key_f48',
'key_f49',
'key_f50',
'key_f51',
'key_f52',
'key_f53',
'key_f54',
'key_f55',
'key_f56',
'key_f57',
'key_f58',
'key_f59',
'key_f60',
'key_f61',
'key_f62',
'key_f63',
'clr_bol',
'clear_margins',
'set_left_margin',
'set_right_margin',
'label_format',
'set_clock',
'display_clock',
'remove_clock',
'create_window',
'goto_window',
'hangup',
'dial_phone',
'quick_dial',
'tone',
'pulse',
'flash_hook',
'fixed_pause',
'wait_tone',
'user0',
'user1',
'user2',
'user3',
'user4',
'user5',
'user6',
'user7',
'user8',
'user9',
'orig_pair',
'orig_colors',
'initialize_color',
'initialize_pair',
'set_color_pair',
'set_foreground',
'set_background',
'change_char_pitch',
'change_line_pitch',
'change_res_horz',
'change_res_vert',
'define_char',
'enter_doublewide_mode',
'enter_draft_quality',
'enter_italics_mode',
'enter_leftward_mode',
'enter_micro_mode',
'enter_near_letter_quality',
'enter_normal_quality',
'enter_shadow_mode',
'enter_subscript_mode',
'enter_superscript_mode',
'enter_upward_mode',
'exit_doublewide_mode',
'exit_italics_mode',
'exit_leftward_mode',
'exit_micro_mode',
'exit_shadow_mode',
'exit_subscript_mode',
'exit_superscript_mode',
'exit_upward_mode',
'micro_column_address',
'micro_down',
'micro_left',
'micro_right',
'micro_row_address',
'micro_up',
'order_of_pins',
'parm_down_micro',
'parm_left_micro',
'parm_right_micro',
'parm_up_micro',
'select_char_set',
'set_bottom_margin',
'set_bottom_margin_parm',
'set_left_margin_parm',
'set_right_margin_parm',
'set_top_margin',
'set_top_margin_parm',
'start_bit_image',
'start_char_set_def',
'stop_bit_image',
'stop_char_set_def',
'subscript_characters',
'superscript_characters',
'these_cause_cr',
'zero_motion',
'char_set_names',
'key_mouse',
'mouse_info',
'req_mouse_pos',
'get_mouse',
'set_a_foreground',
'set_a_background',
'pkey_plab',
'device_type',
'code_set_init',
'set0_des_seq',
'set1_des_seq',
'set2_des_seq',
'set3_des_seq',
'set_lr_margin',
'set_tb_margin',
'bit_image_repeat',
'bit_image_newline',
'bit_image_carriage_return',
'color_names',
'define_bit_image_region',
'end_bit_image_region',
'set_color_band',
'set_page_length',
'display_pc_char',
'enter_pc_charset_mode',
'exit_pc_charset_mode',
'enter_scancode_mode',
'exit_scancode_mode',
'pc_term_options',
'scancode_escape',
'alt_scancode_esc',
'enter_horizontal_hl_mode',
'enter_left_hl_mode',
'enter_low_hl_mode',
'enter_right_hl_mode',
'enter_top_hl_mode',
'enter_vertical_hl_mode',
'set_a_attributes',
'set_pglen_inch',
// #ifdef __INTERNAL_CAPS_VISIBLE
'termcap_init2',
'termcap_reset',
'linefeed_if_not_lf',
'backspace_if_not_bs',
'other_non_function_keys',
'arrow_key_map',
'acs_ulcorner',
'acs_llcorner',
'acs_urcorner',
'acs_lrcorner',
'acs_ltee',
'acs_rtee',
'acs_btee',
'acs_ttee',
'acs_hline',
'acs_vline',
'acs_plus',
'memory_lock',
'memory_unlock',
'box_chars_1'
];
// DEC Special Character and Line Drawing Set.
// Taken from tty.js.
Tput.acsc = { // (0
'`': '\u25c6', // '◆'
'a': '\u2592', // '▒'
'b': '\u0009', // '\t'
'c': '\u000c', // '\f'
'd': '\u000d', // '\r'
'e': '\u000a', // '\n'
'f': '\u00b0', // '°'
'g': '\u00b1', // '±'
'h': '\u2424', // '\u2424' (NL)
'i': '\u000b', // '\v'
'j': '\u2518', // '┘'
'k': '\u2510', // '┐'
'l': '\u250c', // '┌'
'm': '\u2514', // '└'
'n': '\u253c', // '┼'
'o': '\u23ba', // '⎺'
'p': '\u23bb', // '⎻'
'q': '\u2500', // '─'
'r': '\u23bc', // '⎼'
's': '\u23bd', // '⎽'
't': '\u251c', // '├'
'u': '\u2524', // '┤'
'v': '\u2534', // '┴'
'w': '\u252c', // '┬'
'x': '\u2502', // '│'
'y': '\u2264', // '≤'
'z': '\u2265', // '≥'
'{': '\u03c0', // 'π'
'|': '\u2260', // '≠'
'}': '\u00a3', // '£'
'~': '\u00b7' // '·'
};
// Convert ACS unicode characters to the
// most similar-looking ascii characters.
Tput.utoa = Tput.prototype.utoa = {
'\u25c6': '*', // '◆'
'\u2592': ' ', // '▒'
// '\u0009': '\t', // '\t'
// '\u000c': '\f', // '\f'
// '\u000d': '\r', // '\r'
// '\u000a': '\n', // '\n'
'\u00b0': '*', // '°'
'\u00b1': '+', // '±'
'\u2424': '\n', // '\u2424' (NL)
// '\u000b': '\v', // '\v'
'\u2518': '+', // '┘'
'\u2510': '+', // '┐'
'\u250c': '+', // '┌'
'\u2514': '+', // '└'
'\u253c': '+', // '┼'
'\u23ba': '-', // '⎺'
'\u23bb': '-', // '⎻'
'\u2500': '-', // '─'
'\u23bc': '-', // '⎼'
'\u23bd': '_', // '⎽'
'\u251c': '+', // '├'
'\u2524': '+', // '┤'
'\u2534': '+', // '┴'
'\u252c': '+', // '┬'
'\u2502': '|', // '│'
'\u2264': '<', // '≤'
'\u2265': '>', // '≥'
'\u03c0': '?', // 'π'
'\u2260': '=', // '≠'
'\u00a3': '?', // '£'
'\u00b7': '*' // '·'
};
/**
* Expose
*/
exports = Tput;
exports.sprintf = sprintf;
exports.tryRead = tryRead;
module.exports = exports;
};
BundleModuleCode['term/alias']=function (module,exports){
/**
* alias.js - terminfo/cap aliases for blessed.
* https://github.com/chjj/blessed
* Taken from terminfo(5) man page.
*/
/* jshint maxlen: 300 */
// jscs:disable maximumLineLength
// jscs:disable
var alias = exports;
// These are the boolean capabilities:
alias.bools = {
// Variable Cap- TCap Description
// Booleans name Code
'auto_left_margin': ['bw', 'bw'], // cub1 wraps from col umn 0 to last column
'auto_right_margin': ['am', 'am'], // terminal has auto matic margins
'back_color_erase': ['bce', 'ut'], // screen erased with background color
'can_change': ['ccc', 'cc'], // terminal can re- define existing col ors
'ceol_standout_glitch': ['xhp', 'xs'], // standout not erased by overwriting (hp)
'col_addr_glitch': ['xhpa', 'YA'], // only positive motion for hpa/mhpa caps
'cpi_changes_res': ['cpix', 'YF'], // changing character pitch changes reso lution
'cr_cancels_micro_mode': ['crxm', 'YB'], // using cr turns off micro mode
'dest_tabs_magic_smso': ['xt', 'xt'], // tabs destructive, magic so char (t1061)
'eat_newline_glitch': ['xenl', 'xn'], // newline ignored after 80 cols (con cept)
'erase_overstrike': ['eo', 'eo'], // can erase over strikes with a blank
'generic_type': ['gn', 'gn'], // generic line type
'hard_copy': ['hc', 'hc'], // hardcopy terminal
'hard_cursor': ['chts', 'HC'], // cursor is hard to see
'has_meta_key': ['km', 'km'], // Has a meta key (i.e., sets 8th-bit)
'has_print_wheel': ['daisy', 'YC'], // printer needs opera tor to change char acter set
'has_status_line': ['hs', 'hs'], // has extra status line
'hue_lightness_saturation': ['hls', 'hl'], // terminal uses only HLS color notation (Tektronix)
'insert_null_glitch': ['in', 'in'], // insert mode distin guishes nulls
'lpi_changes_res': ['lpix', 'YG'], // changing line pitch changes resolution
'memory_above': ['da', 'da'], // display may be retained above the screen
'memory_below': ['db', 'db'], // display may be retained below the screen
'move_insert_mode': ['mir', 'mi'], // safe to move while in insert mode
'move_standout_mode': ['msgr', 'ms'], // safe to move while in standout mode
'needs_xon_xoff': ['nxon', 'nx'], // padding will not work, xon/xoff required
'no_esc_ctlc': ['xsb', 'xb'], // beehive (f1=escape, f2=ctrl C)
'no_pad_char': ['npc', 'NP'], // pad character does not exist
'non_dest_scroll_region': ['ndscr', 'ND'], // scrolling region is non-destructive
'non_rev_rmcup': ['nrrmc', 'NR'], // smcup does not reverse rmcup
'over_strike': ['os', 'os'], // terminal can over strike
'prtr_silent': ['mc5i', '5i'], // printer will not echo on screen
'row_addr_glitch': ['xvpa', 'YD'], // only positive motion for vpa/mvpa caps
'semi_auto_right_margin': ['sam', 'YE'], // printing in last column causes cr
'status_line_esc_ok': ['eslok', 'es'], // escape can be used on the status line
'tilde_glitch': ['hz', 'hz'], // cannot print ~'s (hazeltine)
'transparent_underline': ['ul', 'ul'], // underline character overstrikes
'xon_xoff': ['xon', 'xo'] // terminal uses xon/xoff handshaking
};
// These are the numeric capabilities:
alias.numbers = {
// Variable Cap- TCap Description
// Numeric name Code
'columns': ['cols', 'co'], // number of columns in a line
'init_tabs': ['it', 'it'], // tabs initially every # spaces
'label_height': ['lh', 'lh'], // rows in each label
'label_width': ['lw', 'lw'], // columns in each label
'lines': ['lines', 'li'], // number of lines on screen or page
'lines_of_memory': ['lm', 'lm'], // lines of memory if > line. 0 means varies
'magic_cookie_glitch': ['xmc', 'sg'], // number of blank characters left by smso or rmso
'max_attributes': ['ma', 'ma'], // maximum combined attributes terminal can handle
'max_colors': ['colors', 'Co'], // maximum number of colors on screen
'max_pairs': ['pairs', 'pa'], // maximum number of color-pairs on the screen
'maximum_windows': ['wnum', 'MW'], // maximum number of defineable windows
'no_color_video': ['ncv', 'NC'], // video attributes that cannot be used with colors
'num_labels': ['nlab', 'Nl'], // number of labels on screen
'padding_baud_rate': ['pb', 'pb'], // lowest baud rate where padding needed
'virtual_terminal': ['vt', 'vt'], // virtual terminal number (CB/unix)
'width_status_line': ['wsl', 'ws'], // number of columns in status line
// The following numeric capabilities are present in the SVr4.0 term structure, but are not yet documented in the man page. They came in with
// SVr4's printer support.
// Variable Cap- TCap Description
// Numeric name Code
'bit_image_entwining': ['bitwin', 'Yo'], // number of passes for each bit-image row
'bit_image_type': ['bitype', 'Yp'], // type of bit-image device
'buffer_capacity': ['bufsz', 'Ya'], // numbers of bytes buffered before printing
'buttons': ['btns', 'BT'], // number of buttons on mouse
'dot_horz_spacing': ['spinh', 'Yc'], // spacing of dots hor izontally in dots per inch
'dot_vert_spacing': ['spinv', 'Yb'], // spacing of pins ver tically in pins per inch
'max_micro_address': ['maddr', 'Yd'], // maximum value in micro_..._address
'max_micro_jump': ['mjump', 'Ye'], // maximum value in parm_..._micro
'micro_col_size': ['mcs', 'Yf'], // character step size when in micro mode
'micro_line_size': ['mls', 'Yg'], // line step size when in micro mode
'number_of_pins': ['npins', 'Yh'], // numbers of pins in print-head
'output_res_char': ['orc', 'Yi'], // horizontal resolu tion in units per line
'output_res_horz_inch': ['orhi', 'Yk'], // horizontal resolu tion in units per inch
'output_res_line': ['orl', 'Yj'], // vertical resolution in units per line
'output_res_vert_inch': ['orvi', 'Yl'], // vertical resolution in units per inch
'print_rate': ['cps', 'Ym'], // print rate in char acters per second
'wide_char_size': ['widcs', 'Yn'] // character step size when in double wide mode
};
// These are the string capabilities:
alias.strings = {
// Variable Cap- TCap Description
// String name Code
'acs_chars': ['acsc', 'ac'], // graphics charset pairs, based on vt100
'back_tab': ['cbt', 'bt'], // back tab (P)
'bell': ['bel', 'bl'], // audible signal (bell) (P)
'carriage_return': ['cr', 'cr'], // carriage return (P*) (P*)
'change_char_pitch': ['cpi', 'ZA'], // Change number of characters per inch to #1
'change_line_pitch': ['lpi', 'ZB'], // Change number of lines per inch to #1
'change_res_horz': ['chr', 'ZC'], // Change horizontal resolution to #1
'change_res_vert': ['cvr', 'ZD'], // Change vertical res olution to #1
'change_scroll_region': ['csr', 'cs'], // change region to line #1 to line #2 (P)
'char_padding': ['rmp', 'rP'], // like ip but when in insert mode
'clear_all_tabs': ['tbc', 'ct'], // clear all tab stops (P)
'clear_margins': ['mgc', 'MC'], // clear right and left soft margins
'clear_screen': ['clear', 'cl'], // clear screen and home cursor (P*)
'clr_bol': ['el1', 'cb'], // Clear to beginning of line
'clr_eol': ['el', 'ce'], // clear to end of line (P)
'clr_eos': ['ed', 'cd'], // clear to end of screen (P*)
'column_address': ['hpa', 'ch'], // horizontal position #1, absolute (P)
'command_character': ['cmdch', 'CC'], // terminal settable cmd character in prototype !?
'create_window': ['cwin', 'CW'], // define a window #1 from #2,#3 to #4,#5
'cursor_address': ['cup', 'cm'], // move to row #1 col umns #2
'cursor_down': ['cud1', 'do'], // down one line
'cursor_home': ['home', 'ho'], // home cursor (if no cup)
'cursor_invisible': ['civis', 'vi'], // make cursor invisi ble
'cursor_left': ['cub1', 'le'], // move left one space
'cursor_mem_address': ['mrcup', 'CM'], // memory relative cur sor addressing, move to row #1 columns #2
'cursor_normal': ['cnorm', 've'], // make cursor appear normal (undo civis/cvvis)
'cursor_right': ['cuf1', 'nd'], // non-destructive space (move right one space)
'cursor_to_ll': ['ll', 'll'], // last line, first column (if no cup)
'cursor_up': ['cuu1', 'up'], // up one line
'cursor_visible': ['cvvis', 'vs'], // make cursor very visible
'define_char': ['defc', 'ZE'], // Define a character #1, #2 dots wide, descender #3
'delete_character': ['dch1', 'dc'], // delete character (P*)
'delete_line': ['dl1', 'dl'], // delete line (P*)
'dial_phone': ['dial', 'DI'], // dial number #1
'dis_status_line': ['dsl', 'ds'], // disable status line
'display_clock': ['dclk', 'DK'], // display clock
'down_half_line': ['hd', 'hd'], // half a line down
'ena_acs': ['enacs', 'eA'], // enable alternate char set
'enter_alt_charset_mode': ['smacs', 'as'], // start alternate character set (P)
'enter_am_mode': ['smam', 'SA'], // turn on automatic margins
'enter_blink_mode': ['blink', 'mb'], // turn on blinking
'enter_bold_mode': ['bold', 'md'], // turn on bold (extra bright) mode
'enter_ca_mode': ['smcup', 'ti'], // string to start pro grams using cup
'enter_delete_mode': ['smdc', 'dm'], // enter delete mode
'enter_dim_mode': ['dim', 'mh'], // turn on half-bright mode
'enter_doublewide_mode': ['swidm', 'ZF'], // Enter double-wide mode
'enter_draft_quality': ['sdrfq', 'ZG'], // Enter draft-quality mode
'enter_insert_mode': ['smir', 'im'], // enter insert mode
'enter_italics_mode': ['sitm', 'ZH'], // Enter italic mode
'enter_leftward_mode': ['slm', 'ZI'], // Start leftward car riage motion
'enter_micro_mode': ['smicm', 'ZJ'], // Start micro-motion mode
'enter_near_letter_quality': ['snlq', 'ZK'], // Enter NLQ mode
'enter_normal_quality': ['snrmq', 'ZL'], // Enter normal-quality mode
'enter_protected_mode': ['prot', 'mp'], // turn on protected mode
'enter_reverse_mode': ['rev', 'mr'], // turn on reverse video mode
'enter_secure_mode': ['invis', 'mk'], // turn on blank mode (characters invisi ble)
'enter_shadow_mode': ['sshm', 'ZM'], // Enter shadow-print mode
'enter_standout_mode': ['smso', 'so'], // begin standout mode
'enter_subscript_mode': ['ssubm', 'ZN'], // Enter subscript mode
'enter_superscript_mode': ['ssupm', 'ZO'], // Enter superscript mode
'enter_underline_mode': ['smul', 'us'], // begin underline mode
'enter_upward_mode': ['sum', 'ZP'], // Start upward car riage motion
'enter_xon_mode': ['smxon', 'SX'], // turn on xon/xoff handshaking
'erase_chars': ['ech', 'ec'], // erase #1 characters (P)
'exit_alt_charset_mode': ['rmacs', 'ae'], // end alternate char acter set (P)
'exit_am_mode': ['rmam', 'RA'], // turn off automatic margins
'exit_attribute_mode': ['sgr0', 'me'], // turn off all attributes
'exit_ca_mode': ['rmcup', 'te'], // strings to end pro grams using cup
'exit_delete_mode': ['rmdc', 'ed'], // end delete mode
'exit_doublewide_mode': ['rwidm', 'ZQ'], // End double-wide mode
'exit_insert_mode': ['rmir', 'ei'], // exit insert mode
'exit_italics_mode': ['ritm', 'ZR'], // End italic mode
'exit_leftward_mode': ['rlm', 'ZS'], // End left-motion mode
'exit_micro_mode': ['rmicm', 'ZT'], // End micro-motion mode
'exit_shadow_mode': ['rshm', 'ZU'], // End shadow-print mode
'exit_standout_mode': ['rmso', 'se'], // exit standout mode
'exit_subscript_mode': ['rsubm', 'ZV'], // End subscript mode
'exit_superscript_mode': ['rsupm', 'ZW'], // End superscript mode
'exit_underline_mode': ['rmul', 'ue'], // exit underline mode
'exit_upward_mode': ['rum', 'ZX'], // End reverse charac ter motion
'exit_xon_mode': ['rmxon', 'RX'], // turn off xon/xoff handshaking
'fixed_pause': ['pause', 'PA'], // pause for 2-3 sec onds
'flash_hook': ['hook', 'fh'], // flash switch hook
'flash_screen': ['flash', 'vb'], // visible bell (may not move cursor)
'form_feed': ['ff', 'ff'], // hardcopy terminal page eject (P*)
'from_status_line': ['fsl', 'fs'], // return from status line
'goto_window': ['wingo', 'WG'], // go to window #1
'hangup': ['hup', 'HU'], // hang-up phone
'init_1string': ['is1', 'i1'], // initialization string
'init_2string': ['is2', 'is'], // initialization string
'init_3string': ['is3', 'i3'], // initialization string
'init_file': ['if', 'if'], // name of initializa tion file
'init_prog': ['iprog', 'iP'], // path name of program for initialization
'initialize_color': ['initc', 'Ic'], // initialize color #1 to (#2,#3,#4)
'initialize_pair': ['initp', 'Ip'], // Initialize color pair #1 to fg=(#2,#3,#4), bg=(#5,#6,#7)
'insert_character': ['ich1', 'ic'], // insert character (P)
'insert_line': ['il1', 'al'], // insert line (P*)
'insert_padding': ['ip', 'ip'], // insert padding after inserted character
'key_a1': ['ka1', 'K1'], // upper left of keypad
'key_a3': ['ka3', 'K3'], // upper right of key pad
'key_b2': ['kb2', 'K2'], // center of keypad
'key_backspace': ['kbs', 'kb'], // backspace key
'key_beg': ['kbeg', '@1'], // begin key
'key_btab': ['kcbt', 'kB'], // back-tab key
'key_c1': ['kc1', 'K4'], // lower left of keypad
'key_c3': ['kc3', 'K5'], // lower right of key pad
'key_cancel': ['kcan', '@2'], // cancel key
'key_catab': ['ktbc', 'ka'], // clear-all-tabs key
'key_clear': ['kclr', 'kC'], // clear-screen or erase key
'key_close': ['kclo', '@3'], // close key
'key_command': ['kcmd', '@4'], // command key
'key_copy': ['kcpy', '@5'], // copy key
'key_create': ['kcrt', '@6'], // create key
'key_ctab': ['kctab', 'kt'], // clear-tab key
'key_dc': ['kdch1', 'kD'], // delete-character key
'key_dl': ['kdl1', 'kL'], // delete-line key
'key_down': ['kcud1', 'kd'], // down-arrow key
'key_eic': ['krmir', 'kM'], // sent by rmir or smir in insert mode
'key_end': ['kend', '@7'], // end key
'key_enter': ['kent', '@8'], // enter/send key
'key_eol': ['kel', 'kE'], // clear-to-end-of-line key
'key_eos': ['ked', 'kS'], // clear-to-end-of- screen key
'key_exit': ['kext', '@9'], // exit key
'key_f0': ['kf0', 'k0'], // F0 function key
'key_f1': ['kf1', 'k1'], // F1 function key
'key_f10': ['kf10', 'k;'], // F10 function key
'key_f11': ['kf11', 'F1'], // F11 function key
'key_f12': ['kf12', 'F2'], // F12 function key
'key_f13': ['kf13', 'F3'], // F13 function key
'key_f14': ['kf14', 'F4'], // F14 function key
'key_f15': ['kf15', 'F5'], // F15 function key
'key_f16': ['kf16', 'F6'], // F16 function key
'key_f17': ['kf17', 'F7'], // F17 function key
'key_f18': ['kf18', 'F8'], // F18 function key
'key_f19': ['kf19', 'F9'], // F19 function key
'key_f2': ['kf2', 'k2'], // F2 function key
'key_f20': ['kf20', 'FA'], // F20 function key
'key_f21': ['kf21', 'FB'], // F21 function key
'key_f22': ['kf22', 'FC'], // F22 function key
'key_f23': ['kf23', 'FD'], // F23 function key
'key_f24': ['kf24', 'FE'], // F24 function key
'key_f25': ['kf25', 'FF'], // F25 function key
'key_f26': ['kf26', 'FG'], // F26 function key
'key_f27': ['kf27', 'FH'], // F27 function key
'key_f28': ['kf28', 'FI'], // F28 function key
'key_f29': ['kf29', 'FJ'], // F29 function key
'key_f3': ['kf3', 'k3'], // F3 function key
'key_f30': ['kf30', 'FK'], // F30 function key
'key_f31': ['kf31', 'FL'], // F31 function key
'key_f32': ['kf32', 'FM'], // F32 function key
'key_f33': ['kf33', 'FN'], // F33 function key
'key_f34': ['kf34', 'FO'], // F34 function key
'key_f35': ['kf35', 'FP'], // F35 function key
'key_f36': ['kf36', 'FQ'], // F36 function key
'key_f37': ['kf37', 'FR'], // F37 function key
'key_f38': ['kf38', 'FS'], // F38 function key
'key_f39': ['kf39', 'FT'], // F39 function key
'key_f4': ['kf4', 'k4'], // F4 function key
'key_f40': ['kf40', 'FU'], // F40 function key
'key_f41': ['kf41', 'FV'], // F41 function key
'key_f42': ['kf42', 'FW'], // F42 function key
'key_f43': ['kf43', 'FX'], // F43 function key
'key_f44': ['kf44', 'FY'], // F44 function key
'key_f45': ['kf45', 'FZ'], // F45 function key
'key_f46': ['kf46', 'Fa'], // F46 function key
'key_f47': ['kf47', 'Fb'], // F47 function key
'key_f48': ['kf48', 'Fc'], // F48 function key
'key_f49': ['kf49', 'Fd'], // F49 function key
'key_f5': ['kf5', 'k5'], // F5 function key
'key_f50': ['kf50', 'Fe'], // F50 function key
'key_f51': ['kf51', 'Ff'], // F51 function key
'key_f52': ['kf52', 'Fg'], // F52 function key
'key_f53': ['kf53', 'Fh'], // F53 function key
'key_f54': ['kf54', 'Fi'], // F54 function key
'key_f55': ['kf55', 'Fj'], // F55 function key
'key_f56': ['kf56', 'Fk'], // F56 function key
'key_f57': ['kf57', 'Fl'], // F57 function key
'key_f58': ['kf58', 'Fm'], // F58 function key
'key_f59': ['kf59', 'Fn'], // F59 function key
'key_f6': ['kf6', 'k6'], // F6 function key
'key_f60': ['kf60', 'Fo'], // F60 function key
'key_f61': ['kf61', 'Fp'], // F61 function key
'key_f62': ['kf62', 'Fq'], // F62 function key
'key_f63': ['kf63', 'Fr'], // F63 function key
'key_f7': ['kf7', 'k7'], // F7 function key
'key_f8': ['kf8', 'k8'], // F8 function key
'key_f9': ['kf9', 'k9'], // F9 function key
'key_find': ['kfnd', '@0'], // find key
'key_help': ['khlp', '%1'], // help key
'key_home': ['khome', 'kh'], // home key
'key_ic': ['kich1', 'kI'], // insert-character key
'key_il': ['kil1', 'kA'], // insert-line key
'key_left': ['kcub1', 'kl'], // left-arrow key
'key_ll': ['kll', 'kH'], // lower-left key (home down)
'key_mark': ['kmrk', '%2'], // mark key
'key_message': ['kmsg', '%3'], // message key
'key_move': ['kmov', '%4'], // move key
'key_next': ['knxt', '%5'], // next key
'key_npage': ['knp', 'kN'], // next-page key
'key_open': ['kopn', '%6'], // open key
'key_options': ['kopt', '%7'], // options key
'key_ppage': ['kpp', 'kP'], // previous-page key
'key_previous': ['kprv', '%8'], // previous key
'key_print': ['kprt', '%9'], // print key
'key_redo': ['krdo', '%0'], // redo key
'key_reference': ['kref', '&1'], // reference key
'key_refresh': ['krfr', '&2'], // refresh key
'key_replace': ['krpl', '&3'], // replace key
'key_restart': ['krst', '&4'], // restart key
'key_resume': ['kres', '&5'], // resume key
'key_right': ['kcuf1', 'kr'], // right-arrow key
'key_save': ['ksav', '&6'], // save key
'key_sbeg': ['kBEG', '&9'], // shifted begin key
'key_scancel': ['kCAN', '&0'], // shifted cancel key
'key_scommand': ['kCMD', '*1'], // shifted command key
'key_scopy': ['kCPY', '*2'], // shifted copy key
'key_screate': ['kCRT', '*3'], // shifted create key
'key_sdc': ['kDC', '*4'], // shifted delete-char acter key
'key_sdl': ['kDL', '*5'], // shifted delete-line key
'key_select': ['kslt', '*6'], // select key
'key_send': ['kEND', '*7'], // shifted end key
'key_seol': ['kEOL', '*8'], // shifted clear-to- end-of-line key
'key_sexit': ['kEXT', '*9'], // shifted exit key
'key_sf': ['kind', 'kF'], // scroll-forward key
'key_sfind': ['kFND', '*0'], // shifted find key
'key_shelp': ['kHLP', '#1'], // shifted help key
'key_shome': ['kHOM', '#2'], // shifted home key
'key_sic': ['kIC', '#3'], // shifted insert-char acter key
'key_sleft': ['kLFT', '#4'], // shifted left-arrow key
'key_smessage': ['kMSG', '%a'], // shifted message key
'key_smove': ['kMOV', '%b'], // shifted move key
'key_snext': ['kNXT', '%c'], // shifted next key
'key_soptions': ['kOPT', '%d'], // shifted options key
'key_sprevious': ['kPRV', '%e'], // shifted previous key
'key_sprint': ['kPRT', '%f'], // shifted print key
'key_sr': ['kri', 'kR'], // scroll-backward key
'key_sredo': ['kRDO', '%g'], // shifted redo key
'key_sreplace': ['kRPL', '%h'], // shifted replace key
'key_sright': ['kRIT', '%i'], // shifted right-arrow key
'key_srsume': ['kRES', '%j'], // shifted resume key
'key_ssave': ['kSAV', '!1'], // shifted save key
'key_ssuspend': ['kSPD', '!2'], // shifted suspend key
'key_stab': ['khts', 'kT'], // set-tab key
'key_sundo': ['kUND', '!3'], // shifted undo key
'key_suspend': ['kspd', '&7'], // suspend key
'key_undo': ['kund', '&8'], // undo key
'key_up': ['kcuu1', 'ku'], // up-arrow key
'keypad_local': ['rmkx', 'ke'], // leave 'key board_transmit' mode
'keypad_xmit': ['smkx', 'ks'], // enter 'key board_transmit' mode
'lab_f0': ['lf0', 'l0'], // label on function key f0 if not f0
'lab_f1': ['lf1', 'l1'], // label on function key f1 if not f1
'lab_f10': ['lf10', 'la'], // label on function key f10 if not f10
'lab_f2': ['lf2', 'l2'], // label on function key f2 if not f2
'lab_f3': ['lf3', 'l3'], // label on function key f3 if not f3
'lab_f4': ['lf4', 'l4'], // label on function key f4 if not f4
'lab_f5': ['lf5', 'l5'], // label on function key f5 if not f5
'lab_f6': ['lf6', 'l6'], // label on function key f6 if not f6
'lab_f7': ['lf7', 'l7'], // label on function key f7 if not f7
'lab_f8': ['lf8', 'l8'], // label on function key f8 if not f8
'lab_f9': ['lf9', 'l9'], // label on function key f9 if not f9
'label_format': ['fln', 'Lf'], // label format
'label_off': ['rmln', 'LF'], // turn off soft labels
'label_on': ['smln', 'LO'], // turn on soft labels
'meta_off': ['rmm', 'mo'], // turn off meta mode
'meta_on': ['smm', 'mm'], // turn on meta mode (8th-bit on)
'micro_column_address': ['mhpa', 'ZY'], // Like column_address in micro mode
'micro_down': ['mcud1', 'ZZ'], // Like cursor_down in micro mode
'micro_left': ['mcub1', 'Za'], // Like cursor_left in micro mode
'micro_right': ['mcuf1', 'Zb'], // Like cursor_right in micro mode
'micro_row_address': ['mvpa', 'Zc'], // Like row_address #1 in micro mode
'micro_up': ['mcuu1', 'Zd'], // Like cursor_up in micro mode
'newline': ['nel', 'nw'], // newline (behave like cr followed by lf)
'order_of_pins': ['porder', 'Ze'], // Match software bits to print-head pins
'orig_colors': ['oc', 'oc'], // Set all color pairs to the original ones
'orig_pair': ['op', 'op'], // Set default pair to its original value
'pad_char': ['pad', 'pc'], // padding char (instead of null)
'parm_dch': ['dch', 'DC'], // delete #1 characters (P*)
'parm_delete_line': ['dl', 'DL'], // delete #1 lines (P*)
'parm_down_cursor': ['cud', 'DO'], // down #1 lines (P*)
'parm_down_micro': ['mcud', 'Zf'], // Like parm_down_cur sor in micro mode
'parm_ich': ['ich', 'IC'], // insert #1 characters (P*)
'parm_index': ['indn', 'SF'], // scroll forward #1 lines (P)
'parm_insert_line': ['il', 'AL'], // insert #1 lines (P*)
'parm_left_cursor': ['cub', 'LE'], // move #1 characters to the left (P)
'parm_left_micro': ['mcub', 'Zg'], // Like parm_left_cur sor in micro mode
'parm_right_cursor': ['cuf', 'RI'], // move #1 characters to the right (P*)
'parm_right_micro': ['mcuf', 'Zh'], // Like parm_right_cur sor in micro mode
'parm_rindex': ['rin', 'SR'], // scroll back #1 lines (P)
'parm_up_cursor': ['cuu', 'UP'], // up #1 lines (P*)
'parm_up_micro': ['mcuu', 'Zi'], // Like parm_up_cursor in micro mode
'pkey_key': ['pfkey', 'pk'], // program function key #1 to type string #2
'pkey_local': ['pfloc', 'pl'], // program function key #1 to execute string #2
'pkey_xmit': ['pfx', 'px'], // program function key #1 to transmit string #2
'plab_norm': ['pln', 'pn'], // program label #1 to show string #2
'print_screen': ['mc0', 'ps'], // print contents of screen
'prtr_non': ['mc5p', 'pO'], // turn on printer for #1 bytes
'prtr_off': ['mc4', 'pf'], // turn off printer
'prtr_on': ['mc5', 'po'], // turn on printer
'pulse': ['pulse', 'PU'], // select pulse dialing
'quick_dial': ['qdial', 'QD'], // dial number #1 with out checking
'remove_clock': ['rmclk', 'RC'], // remove clock
'repeat_char': ['rep', 'rp'], // repeat char #1 #2 times (P*)
'req_for_input': ['rfi', 'RF'], // send next input char (for ptys)
'reset_1string': ['rs1', 'r1'], // reset string
'reset_2string': ['rs2', 'r2'], // reset string
'reset_3string': ['rs3', 'r3'], // reset string
'reset_file': ['rf', 'rf'], // name of reset file
'restore_cursor': ['rc', 'rc'], // restore cursor to position of last save_cursor
'row_address': ['vpa', 'cv'], // vertical position #1 absolute (P)
'save_cursor': ['sc', 'sc'], // save current cursor position (P)
'scroll_forward': ['ind', 'sf'], // scroll text up (P)
'scroll_reverse': ['ri', 'sr'], // scroll text down (P)
'select_char_set': ['scs', 'Zj'], // Select character set, #1
'set_attributes': ['sgr', 'sa'], // define video attributes #1-#9 (PG9)
'set_background': ['setb', 'Sb'], // Set background color #1
'set_bottom_margin': ['smgb', 'Zk'], // Set bottom margin at current line
'set_bottom_margin_parm': ['smgbp', 'Zl'], // Set bottom margin at line #1 or (if smgtp is not given) #2 lines from bottom
'set_clock': ['sclk', 'SC'], // set clock, #1 hrs #2 mins #3 secs
'set_color_pair': ['scp', 'sp'], // Set current color pair to #1
'set_foreground': ['setf', 'Sf'], // Set foreground color #1
'set_left_margin': ['smgl', 'ML'], // set left soft margin at current col umn. See smgl. (ML is not in BSD termcap).
'set_left_margin_parm': ['smglp', 'Zm'], // Set left (right) margin at column #1
'set_right_margin': ['smgr', 'MR'], // set right soft margin at current column
'set_right_margin_parm': ['smgrp', 'Zn'], // Set right margin at column #1
'set_tab': ['hts', 'st'], // set a tab in every row, current columns
'set_top_margin': ['smgt', 'Zo'], // Set top margin at current line
'set_top_margin_parm': ['smgtp', 'Zp'], // Set top (bottom) margin at row #1
'set_window': ['wind', 'wi'], // current window is lines #1-#2 cols #3-#4
'start_bit_image': ['sbim', 'Zq'], // Start printing bit image graphics
'start_char_set_def': ['scsd', 'Zr'], // Start character set defi nition #1, with #2 charac ters in the set
'stop_bit_image': ['rbim', 'Zs'], // Stop printing bit image graphics
'stop_char_set_def': ['rcsd', 'Zt'], // End definition of charac ter set #1
'subscript_characters': ['subcs', 'Zu'], // List of subscriptable characters
'superscript_characters': ['supcs', 'Zv'], // List of superscriptable characters
'tab': ['ht', 'ta'], // tab to next 8-space hard ware tab stop
'these_cause_cr': ['docr', 'Zw'], // Printing any of these characters causes CR
'to_status_line': ['tsl', 'ts'], // move to status line, col umn #1
'tone': ['tone', 'TO'], // select touch tone dialing
'underline_char': ['uc', 'uc'], // underline char and move past it
'up_half_line': ['hu', 'hu'], // half a line up
'user0': ['u0', 'u0'], // User string #0
'user1': ['u1', 'u1'], // User string #1
'user2': ['u2', 'u2'], // User string #2
'user3': ['u3', 'u3'], // User string #3
'user4': ['u4', 'u4'], // User string #4
'user5': ['u5', 'u5'], // User string #5
'user6': ['u6', 'u6'], // User string #6
'user7': ['u7', 'u7'], // User string #7
'user8': ['u8', 'u8'], // User string #8
'user9': ['u9', 'u9'], // User string #9
'wait_tone': ['wait', 'WA'], // wait for dial-tone
'xoff_character': ['xoffc', 'XF'], // XOFF character
'xon_character': ['xonc', 'XN'], // XON character
'zero_motion': ['zerom', 'Zx'], // No motion for subsequent character
// The following string capabilities are present in the SVr4.0 term structure, but were originally not documented in the man page.
// Variable Cap- TCap Description
// String name Code
'alt_scancode_esc': ['scesa', 'S8'], // Alternate escape for scancode emu lation
'bit_image_carriage_return': ['bicr', 'Yv'], // Move to beginning of same row
'bit_image_newline': ['binel', 'Zz'], // Move to next row of the bit image
'bit_image_repeat': ['birep', 'Xy'], // Repeat bit image cell #1 #2 times
'char_set_names': ['csnm', 'Zy'], // Produce #1'th item from list of char acter set names
'code_set_init': ['csin', 'ci'], // Init sequence for multiple codesets
'color_names': ['colornm', 'Yw'], // Give name for color #1
'define_bit_image_region': ['defbi', 'Yx'], // Define rectan gualar bit image region
'device_type': ['devt', 'dv'], // Indicate lan guage/codeset sup port
'display_pc_char': ['dispc', 'S1'], // Display PC charac ter #1
'end_bit_image_region': ['endbi', 'Yy'], // End a bit-image region
'enter_pc_charset_mode': ['smpch', 'S2'], // Enter PC character display mode
'enter_scancode_mode': ['smsc', 'S4'], // Enter PC scancode mode
'exit_pc_charset_mode': ['rmpch', 'S3'], // Exit PC character display mode
'exit_scancode_mode': ['rmsc', 'S5'], // Exit PC scancode mode
'get_mouse': ['getm', 'Gm'], // Curses should get button events, parameter #1 not documented.
'key_mouse': ['kmous', 'Km'], // Mouse event has occurred
'mouse_info': ['minfo', 'Mi'], // Mouse status information
'pc_term_options': ['pctrm', 'S6'], // PC terminal options
'pkey_plab': ['pfxl', 'xl'], // Program function key #1 to type string #2 and show string #3
'req_mouse_pos': ['reqmp', 'RQ'], // Request mouse position
'scancode_escape': ['scesc', 'S7'], // Escape for scan code emulation
'set0_des_seq': ['s0ds', 's0'], // Shift to codeset 0 (EUC set 0, ASCII)
'set1_des_seq': ['s1ds', 's1'], // Shift to codeset 1
'set2_des_seq': ['s2ds', 's2'], // Shift to codeset 2
'set3_des_seq': ['s3ds', 's3'], // Shift to codeset 3
'set_a_background': ['setab', 'AB'], // Set background color to #1, using ANSI escape
'set_a_foreground': ['setaf', 'AF'], // Set foreground color to #1, using ANSI escape
'set_color_band': ['setcolor', 'Yz'], // Change to ribbon color #1
'set_lr_margin': ['smglr', 'ML'], // Set both left and right margins to #1, #2. (ML is not in BSD term cap).
'set_page_length': ['slines', 'YZ'], // Set page length to #1 lines
'set_tb_margin': ['smgtb', 'MT'], // Sets both top and bottom margins to #1, #2
// The XSI Curses standard added these. They are some post-4.1 versions of System V curses, e.g., Solaris 2.5 and IRIX 6.x. The ncurses termcap
// names for them are invented; according to the XSI Curses standard, they have no termcap names. If your compiled terminfo entries use these,
// they may not be binary-compatible with System V terminfo entries after SVr4.1; beware!
// Variable Cap- TCap Description
// String name Code
'enter_horizontal_hl_mode': ['ehhlm', 'Xh'], // Enter horizontal highlight mode
'enter_left_hl_mode': ['elhlm', 'Xl'], // Enter left highlight mode
'enter_low_hl_mode': ['elohlm', 'Xo'], // Enter low highlight mode
'enter_right_hl_mode': ['erhlm', 'Xr'], // Enter right high light mode
'enter_top_hl_mode': ['ethlm', 'Xt'], // Enter top highlight mode
'enter_vertical_hl_mode': ['evhlm', 'Xv'], // Enter vertical high light mode
'set_a_attributes': ['sgr1', 'sA'], // Define second set of video attributes #1-#6
'set_pglen_inch': ['slength', 'sL'] // YI Set page length to #1 hundredth of an inch
};
};
BundleModuleCode['term/colors']=function (module,exports){
/**
* colors.js - color-related functions for blessed.
* Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
* https://github.com/chjj/blessed
*/
exports.match = function(r1, g1, b1) {
if (typeof r1 === 'string') {
var hex = r1;
if (hex[0] !== '#') {
return -1;
}
hex = exports.hexToRGB(hex);
r1 = hex[0], g1 = hex[1], b1 = hex[2];
} else if (Array.isArray(r1)) {
b1 = r1[2], g1 = r1[1], r1 = r1[0];
}
var hash = (r1 << 16) | (g1 << 8) | b1;
if (exports._cache[hash] != null) {
return exports._cache[hash];
}
var ldiff = Infinity
, li = -1
, i = 0
, c
, r2
, g2
, b2
, diff;
for (; i < exports.vcolors.length; i++) {
c = exports.vcolors[i];
r2 = c[0];
g2 = c[1];
b2 = c[2];
diff = colorDistance(r1, g1, b1, r2, g2, b2);
if (diff === 0) {
li = i;
break;
}
if (diff < ldiff) {
ldiff = diff;
li = i;
}
}
return exports._cache[hash] = li;
};
exports.RGBToHex = function(r, g, b) {
if (Array.isArray(r)) {
b = r[2], g = r[1], r = r[0];
}
function hex(n) {
n = n.toString(16);
if (n.length < 2) n = '0' + n;
return n;
}
return '#' + hex(r) + hex(g) + hex(b);
};
exports.hexToRGB = function(hex) {
if (hex.length === 4) {
hex = hex[0]
+ hex[1] + hex[1]
+ hex[2] + hex[2]
+ hex[3] + hex[3];
}
var col = parseInt(hex.substring(1), 16)
, r = (col >> 16) & 0xff
, g = (col >> 8) & 0xff
, b = col & 0xff;
return [r, g, b];
};
// As it happens, comparing how similar two colors are is really hard. Here is
// one of the simplest solutions, which doesn't require conversion to another
// color space, posted on stackoverflow[1]. Maybe someone better at math can
// propose a superior solution.
// [1] http://stackoverflow.com/questions/1633828
function colorDistance(r1, g1, b1, r2, g2, b2) {
return Math.pow(30 * (r1 - r2), 2)
+ Math.pow(59 * (g1 - g2), 2)
+ Math.pow(11 * (b1 - b2), 2);
}
// This might work well enough for a terminal's colors: treat RGB as XYZ in a
// 3-dimensional space and go midway between the two points.
exports.mixColors = function(c1, c2, alpha) {
// if (c1 === 0x1ff) return c1;
// if (c2 === 0x1ff) return c1;
if (c1 === 0x1ff) c1 = 0;
if (c2 === 0x1ff) c2 = 0;
if (alpha == null) alpha = 0.5;
c1 = exports.vcolors[c1];
var r1 = c1[0];
var g1 = c1[1];
var b1 = c1[2];
c2 = exports.vcolors[c2];
var r2 = c2[0];
var g2 = c2[1];
var b2 = c2[2];
r1 += (r2 - r1) * alpha | 0;
g1 += (g2 - g1) * alpha | 0;
b1 += (b2 - b1) * alpha | 0;
return exports.match([r1, g1, b1]);
};
exports.blend = function blend(attr, attr2, alpha) {
var name, i, c, nc;
var bg = attr & 0x1ff;
if (attr2 != null) {
var bg2 = attr2 & 0x1ff;
if (bg === 0x1ff) bg = 0;
if (bg2 === 0x1ff) bg2 = 0;
bg = exports.mixColors(bg, bg2, alpha);
} else {
if (blend._cache[bg] != null) {
bg = blend._cache[bg];
// } else if (bg < 8) {
// bg += 8;
} else if (bg >= 8 && bg <= 15) {
bg -= 8;
} else {
name = exports.ncolors[bg];
if (name) {
for (i = 0; i < exports.ncolors.length; i++) {
if (name === exports.ncolors[i] && i !== bg) {
c = exports.vcolors[bg];
nc = exports.vcolors[i];
if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
blend._cache[bg] = i;
bg = i;
break;
}
}
}
}
}
}
attr &= ~0x1ff;
attr |= bg;
var fg = (attr >> 9) & 0x1ff;
if (attr2 != null) {
var fg2 = (attr2 >> 9) & 0x1ff;
// 0, 7, 188, 231, 251
if (fg === 0x1ff) {
// XXX workaround
fg = 248;
} else {
if (fg === 0x1ff) fg = 7;
if (fg2 === 0x1ff) fg2 = 7;
fg = exports.mixColors(fg, fg2, alpha);
}
} else {
if (blend._cache[fg] != null) {
fg = blend._cache[fg];
// } else if (fg < 8) {
// fg += 8;
} else if (fg >= 8 && fg <= 15) {
fg -= 8;
} else {
name = exports.ncolors[fg];
if (name) {
for (i = 0; i < exports.ncolors.length; i++) {
if (name === exports.ncolors[i] && i !== fg) {
c = exports.vcolors[fg];
nc = exports.vcolors[i];
if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
blend._cache[fg] = i;
fg = i;
break;
}
}
}
}
}
}
attr &= ~(0x1ff << 9);
attr |= fg << 9;
return attr;
};
exports.blend._cache = {};
exports._cache = {};
exports.reduce = function(color, total) {
if (color >= 16 && total <= 16) {
color = exports.ccolors[color];
} else if (color >= 8 && total <= 8) {
color -= 8;
} else if (color >= 2 && total <= 2) {
color %= 2;
}
return color;
};
// XTerm Colors
// These were actually tough to track down. The xterm source only uses color
// keywords. The X11 source needed to be examined to find the actual values.
// They then had to be mapped to rgb values and then converted to hex values.
exports.xterm = [
'#000000', // black
'#cd0000', // red3
'#00cd00', // green3
'#cdcd00', // yellow3
'#0000ee', // blue2
'#cd00cd', // magenta3
'#00cdcd', // cyan3
'#e5e5e5', // gray90
'#7f7f7f', // gray50
'#ff0000', // red
'#00ff00', // green
'#ffff00', // yellow
'#5c5cff', // rgb:5c/5c/ff
'#ff00ff', // magenta
'#00ffff', // cyan
'#ffffff' // white
];
// Seed all 256 colors. Assume xterm defaults.
// Ported from the xterm color generation script.
exports.colors = (function() {
var cols = exports.colors = []
, _cols = exports.vcolors = []
, r
, g
, b
, i
, l;
function hex(n) {
n = n.toString(16);
if (n.length < 2) n = '0' + n;
return n;
}
function push(i, r, g, b) {
cols[i] = '#' + hex(r) + hex(g) + hex(b);
_cols[i] = [r, g, b];
}
// 0 - 15
exports.xterm.forEach(function(c, i) {
c = parseInt(c.substring(1), 16);
push(i, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
});
// 16 - 231
for (r = 0; r < 6; r++) {
for (g = 0; g < 6; g++) {
for (b = 0; b < 6; b++) {
i = 16 + (r * 36) + (g * 6) + b;
push(i,
r ? (r * 40 + 55) : 0,
g ? (g * 40 + 55) : 0,
b ? (b * 40 + 55) : 0);
}
}
}
// 232 - 255 are grey.
for (g = 0; g < 24; g++) {
l = (g * 10) + 8;
i = 232 + g;
push(i, l, l, l);
}
return cols;
})();
// Map higher colors to the first 8 colors.
// This allows translation of high colors to low colors on 8-color terminals.
exports.ccolors = (function() {
var _cols = exports.vcolors.slice()
, cols = exports.colors.slice()
, out;
exports.vcolors = exports.vcolors.slice(0, 8);
exports.colors = exports.colors.slice(0, 8);
out = cols.map(exports.match);
exports.colors = cols;
exports.vcolors = _cols;
exports.ccolors = out;
return out;
})();
var colorNames = exports.colorNames = {
// special
default: -1,
normal: -1,
bg: -1,
fg: -1,
// normal
black: 0,
red: 1,
green: 2,
yellow: 3,
blue: 4,
magenta: 5,
cyan: 6,
white: 7,
// light
lightblack: 8,
lightred: 9,
lightgreen: 10,
lightyellow: 11,
lightblue: 12,
lightmagenta: 13,
lightcyan: 14,
lightwhite: 15,
// bright
brightblack: 8,
brightred: 9,
brightgreen: 10,
brightyellow: 11,
brightblue: 12,
brightmagenta: 13,
brightcyan: 14,
brightwhite: 15,
// alternate spellings
grey: 8,
gray: 8,
lightgrey: 7,
lightgray: 7,
brightgrey: 7,
brightgray: 7
};
exports.convert = function(color) {
if (typeof color === 'number') {
;
} else if (typeof color === 'string') {
color = color.replace(/[\- ]/g, '');
if (colorNames[color] != null) {
color = colorNames[color];
} else {
color = exports.match(color);
}
} else if (Array.isArray(color)) {
color = exports.match(color);
} else {
color = -1;
}
return color !== -1 ? color : 0x1ff;
};
// Map higher colors to the first 8 colors.
// This allows translation of high colors to low colors on 8-color terminals.
// Why the hell did I do this by hand?
exports.ccolors = {
blue: [
4,
12,
[17, 21],
[24, 27],
[31, 33],
[38, 39],
45,
[54, 57],
[60, 63],
[67, 69],
[74, 75],
81,
[91, 93],
[97, 99],
[103, 105],
[110, 111],
117,
[128, 129],
[134, 135],
[140, 141],
[146, 147],
153,
165,
171,
177,
183,
189
],
green: [
2,
10,
22,
[28, 29],
[34, 36],
[40, 43],
[46, 50],
[64, 65],
[70, 72],
[76, 79],
[82, 86],
[106, 108],
[112, 115],
[118, 122],
[148, 151],
[154, 158],
[190, 194]
],
cyan: [
6,
14,
23,
30,
37,
44,
51,
66,
73,
80,
87,
109,
116,
123,
152,
159,
195
],
red: [
1,
9,
52,
[88, 89],
[94, 95],
[124, 126],
[130, 132],
[136, 138],
[160, 163],
[166, 169],
[172, 175],
[178, 181],
[196, 200],
[202, 206],
[208, 212],
[214, 218],
[220, 224]
],
magenta: [
5,
13,
53,
90,
96,
127,
133,
139,
164,
170,
176,
182,
201,
207,
213,
219,
225
],
yellow: [
3,
11,
58,
[100, 101],
[142, 144],
[184, 187],
[226, 230]
],
black: [
0,
8,
16,
59,
102,
[232, 243]
],
white: [
7,
15,
145,
188,
231,
[244, 255]
]
};
exports.ncolors = [];
Object.keys(exports.ccolors).forEach(function(name) {
exports.ccolors[name].forEach(function(offset) {
if (typeof offset === 'number') {
exports.ncolors[offset] = name;
exports.ccolors[offset] = exports.colorNames[name];
return;
}
for (var i = offset[0], l = offset[1]; i <= l; i++) {
exports.ncolors[i] = name;
exports.ccolors[i] = exports.colorNames[name];
}
});
delete exports.ccolors[name];
});
};
BundleModuleCode['term/keys']=function (module,exports){
/**
* keys.js - emit key presses
* Copyright (c) 2010-2015, Joyent, Inc. and other contributors (MIT License)
* https://github.com/chjj/blessed
*/
// Originally taken from the node.js tree:
//
// Copyright Joyent, Inc. and other Node contributors. All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
var EventEmitter = Require('events').EventEmitter;
// NOTE: node <=v0.8.x has no EventEmitter.listenerCount
function listenerCount(stream, event) {
return EventEmitter.listenerCount
? EventEmitter.listenerCount(stream, event)
: stream.listeners(event).length;
}
/**
* accepts a readable Stream instance and makes it emit "keypress" events
*/
function emitKeypressEvents(stream) {
if (stream._keypressDecoder) return;
var StringDecoder = Require('string_decoder').StringDecoder; // lazy load
stream._keypressDecoder = new StringDecoder('utf8');
function onData(b) {
if (listenerCount(stream, 'keypress') > 0) {
var r = stream._keypressDecoder.write(b);
if (r) emitKeys(stream, r);
} else {
// Nobody's watching anyway
stream.removeListener('data', onData);
stream.on('newListener', onNewListener);
}
}
function onNewListener(event) {
if (event === 'keypress') {
stream.on('data', onData);
stream.removeListener('newListener', onNewListener);
}
}
if (listenerCount(stream, 'keypress') > 0) {
stream.on('data', onData);
} else {
stream.on('newListener', onNewListener);
}
}
exports.emitKeypressEvents = emitKeypressEvents;
/*
Some patterns seen in terminal key escape codes, derived from combos seen
at http://www.midnight-commander.org/browser/lib/tty/key.c
ESC letter
ESC [ letter
ESC [ modifier letter
ESC [ 1 ; modifier letter
ESC [ num char
ESC [ num ; modifier char
ESC O letter
ESC O modifier letter
ESC O 1 ; modifier letter
ESC N letter
ESC [ [ num ; modifier char
ESC [ [ 1 ; modifier letter
ESC ESC [ num char
ESC ESC O letter
- char is usually ~ but $ and ^ also happen with rxvt
- modifier is 1 +
(shift * 1) +
(left_alt * 2) +
(ctrl * 4) +
(right_alt * 8)
- two leading ESCs apparently mean the same as one leading ESC
*/
// Regexes used for ansi escape code splitting
var metaKeyCodeReAnywhere = /(?:\x1b)([a-zA-Z0-9])/;
var metaKeyCodeRe = new RegExp('^' + metaKeyCodeReAnywhere.source + '$');
var functionKeyCodeReAnywhere = new RegExp('(?:\x1b+)(O|N|\\[|\\[\\[)(?:' + [
'(\\d+)(?:;(\\d+))?([~^$])',
'(?:M([@ #!a`])(.)(.))', // mouse
'(?:1;)?(\\d+)?([a-zA-Z])'
].join('|') + ')');
var functionKeyCodeRe = new RegExp('^' + functionKeyCodeReAnywhere.source);
var escapeCodeReAnywhere = new RegExp([
functionKeyCodeReAnywhere.source, metaKeyCodeReAnywhere.source, /\x1b./.source
].join('|'));
function emitKeys(stream, s) {
if (Buffer.isBuffer(s)) {
if (s[0] > 127 && s[1] === undefined) {
s[0] -= 128;
s = '\x1b' + s.toString(stream.encoding || 'utf-8');
} else {
s = s.toString(stream.encoding || 'utf-8');
}
}
if (isMouse(s)) return;
var buffer = [];
var match;
while (match = escapeCodeReAnywhere.exec(s)) {
buffer = buffer.concat(s.slice(0, match.index).split(''));
buffer.push(match[0]);
s = s.slice(match.index + match[0].length);
}
buffer = buffer.concat(s.split(''));
buffer.forEach(function(s) {
var ch,
key = {
sequence: s,
name: undefined,
ctrl: false,
meta: false,
shift: false
},
parts;
if (s === '\r') {
// carriage return
key.name = 'return';
} else if (s === '\n') {
// enter, should have been called linefeed
key.name = 'enter';
// linefeed
// key.name = 'linefeed';
} else if (s === '\t') {
// tab
key.name = 'tab';
} else if (s === '\b' || s === '\x7f' ||
s === '\x1b\x7f' || s === '\x1b\b') {
// backspace or ctrl+h
key.name = 'backspace';
key.meta = (s.charAt(0) === '\x1b');
} else if (s === '\x1b' || s === '\x1b\x1b') {
// escape key
key.name = 'escape';
key.meta = (s.length === 2);
} else if (s === ' ' || s === '\x1b ') {
key.name = 'space';
key.meta = (s.length === 2);
} else if (s.length === 1 && s <= '\x1a') {
// ctrl+letter
key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1);
key.ctrl = true;
} else if (s.length === 1 && s >= 'a' && s <= 'z') {
// lowercase letter
key.name = s;
} else if (s.length === 1 && s >= 'A' && s <= 'Z') {
// shift+letter
key.name = s.toLowerCase();
key.shift = true;
} else if (parts = metaKeyCodeRe.exec(s)) {
// meta+character key
key.name = parts[1].toLowerCase();
key.meta = true;
key.shift = /^[A-Z]$/.test(parts[1]);
} else if (parts = functionKeyCodeRe.exec(s)) {
// ansi escape sequence
// reassemble the key code leaving out leading \x1b's,
// the modifier key bitflag and any meaningless "1;" sequence
var code = (parts[1] || '') + (parts[2] || '') +
(parts[4] || '') + (parts[9] || ''),
modifier = (parts[3] || parts[8] || 1) - 1;
// Parse the key modifier
key.ctrl = !!(modifier & 4);
key.meta = !!(modifier & 10);
key.shift = !!(modifier & 1);
key.code = code;
// Parse the key itself
switch (code) {
/* xterm/gnome ESC O letter */
case 'OP': key.name = 'f1'; break;
case 'OQ': key.name = 'f2'; break;
case 'OR': key.name = 'f3'; break;
case 'OS': key.name = 'f4'; break;
/* xterm/rxvt ESC [ number ~ */
case '[11~': key.name = 'f1'; break;
case '[12~': key.name = 'f2'; break;
case '[13~': key.name = 'f3'; break;
case '[14~': key.name = 'f4'; break;
/* from Cygwin and used in libuv */
case '[[A': key.name = 'f1'; break;
case '[[B': key.name = 'f2'; break;
case '[[C': key.name = 'f3'; break;
case '[[D': key.name = 'f4'; break;
case '[[E': key.name = 'f5'; break;
/* common */
case '[15~': key.name = 'f5'; break;
case '[17~': key.name = 'f6'; break;
case '[18~': key.name = 'f7'; break;
case '[19~': key.name = 'f8'; break;
case '[20~': key.name = 'f9'; break;
case '[21~': key.name = 'f10'; break;
case '[23~': key.name = 'f11'; break;
case '[24~': key.name = 'f12'; break;
/* xterm ESC [ letter */
case '[A': key.name = 'up'; break;
case '[B': key.name = 'down'; break;
case '[C': key.name = 'right'; break;
case '[D': key.name = 'left'; break;
case '[E': key.name = 'clear'; break;
case '[F': key.name = 'end'; break;
case '[H': key.name = 'home'; break;
/* xterm/gnome ESC O letter */
case 'OA': key.name = 'up'; break;
case 'OB': key.name = 'down'; break;
case 'OC': key.name = 'right'; break;
case 'OD': key.name = 'left'; break;
case 'OE': key.name = 'clear'; break;
case 'OF': key.name = 'end'; break;
case 'OH': key.name = 'home'; break;
/* xterm/rxvt ESC [ number ~ */
case '[1~': key.name = 'home'; break;
case '[2~': key.name = 'insert'; break;
case '[3~': key.name = 'delete'; break;
case '[4~': key.name = 'end'; break;
case '[5~': key.name = 'pageup'; break;
case '[6~': key.name = 'pagedown'; break;
/* putty */
case '[[5~': key.name = 'pageup'; break;
case '[[6~': key.name = 'pagedown'; break;
/* rxvt */
case '[7~': key.name = 'home'; break;
case '[8~': key.name = 'end'; break;
/* rxvt keys with modifiers */
case '[a': key.name = 'up'; key.shift = true; break;
case '[b': key.name = 'down'; key.shift = true; break;
case '[c': key.name = 'right'; key.shift = true; break;
case '[d': key.name = 'left'; key.shift = true; break;
case '[e': key.name = 'clear'; key.shift = true; break;
case '[2$': key.name = 'insert'; key.shift = true; break;
case '[3$': key.name = 'delete'; key.shift = true; break;
case '[5$': key.name = 'pageup'; key.shift = true; break;
case '[6$': key.name = 'pagedown'; key.shift = true; break;
case '[7$': key.name = 'home'; key.shift = true; break;
case '[8$': key.name = 'end'; key.shift = true; break;
case 'Oa': key.name = 'up'; key.ctrl = true; break;
case 'Ob': key.name = 'down'; key.ctrl = true; break;
case 'Oc': key.name = 'right'; key.ctrl = true; break;
case 'Od': key.name = 'left'; key.ctrl = true; break;
case 'Oe': key.name = 'clear'; key.ctrl = true; break;
case '[2^': key.name = 'insert'; key.ctrl = true; break;
case '[3^': key.name = 'delete'; key.ctrl = true; break;
case '[5^': key.name = 'pageup'; key.ctrl = true; break;
case '[6^': key.name = 'pagedown'; key.ctrl = true; break;
case '[7^': key.name = 'home'; key.ctrl = true; break;
case '[8^': key.name = 'end'; key.ctrl = true; break;
/* misc. */
case '[Z': key.name = 'tab'; key.shift = true; break;
default: key.name = 'undefined'; break;
}
}
// Don't emit a key if no name was found
if (key.name === undefined) {
key = undefined;
}
if (s.length === 1) {
ch = s;
}
if (key || ch) {
stream.emit('keypress', ch, key);
// if (key && key.name === 'return') {
// var nkey = {};
// Object.keys(key).forEach(function(k) {
// nkey[k] = key[k];
// });
// nkey.name = 'enter';
// stream.emit('keypress', ch, nkey);
// }
}
});
}
function isMouse(s) {
return /\x1b\[M/.test(s)
|| /\x1b\[M([\x00\u0020-\ufffe]{3})/.test(s)
|| /\x1b\[(\d+;\d+;\d+)M/.test(s)
|| /\x1b\[<(\d+;\d+;\d+)([mM])/.test(s)
|| /\x1b\[<(\d+;\d+;\d+;\d+)&w/.test(s)
|| /\x1b\[24([0135])~\[(\d+),(\d+)\]\r/.test(s)
|| /\x1b\[(O|I)/.test(s);
}
};
BundleModuleCode['term/widget']=function (module,exports){
/**
* widget.js - high-level interface for blessed
* Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
* https://github.com/chjj/blessed
*/
var widget = exports;
widget.classes = [
'Node',
'Screen',
'Element',
'Box',
'Chat',
'Text',
'Line',
'ScrollableBox',
'ScrollableText',
'BigText',
'List',
'Form',
'Input',
'Textarea',
'Textbox',
'Button',
'ProgressBar',
'FileManager',
'Checkbox',
'RadioSet',
'RadioButton',
'Prompt',
'Question',
'Message',
'Keyboard',
'Loading',
'Listbar',
'Log',
'Table',
'ListTable',
'Terminal',
'Image',
'ANSIImage',
'OverlayImage',
'Video',
'Layout',
'Log',
'Tree'
];
widget.classes.forEach(function(name) {
var file = name.toLowerCase();
widget[name] = widget[file] = Require('term/widgets/' + file);
});
widget.aliases = {
'ListBar': 'Listbar',
'PNG': 'ANSIImage'
};
Object.keys(widget.aliases).forEach(function(key) {
var name = widget.aliases[key];
widget[key] = widget[name];
widget[key.toLowerCase()] = widget[name];
});
};
BundleModuleCode['term/widgets/node']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: sbosse (2017-2021).
** $VERSION: 1.2.1
**
** $INFO:
*
* node.js - base abstract node for blessed
*
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var EventEmitter = Require('term/events').EventEmitter;
/**
* Node
*/
function Node(options) {
var self = this;
var Screen = Require('term/widgets/screen');
if (!instanceOf(this,Node)) {
return new Node(options);
}
EventEmitter.call(this);
options = options || {};
this.options = options;
this.screen = this.screen || options.screen;
if (!this.screen) {
if (this.type === 'screen') {
this.screen = this;
} else if (Screen.total === 1) {
this.screen = Screen.global;
} else if (options.parent) {
this.screen = options.parent;
while (this.screen && this.screen.type !== 'screen') {
this.screen = this.screen.parent;
}
} else if (Screen.total) {
// This _should_ work in most cases as long as the element is appended
// synchronously after the screen's creation. Throw error if not.
this.screen = Screen.instances[Screen.instances.length - 1];
process.nextTick(function() {
if (!self.parent) {
throw new Error('Element (' + self.type + ')'
+ ' was not appended synchronously after the'
+ ' screen\'s creation. Please set a `parent`'
+ ' or `screen` option in the element\'s constructor'
+ ' if you are going to use multiple screens and'
+ ' append the element later.');
}
});
} else {
throw new Error('No active screen.');
}
}
this.parent = options.parent || null;
this.children = [];
this.$ = this._ = this.data = {};
this.uid = Node.uid++;
this.index = this.index != null ? this.index : -1;
if (this.type !== 'screen') {
this.detached = true;
}
if (this.parent) {
this.parent.append(this);
}
(options.children || []).forEach(this.append.bind(this));
}
Node.uid = 0;
//Node.prototype.__proto__ = EventEmitter.prototype;
inheritPrototype(Node,EventEmitter);
Node.prototype.type = 'node';
Node.prototype.insert = function(element, i) {
var self = this;
if (element.screen && element.screen !== this.screen) {
throw new Error('Cannot switch a node\'s screen.');
}
element.detach();
element.parent = this;
element.screen = this.screen;
if (i === 0) {
this.children.unshift(element);
} else if (i === this.children.length) {
this.children.push(element);
} else {
this.children.splice(i, 0, element);
}
element.emit('reparent', this);
this.emit('adopt', element);
(function emit(el) {
var n = el.detached !== self.detached;
el.detached = self.detached;
if (n) el.emit('attach');
el.children.forEach(emit);
})(element);
if (!this.screen.focused) {
this.screen.focused = element;
}
};
Node.prototype.prepend = function(element) {
this.insert(element, 0);
};
Node.prototype.append = function(element) {
this.insert(element, this.children.length);
};
Node.prototype.insertBefore = function(element, other) {
var i = this.children.indexOf(other);
if (~i) this.insert(element, i);
};
Node.prototype.insertAfter = function(element, other) {
var i = this.children.indexOf(other);
if (~i) this.insert(element, i + 1);
};
Node.prototype.remove = function(element) {
if (element.parent !== this) return;
var i = this.children.indexOf(element);
if (!~i) return;
element.clearPos();
element.parent = null;
this.children.splice(i, 1);
i = this.screen.clickable.indexOf(element);
if (~i) this.screen.clickable.splice(i, 1);
i = this.screen.keyable.indexOf(element);
if (~i) this.screen.keyable.splice(i, 1);
element.emit('reparent', null);
this.emit('remove', element);
(function emit(el) {
var n = el.detached !== true;
el.detached = true;
if (n) el.emit('detach');
el.children.forEach(emit);
})(element);
if (this.screen.focused === element) {
this.screen.rewindFocus();
}
};
Node.prototype.detach = function() {
if (this.parent) this.parent.remove(this);
};
Node.prototype.free = function() {
return;
};
Node.prototype.destroy = function() {
this.detach();
this.forDescendants(function(el) {
el.free();
el.destroyed = true;
el.emit('destroy');
}, this);
};
Node.prototype.forDescendants = function(iter, s) {
if (s) iter(this);
this.children.forEach(function emit(el) {
iter(el);
el.children.forEach(emit);
});
};
Node.prototype.forAncestors = function(iter, s) {
var el = this;
if (s) iter(this);
while (el = el.parent) {
iter(el);
}
};
Node.prototype.collectDescendants = function(s) {
var out = [];
this.forDescendants(function(el) {
out.push(el);
}, s);
return out;
};
Node.prototype.collectAncestors = function(s) {
var out = [];
this.forAncestors(function(el) {
out.push(el);
}, s);
return out;
};
Node.prototype.emitDescendants = function() {
var args = Array.prototype.slice(arguments)
, iter;
if (typeof args[args.length - 1] === 'function') {
iter = args.pop();
}
return this.forDescendants(function(el) {
if (iter) iter(el);
el.emit.apply(el, args);
}, true);
};
Node.prototype.emitAncestors = function() {
var args = Array.prototype.slice(arguments)
, iter;
if (typeof args[args.length - 1] === 'function') {
iter = args.pop();
}
return this.forAncestors(function(el) {
if (iter) iter(el);
el.emit.apply(el, args);
}, true);
};
Node.prototype.hasDescendant = function(target) {
return (function find(el) {
for (var i = 0; i < el.children.length; i++) {
if (el.children[i] === target) {
return true;
}
if (find(el.children[i]) === true) {
return true;
}
}
return false;
})(this);
};
Node.prototype.hasAncestor = function(target) {
var el = this;
while (el = el.parent) {
if (el === target) return true;
}
return false;
};
Node.prototype.get = function(name, value) {
if (this.data.hasOwnProperty(name)) {
return this.data[name];
}
return value;
};
Node.prototype.set = function(name, value) {
return this.data[name] = value;
};
/**
* Expose
*/
module.exports = Node;
};
BundleModuleCode['term/events']=function (module,exports){
/**
* Version 1.1.2
* events.js - event emitter for blessed
* Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
* https://github.com/chjj/blessed
*/
var slice = Array.prototype.slice;
/**
* EventEmitter
*/
function EventEmitter() {
if (!this._events) this._events = {};
}
EventEmitter.prototype.setMaxListeners = function(n) {
this._maxListeners = n;
};
EventEmitter.prototype.addListener = function(type, listener) {
if (!this._events[type]) {
this._events[type] = listener;
} else if (typeof this._events[type] === 'function') {
this._events[type] = [this._events[type], listener];
} else {
this._events[type].push(listener);
}
this._emit('newListener', [type, listener]);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.removeListener = function(type, listener) {
var handler = this._events[type];
if (!handler) return;
if (typeof handler === 'function' ) {
if (!(handler === listener)) return;
delete this._events[type];
this._emit('removeListener', [type, listener]);
return;
}
if (handler.length === 1) {
if (!(handler[0] === listener)) return;
delete this._events[type];
this._emit('removeListener', [type, listener]);
return;
}
for (var i = 0; i < handler.length; i++) {
if (handler[i] === listener || handler[i].listener === listener) {
handler.splice(i, 1);
this._emit('removeListener', [type, listener]);
return;
}
}
};
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners = function(type) {
if (type) {
delete this._events[type];
} else {
this._events = {};
}
};
EventEmitter.prototype.once = function(type, listener) {
function on() {
this.removeListener(type, on);
return listener.apply(this, arguments);
}
on.listener = listener;
return this.on(type, on);
};
EventEmitter.prototype.listeners = function(type) {
return typeof this._events[type] === 'function'
? [this._events[type]]
: this._events[type] || [];
};
EventEmitter.prototype._emit = function(type, args) {
var handler = this._events[type]
, ret;
// if (type !== 'event') {
// this._emit('event', [type.replace(/^element /, '')].concat(args));
// }
if (!handler) {
if (type === 'error') {
throw new args[0];
}
return;
}
if (typeof handler === 'function') {
return handler.apply(this, args);
}
for (var i = 0; i < handler.length; i++) {
if (handler[i].apply(this, args) === false) {
ret = false;
}
}
return ret !== false;
};
EventEmitter.prototype.emit = function(type) {
var args = slice.call(arguments, 1)
, params = slice.call(arguments)
, el = this;
this._emit('event', params);
if (this.type === 'screen') {
return this._emit(type, args);
}
if (this._emit(type, args) === false) {
return false;
}
type = 'element ' + type;
args.unshift(this);
// `element` prefix
// params = [type].concat(args);
// no `element` prefix
// params.splice(1, 0, this);
do {
// el._emit('event', params);
if (!el._events[type]) continue;
if (el._emit(type, args) === false) {
return false;
}
} while (el = el.parent);
return true;
};
// For hooking into the main EventEmitter if we want to.
// Might be better to do things this way being that it
// will always be compatible with node, not to mention
// it gives us domain support as well.
// Node.prototype._emit = Node.prototype.emit;
// Node.prototype.emit = function(type) {
// var args, el;
//
// if (this.type === 'screen') {
// return this._emit.apply(this, arguments);
// }
//
// this._emit.apply(this, arguments);
// if (this._bubbleStopped) return false;
//
// args = slice.call(arguments, 1);
// el = this;
//
// args.unshift('element ' + type, this);
// this._bubbleStopped = false;
// //args.push(stopBubble);
//
// do {
// if (!el._events || !el._events[type]) continue;
// el._emit.apply(el, args);
// if (this._bubbleStopped) return false;
// } while (el = el.parent);
//
// return true;
// };
//
// Node.prototype._addListener = Node.prototype.addListener;
// Node.prototype.on =
// Node.prototype.addListener = function(type, listener) {
// function on() {
// if (listener.apply(this, arguments) === false) {
// this._bubbleStopped = true;
// }
// }
// on.listener = listener;
// return this._addListener(type, on);
// };
/**
* Expose
*/
exports = EventEmitter;
exports.EventEmitter = EventEmitter;
module.exports = exports;
};
BundleModuleCode['term/widgets/screen']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2016-2017)
** $REVESIO: 1.1.6
**
** $INFO:
**
** screen.js - screen node for blessed
**
** Added:
** - Round-Robin focus shift on tabulator key press event. Requires <box>.options.focus=true
** setting for selectable boxes.
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var path = Require('path')
, fs = Require('fs')
, cp = Require('child_process');
var colors = Require('term/colors')
, program = Require('term/program')
, unicode = Require('term/unicode');
var nextTick = global.setImmediate || process.nextTick.bind(process);
var helpers = Require('term/helpers');
var Node = Require('term/widgets/node');
var Log = Require('term/widgets/log');
var Element = Require('term/widgets/element');
var Box = Require('term/widgets/box');
/**
* Screen
*/
function Screen(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Screen(options);
}
Screen.bind(this);
options = options || {};
if (options.rsety && options.listen) {
options = { program: options };
}
this.program = options.program;
if (!this.program) {
this.program = program({
input: options.input,
output: options.output,
log: options.log,
debug: options.debug,
dump: options.dump,
terminal: options.terminal || options.term,
resizeTimeout: options.resizeTimeout,
forceUnicode: options.forceUnicode,
tput: true,
buffer: true,
zero: true
});
} else {
this.program.setupTput();
this.program.useBuffer = true;
this.program.zero = true;
this.program.options.resizeTimeout = options.resizeTimeout;
if (options.forceUnicode != null) {
this.program.tput.features.unicode = options.forceUnicode;
this.program.tput.unicode = options.forceUnicode;
}
}
this.tput = this.program.tput;
Node.call(this, options);
this.autoPadding = options.autoPadding !== false;
this.tabc = Array((options.tabSize || 4) + 1).join(' ');
this.dockBorders = options.dockBorders;
// use terminal reset code to hide cursor
this.forceCursor = options.forceCursor;
this.ignoreLocked = options.ignoreLocked || [];
this._unicode = this.tput.unicode || this.tput.numbers.U8 === 1;
this.fullUnicode = this.options.fullUnicode && this._unicode;
this.dattr = ((0 << 18) | (0x1ff << 9)) | 0x1ff;
this.renders = 0;
this.position = {
left: this.left = this.aleft = this.rleft = 0,
right: this.right = this.aright = this.rright = 0,
top: this.top = this.atop = this.rtop = 0,
bottom: this.bottom = this.abottom = this.rbottom = 0,
get height() { return self.height; },
get width() { return self.width; }
};
this.ileft = 0;
this.itop = 0;
this.iright = 0;
this.ibottom = 0;
this.iheight = 0;
this.iwidth = 0;
this.padding = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
this.hover = null;
this.history = [];
this.clickable = [];
this.keyable = [];
this.grabKeys = false;
this.lockKeys = false;
this.focused;
this._buf = '';
this._ci = -1;
if (options.title) {
this.title = options.title;
}
options.cursor = options.cursor || {
artificial: options.artificialCursor,
shape: options.cursorShape,
blink: options.cursorBlink,
color: options.cursorColor
};
this.cursor = {
artificial: options.cursor.artificial || false,
shape: options.cursor.shape || 'block',
blink: options.cursor.blink || false,
color: options.cursor.color || null,
_set: false,
_state: 1,
_hidden: true
};
this.program.on('resize', function() {
self.alloc();
self.render();
(function emit(el) {
el.emit('resize');
el.children.forEach(emit);
})(self);
});
this.program.on('focus', function() {
self.emit('focus');
});
this.program.on('blur', function() {
self.emit('blur');
});
this.program.on('warning', function(text) {
self.emit('warning', text);
});
this.on('newListener', function fn(type) {
if (type === 'keypress' || type.indexOf('key ') === 0 || type === 'mouse') {
if (type === 'keypress' || type.indexOf('key ') === 0) self._listenKeys();
if (type === 'mouse') self._listenMouse();
}
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
|| type === 'wheeldown'
|| type === 'wheelup'
|| type === 'mousemove') {
self._listenMouse();
}
});
this.setMaxListeners(Infinity);
this.enter();
this.postEnter();
}
Screen.global = null;
Screen.total = 0;
Screen.instances = [];
Screen.bind = function(screen) {
if (!Screen.global) {
Screen.global = screen;
}
if (!~Screen.instances.indexOf(screen)) {
Screen.instances.push(screen);
screen.index = Screen.total;
Screen.total++;
}
if (Screen._bound) return;
Screen._bound = true;
process.on('uncaughtException', Screen._exceptionHandler = function(err) {
if (process.listeners('uncaughtException').length > 1) {
return;
}
Screen.instances.slice().forEach(function(screen) {
screen.destroy();
});
err = err || new Error('Uncaught Exception.');
console.error(err.stack ? err.stack + '' : err + '');
nextTick(function() {
process.exit(1);
});
});
['SIGTERM', 'SIGINT', 'SIGQUIT'].forEach(function(signal) {
var name = '_' + signal.toLowerCase() + 'Handler';
process.on(signal, Screen[name] = function() {
if (process.listeners(signal).length > 1) {
return;
}
nextTick(function() {
process.exit(0);
});
});
});
process.on('exit', Screen._exitHandler = function() {
Screen.instances.slice().forEach(function(screen) {
screen.destroy();
});
});
};
//Screen.prototype.__proto__ = Node.prototype;
inheritPrototype(Screen,Node);
Screen.prototype.type = 'screen';
/* Depricated
Screen.prototype.__defineGetter__('title', function() {
return this.program.title;
});
Screen.prototype.__defineSetter__('title', function(title) {
return this.program.title = title;
});
Screen.prototype.__defineGetter__('terminal', function() {
return this.program.terminal;
});
Screen.prototype.__defineSetter__('terminal', function(terminal) {
this.setTerminal(terminal);
return this.program.terminal;
});
*/
defineGetter(Screen,'title', function() {
return this.program.title;
});
defineSetter(Screen,'title', function(title) {
return this.program.title = title;
});
defineGetter(Screen,'terminal', function() {
return this.program.terminal;
});
defineSetter(Screen,'terminal', function(terminal) {
this.setTerminal(terminal);
return this.program.terminal;
});
/*
Object.defineProperty(Screen.prototype,'title',{
get: function () {
return this.program.title;
},
set: function (title) {
return this.program.title = title;
}
});
Object.defineProperty(Screen.prototype,'terminal',{
get: function () {
return this.program.terminal;
},
set: function (terminal) {
this.setTerminal(terminal);
return this.program.terminal;
}
});
*/
Screen.prototype.setTerminal = function(terminal) {
var entered = !!this.program.isAlt;
if (entered) {
this._buf = '';
this.program._buf = '';
this.leave();
}
this.program.setTerminal(terminal);
this.tput = this.program.tput;
if (entered) {
this.enter();
}
};
Screen.prototype.enter = function() {
if (this.program.isAlt) return;
if (!this.cursor._set) {
if (this.options.cursor.shape) {
this.cursorShape(this.cursor.shape, this.cursor.blink);
}
if (this.options.cursor.color) {
this.cursorColor(this.cursor.color);
}
}
if (process.platform === 'win32') {
try {
cp.execSync('cls', { stdio: 'ignore', timeout: 1000 });
} catch (e) {
;
}
}
this.program.alternateBuffer();
this.program.put.keypad_xmit();
this.program.csr(0, this.height - 1);
this.program.hideCursor();
this.program.cup(0, 0);
// We need this for tmux now:
if (this.tput.strings.ena_acs) {
this.program._write(this.tput.enacs());
}
this.alloc();
};
Screen.prototype.leave = function() {
if (!this.program.isAlt) return;
this.program.put.keypad_local();
if (this.program.scrollTop !== 0
|| this.program.scrollBottom !== this.rows - 1) {
this.program.csr(0, this.height - 1);
}
// XXX For some reason if alloc/clear() is before this
// line, it doesn't work on linux console.
this.program.showCursor();
this.alloc();
if (this._listenedMouse) {
this.program.disableMouse();
}
this.program.normalBuffer();
if (this.cursor._set) this.cursorReset();
this.program.flush();
if (process.platform === 'win32') {
try {
cp.execSync('cls', { stdio: 'ignore', timeout: 1000 });
} catch (e) {
;
}
}
};
Screen.prototype.postEnter = function() {
var self = this;
if (this.options.debug) {
this.debugLog = new Log({
screen: this,
parent: this,
hidden: true,
draggable: true,
left: 'center',
top: 'center',
width: '30%',
height: '30%',
border: 'line',
label: ' {bold}Debug Log{/bold} ',
tags: true,
keys: true,
vi: true,
mouse: true,
scrollbar: {
ch: ' ',
track: {
bg: 'yellow'
},
style: {
inverse: true
}
}
});
this.debugLog.toggle = function() {
if (self.debugLog.hidden) {
self.saveFocus();
self.debugLog.show();
self.debugLog.setFront();
self.debugLog.focus();
} else {
self.debugLog.hide();
self.restoreFocus();
}
self.render();
};
this.debugLog.key(['q', 'escape'], self.debugLog.toggle);
this.key('f12', self.debugLog.toggle);
}
if (this.options.warnings) {
this.on('warning', function(text) {
var warning = new Box({
screen: self,
parent: self,
left: 'center',
top: 'center',
width: 'shrink',
padding: 1,
height: 'shrink',
align: 'center',
valign: 'middle',
border: 'line',
label: ' {red-fg}{bold}WARNING{/} ',
content: '{bold}' + text + '{/bold}',
tags: true
});
self.render();
var timeout = setTimeout(function() {
warning.destroy();
self.render();
}, 1500);
if (timeout.unref) {
timeout.unref();
}
});
}
};
Screen.prototype._destroy = Screen.prototype.destroy;
Screen.prototype.destroy = function() {
if (!this.program) return;
this.leave();
var index = Screen.instances.indexOf(this);
if (~index) {
Screen.instances.splice(index, 1);
Screen.total--;
Screen.global = Screen.instances[0];
if (Screen.total === 0) {
Screen.global = null;
process.removeListener('uncaughtException', Screen._exceptionHandler);
process.removeListener('SIGTERM', Screen._sigtermHandler);
process.removeListener('SIGINT', Screen._sigintHandler);
process.removeListener('SIGQUIT', Screen._sigquitHandler);
process.removeListener('exit', Screen._exitHandler);
delete Screen._exceptionHandler;
delete Screen._sigtermHandler;
delete Screen._sigintHandler;
delete Screen._sigquitHandler;
delete Screen._exitHandler;
delete Screen._bound;
}
this.destroyed = true;
this.emit('destroy');
this._destroy();
}
this.program.destroy();
};
Screen.prototype.log = function() {
return this.program.log.apply(this.program, arguments);
};
Screen.prototype.debug = function() {
if (this.debugLog) {
this.debugLog.log.apply(this.debugLog, arguments);
}
return this.program.debug.apply(this.program, arguments);
};
Screen.prototype._listenMouse = function(el) {
var self = this;
if (el && !~this.clickable.indexOf(el)) {
el.clickable = true;
this.clickable.push(el);
}
if (this._listenedMouse) return;
this._listenedMouse = true;
this.program.enableMouse();
if (this.options.sendFocus) {
this.program.setMouse({ sendFocus: true }, true);
}
this.on('render', function() {
self._needsClickableSort = true;
});
this.program.on('mouse', function(data) {
if (self.lockKeys) return;
if (self._needsClickableSort) {
self.clickable = helpers.hsort(self.clickable);
self._needsClickableSort = false;
}
var i = 0
, el
, set
, pos;
for (; i < self.clickable.length; i++) {
el = self.clickable[i];
if (el.detached || !el.visible) {
continue;
}
// if (self.grabMouse && self.focused !== el
// && !el.hasAncestor(self.focused)) continue;
pos = el.lpos;
if (!pos) continue;
if (data.x >= pos.xi && data.x < pos.xl
&& data.y >= pos.yi && data.y < pos.yl) {
el.emit('mouse', data);
if (data.action === 'mousedown') {
self.mouseDown = el;
} else if (data.action === 'mouseup') {
(self.mouseDown || el).emit('click', data);
self.mouseDown = null;
} else if (data.action === 'mousemove') {
if (self.hover && el.index > self.hover.index) {
set = false;
}
if (self.hover !== el && !set) {
if (self.hover) {
self.hover.emit('mouseout', data);
}
el.emit('mouseover', data);
self.hover = el;
}
set = true;
}
el.emit(data.action, data);
break;
}
}
// Just mouseover?
if ((data.action === 'mousemove'
|| data.action === 'mousedown'
|| data.action === 'mouseup')
&& self.hover
&& !set) {
self.hover.emit('mouseout', data);
self.hover = null;
}
self.emit('mouse', data);
self.emit(data.action, data);
});
// Autofocus highest element.
// this.on('element click', function(el, data) {
// var target;
// do {
// if (el.clickable === true && el.options.autoFocus !== false) {
// target = el;
// }
// } while (el = el.parent);
// if (target) target.focus();
// });
// Autofocus elements with the appropriate option.
this.on('element click', function(el) {
if (el.clickable === true && el.options.autoFocus !== false) {
el.focus();
}
});
};
Screen.prototype.enableMouse = function(el) {
this._listenMouse(el);
};
Screen.prototype._listenKeys = function(el) {
var self = this;
if (el && !~this.keyable.indexOf(el)) {
el.keyable = true;
this.keyable.push(el);
}
if (this._listenedKeys) return;
this._listenedKeys = true;
// NOTE: The event emissions used to be reversed:
// element + screen
// They are now:
// screen + element
// After the first keypress emitted, the handler
// checks to make sure grabKeys, lockKeys, and focused
// weren't changed, and handles those situations appropriately.
this.program.on('keypress', function(ch, key) {
if (key.name == 'tab') {
/* Round-Robin focus shift of top-level boxes -- find
** currently focussed box by iterating over children list.
** Maybe it is a child of a top-level box itself!
*/
//console.log(self.focused.type)
self.program.hideCursor(self.forceCursor);
var last=self.focused;
var first=undefined;
var next=undefined;
for (var c in self.children) {
var child=self.children[c];
if (first==undefined) first=child;
if (last==undefined && next==undefined && child && child.options && child.options.focus) next=child;
if (last && child==last) last=undefined;
if (last && child[last.type]==last) last=undefined;
}
if (!next) next=first;
if (next) next.focus();
self.render();
return;
}
if (self.lockKeys && !~self.ignoreLocked.indexOf(key.full)) {
return;
}
var focused = self.focused
, grabKeys = self.grabKeys;
if (!grabKeys || ~self.ignoreLocked.indexOf(key.full)) {
self.emit('keypress', ch, key);
self.emit('key ' + key.full, ch, key);
}
// If something changed from the screen key handler, stop.
if (self.grabKeys !== grabKeys || self.lockKeys) {
return;
}
if (focused && focused.keyable) {
focused.emit('keypress', ch, key);
focused.emit('key ' + key.full, ch, key);
}
});
};
Screen.prototype.enableKeys = function(el) {
this._listenKeys(el);
};
Screen.prototype.enableInput = function(el) {
this._listenMouse(el);
this._listenKeys(el);
};
Screen.prototype._initHover = function() {
var self = this;
if (this._hoverText) {
return;
}
this._hoverText = new Box({
screen: this,
left: 0,
top: 0,
tags: false,
height: 'shrink',
width: 'shrink',
border: 'line',
style: {
border: {
fg: 'default'
},
bg: 'default',
fg: 'default'
}
});
this.on('mousemove', function(data) {
if (self._hoverText.detached) return;
self._hoverText.rleft = data.x + 1;
self._hoverText.rtop = data.y;
self.render();
});
this.on('element mouseover', function(el, data) {
if (!el._hoverOptions) return;
self._hoverText.parseTags = el.parseTags;
self._hoverText.setContent(el._hoverOptions.text);
self.append(self._hoverText);
self._hoverText.rleft = data.x + 1;
self._hoverText.rtop = data.y;
self.render();
});
this.on('element mouseout', function() {
if (self._hoverText.detached) return;
self._hoverText.detach();
self.render();
});
// XXX This can cause problems if the
// terminal does not support allMotion.
// Workaround: check to see if content is set.
this.on('element mouseup', function(el) {
if (!self._hoverText.getContent()) return;
if (!el._hoverOptions) return;
self.append(self._hoverText);
self.render();
});
};
/* Depricated
Screen.prototype.__defineGetter__('cols', function() {
return this.program.cols;
});
Screen.prototype.__defineGetter__('rows', function() {
return this.program.rows;
});
Screen.prototype.__defineGetter__('width', function() {
return this.program.cols;
});
Screen.prototype.__defineGetter__('height', function() {
return this.program.rows;
});
*/
Object.defineProperty(Screen.prototype,'cols',{
get: function () {
return this.program.cols;
},
});
Object.defineProperty(Screen.prototype,'rows',{
get: function () {
return this.program.rows;
},
});
Object.defineProperty(Screen.prototype,'width',{
get: function () {
return this.program.cols;
},
});
Object.defineProperty(Screen.prototype,'height',{
get: function () {
return this.program.rows;
},
});
Screen.prototype.alloc = function(dirty) {
var x, y;
this.lines = [];
for (y = 0; y < this.rows; y++) {
this.lines[y] = [];
for (x = 0; x < this.cols; x++) {
this.lines[y][x] = [this.dattr, ' '];
}
this.lines[y].dirty = !!dirty;
}
this.olines = [];
for (y = 0; y < this.rows; y++) {
this.olines[y] = [];
for (x = 0; x < this.cols; x++) {
this.olines[y][x] = [this.dattr, ' '];
}
}
this.program.clear();
};
Screen.prototype.realloc = function() {
return this.alloc(true);
};
Screen.prototype.render = function() {
var self = this;
if (this.destroyed) return;
this.emit('prerender');
this._borderStops = {};
// TODO: Possibly get rid of .dirty altogether.
// TODO: Could possibly drop .dirty and just clear the `lines` buffer every
// time before a screen.render. This way clearRegion doesn't have to be
// called in arbitrary places for the sake of clearing a spot where an
// element used to be (e.g. when an element moves or is hidden). There could
// be some overhead though.
// this.screen.clearRegion(0, this.cols, 0, this.rows);
this._ci = 0;
this.children.forEach(function(el) {
el.index = self._ci++;
//el._rendering = true;
el.render();
//el._rendering = false;
});
this._ci = -1;
if (this.screen.dockBorders) {
this._dockBorders();
}
this.draw(0, this.lines.length - 1);
// XXX Workaround to deal with cursor pos before the screen has rendered and
// lpos is not reliable (stale).
if (this.focused && this.focused._updateCursor) {
this.focused._updateCursor(true);
}
this.renders++;
this.emit('render');
};
Screen.prototype.blankLine = function(ch, dirty) {
var out = [];
for (var x = 0; x < this.cols; x++) {
out[x] = [this.dattr, ch || ' '];
}
out.dirty = dirty;
return out;
};
Screen.prototype.insertLine = function(n, y, top, bottom) {
// if (y === top) return this.insertLineNC(n, y, top, bottom);
if (!this.tput.strings.change_scroll_region
|| !this.tput.strings.delete_line
|| !this.tput.strings.insert_line) return;
this._buf += this.tput.csr(top, bottom);
this._buf += this.tput.cup(y, 0);
this._buf += this.tput.il(n);
this._buf += this.tput.csr(0, this.height - 1);
var j = bottom + 1;
while (n--) {
this.lines.splice(y, 0, this.blankLine());
this.lines.splice(j, 1);
this.olines.splice(y, 0, this.blankLine());
this.olines.splice(j, 1);
}
};
Screen.prototype.deleteLine = function(n, y, top, bottom) {
// if (y === top) return this.deleteLineNC(n, y, top, bottom);
if (!this.tput.strings.change_scroll_region
|| !this.tput.strings.delete_line
|| !this.tput.strings.insert_line) return;
this._buf += this.tput.csr(top, bottom);
this._buf += this.tput.cup(y, 0);
this._buf += this.tput.dl(n);
this._buf += this.tput.csr(0, this.height - 1);
var j = bottom + 1;
while (n--) {
this.lines.splice(j, 0, this.blankLine());
this.lines.splice(y, 1);
this.olines.splice(j, 0, this.blankLine());
this.olines.splice(y, 1);
}
};
// This is how ncurses does it.
// Scroll down (up cursor-wise).
// This will only work for top line deletion as opposed to arbitrary lines.
Screen.prototype.insertLineNC = function(n, y, top, bottom) {
if (!this.tput.strings.change_scroll_region
|| !this.tput.strings.delete_line) return;
this._buf += this.tput.csr(top, bottom);
this._buf += this.tput.cup(top, 0);
this._buf += this.tput.dl(n);
this._buf += this.tput.csr(0, this.height - 1);
var j = bottom + 1;
while (n--) {
this.lines.splice(j, 0, this.blankLine());
this.lines.splice(y, 1);
this.olines.splice(j, 0, this.blankLine());
this.olines.splice(y, 1);
}
};
// This is how ncurses does it.
// Scroll up (down cursor-wise).
// This will only work for bottom line deletion as opposed to arbitrary lines.
Screen.prototype.deleteLineNC = function(n, y, top, bottom) {
if (!this.tput.strings.change_scroll_region
|| !this.tput.strings.delete_line) return;
this._buf += this.tput.csr(top, bottom);
this._buf += this.tput.cup(bottom, 0);
this._buf += Array(n + 1).join('\n');
this._buf += this.tput.csr(0, this.height - 1);
var j = bottom + 1;
while (n--) {
this.lines.splice(j, 0, this.blankLine());
this.lines.splice(y, 1);
this.olines.splice(j, 0, this.blankLine());
this.olines.splice(y, 1);
}
};
Screen.prototype.insertBottom = function(top, bottom) {
return this.deleteLine(1, top, top, bottom);
};
Screen.prototype.insertTop = function(top, bottom) {
return this.insertLine(1, top, top, bottom);
};
Screen.prototype.deleteBottom = function(top, bottom) {
return this.clearRegion(0, this.width, bottom, bottom);
};
Screen.prototype.deleteTop = function(top, bottom) {
// Same as: return this.insertBottom(top, bottom);
return this.deleteLine(1, top, top, bottom);
};
// Parse the sides of an element to determine
// whether an element has uniform cells on
// both sides. If it does, we can use CSR to
// optimize scrolling on a scrollable element.
// Not exactly sure how worthwile this is.
// This will cause a performance/cpu-usage hit,
// but will it be less or greater than the
// performance hit of slow-rendering scrollable
// boxes with clean sides?
Screen.prototype.cleanSides = function(el) {
var pos = el.lpos;
if (!pos) {
return false;
}
if (pos._cleanSides != null) {
return pos._cleanSides;
}
if (pos.xi <= 0 && pos.xl >= this.width) {
return pos._cleanSides = true;
}
if (this.options.fastCSR) {
// Maybe just do this instead of parsing.
if (pos.yi < 0) return pos._cleanSides = false;
if (pos.yl > this.height) return pos._cleanSides = false;
if (this.width - (pos.xl - pos.xi) < 40) {
return pos._cleanSides = true;
}
return pos._cleanSides = false;
}
if (!this.options.smartCSR) {
return false;
}
// The scrollbar can't update properly, and there's also a
// chance that the scrollbar may get moved around senselessly.
// NOTE: In pratice, this doesn't seem to be the case.
// if (this.scrollbar) {
// return pos._cleanSides = false;
// }
// Doesn't matter if we're only a height of 1.
// if ((pos.yl - el.ibottom) - (pos.yi + el.itop) <= 1) {
// return pos._cleanSides = false;
// }
var yi = pos.yi + el.itop
, yl = pos.yl - el.ibottom
, first
, ch
, x
, y;
if (pos.yi < 0) return pos._cleanSides = false;
if (pos.yl > this.height) return pos._cleanSides = false;
if (pos.xi - 1 < 0) return pos._cleanSides = true;
if (pos.xl > this.width) return pos._cleanSides = true;
for (x = pos.xi - 1; x >= 0; x--) {
if (!this.olines[yi]) break;
first = this.olines[yi][x];
for (y = yi; y < yl; y++) {
if (!this.olines[y] || !this.olines[y][x]) break;
ch = this.olines[y][x];
if (ch[0] !== first[0] || ch[1] !== first[1]) {
return pos._cleanSides = false;
}
}
}
for (x = pos.xl; x < this.width; x++) {
if (!this.olines[yi]) break;
first = this.olines[yi][x];
for (y = yi; y < yl; y++) {
if (!this.olines[y] || !this.olines[y][x]) break;
ch = this.olines[y][x];
if (ch[0] !== first[0] || ch[1] !== first[1]) {
return pos._cleanSides = false;
}
}
}
return pos._cleanSides = true;
};
Screen.prototype._dockBorders = function() {
var lines = this.lines
, stops = this._borderStops
, i
, y
, x
, ch;
// var keys, stop;
//
// keys = Object.keys(this._borderStops)
// .map(function(k) { return +k; })
// .sort(function(a, b) { return a - b; });
//
// for (i = 0; i < keys.length; i++) {
// y = keys[i];
// if (!lines[y]) continue;
// stop = this._borderStops[y];
// for (x = stop.xi; x < stop.xl; x++) {
stops = Object.keys(stops)
.map(function(k) { return +k; })
.sort(function(a, b) { return a - b; });
for (i = 0; i < stops.length; i++) {
y = stops[i];
if (!lines[y]) continue;
for (x = 0; x < this.width; x++) {
ch = lines[y][x][1];
if (angles[ch]) {
lines[y][x][1] = this._getAngle(lines, x, y);
lines[y].dirty = true;
}
}
}
};
Screen.prototype._getAngle = function(lines, x, y) {
var angle = 0
, attr = lines[y][x][0]
, ch = lines[y][x][1];
if (lines[y][x - 1] && langles[lines[y][x - 1][1]]) {
if (!this.options.ignoreDockContrast) {
if (lines[y][x - 1][0] !== attr) return ch;
}
angle |= 1 << 3;
}
if (lines[y - 1] && uangles[lines[y - 1][x][1]]) {
if (!this.options.ignoreDockContrast) {
if (lines[y - 1][x][0] !== attr) return ch;
}
angle |= 1 << 2;
}
if (lines[y][x + 1] && rangles[lines[y][x + 1][1]]) {
if (!this.options.ignoreDockContrast) {
if (lines[y][x + 1][0] !== attr) return ch;
}
angle |= 1 << 1;
}
if (lines[y + 1] && dangles[lines[y + 1][x][1]]) {
if (!this.options.ignoreDockContrast) {
if (lines[y + 1][x][0] !== attr) return ch;
}
angle |= 1 << 0;
}
// Experimental: fixes this situation:
// +----------+
// | <-- empty space here, should be a T angle
// +-------+ |
// | | |
// +-------+ |
// | |
// +----------+
// if (uangles[lines[y][x][1]]) {
// if (lines[y + 1] && cdangles[lines[y + 1][x][1]]) {
// if (!this.options.ignoreDockContrast) {
// if (lines[y + 1][x][0] !== attr) return ch;
// }
// angle |= 1 << 0;
// }
// }
return angleTable[angle] || ch;
};
Screen.prototype.draw = function(start, end) {
// this.emit('predraw');
var x
, y
, line
, out
, ch
, data
, attr
, fg
, bg
, flags;
var main = ''
, pre
, post;
var clr
, neq
, xx;
var lx = -1
, ly = -1
, o;
var acs;
if (this._buf) {
main += this._buf;
this._buf = '';
}
for (y = start; y <= end; y++) {
line = this.lines[y];
o = this.olines[y];
if (!line.dirty && !(this.cursor.artificial && y === this.program.y)) {
continue;
}
line.dirty = false;
out = '';
attr = this.dattr;
for (x = 0; x < line.length; x++) {
data = line[x][0];
ch = line[x][1];
// Render the artificial cursor.
if (this.cursor.artificial
&& !this.cursor._hidden
&& this.cursor._state
&& x === this.program.x
&& y === this.program.y) {
var cattr = this._cursorAttr(this.cursor, data);
if (cattr.ch) ch = cattr.ch;
data = cattr.attr;
}
// Take advantage of xterm's back_color_erase feature by using a
// lookahead. Stop spitting out so many damn spaces. NOTE: Is checking
// the bg for non BCE terminals worth the overhead?
if (this.options.useBCE
&& ch === ' '
&& (this.tput.bools.back_color_erase
|| (data & 0x1ff) === (this.dattr & 0x1ff))
&& ((data >> 18) & 8) === ((this.dattr >> 18) & 8)) {
clr = true;
neq = false;
for (xx = x; xx < line.length; xx++) {
if (line[xx][0] !== data || line[xx][1] !== ' ') {
clr = false;
break;
}
if (line[xx][0] !== o[xx][0] || line[xx][1] !== o[xx][1]) {
neq = true;
}
}
if (clr && neq) {
lx = -1, ly = -1;
if (data !== attr) {
out += this.codeAttr(data);
attr = data;
}
out += this.tput.cup(y, x);
out += this.tput.el();
for (xx = x; xx < line.length; xx++) {
o[xx][0] = data;
o[xx][1] = ' ';
}
break;
}
// If there's more than 10 spaces, use EL regardless
// and start over drawing the rest of line. Might
// not be worth it. Try to use ECH if the terminal
// supports it. Maybe only try to use ECH here.
// //if (this.tput.strings.erase_chars)
// if (!clr && neq && (xx - x) > 10) {
// lx = -1, ly = -1;
// if (data !== attr) {
// out += this.codeAttr(data);
// attr = data;
// }
// out += this.tput.cup(y, x);
// if (this.tput.strings.erase_chars) {
// // Use erase_chars to avoid erasing the whole line.
// out += this.tput.ech(xx - x);
// } else {
// out += this.tput.el();
// }
// if (this.tput.strings.parm_right_cursor) {
// out += this.tput.cuf(xx - x);
// } else {
// out += this.tput.cup(y, xx);
// }
// this.fillRegion(data, ' ',
// x, this.tput.strings.erase_chars ? xx : line.length,
// y, y + 1);
// x = xx - 1;
// continue;
// }
// Skip to the next line if the
// rest of the line is already drawn.
// if (!neq) {
// for (; xx < line.length; xx++) {
// if (line[xx][0] !== o[xx][0] || line[xx][1] !== o[xx][1]) {
// neq = true;
// break;
// }
// }
// if (!neq) {
// attr = data;
// break;
// }
// }
}
// Optimize by comparing the real output
// buffer to the pending output buffer.
if (data === o[x][0] && ch === o[x][1]) {
if (lx === -1) {
lx = x;
ly = y;
}
continue;
} else if (lx !== -1) {
if (this.tput.strings.parm_right_cursor) {
out += y === ly
? this.tput.cuf(x - lx)
: this.tput.cup(y, x);
} else {
out += this.tput.cup(y, x);
}
lx = -1, ly = -1;
}
o[x][0] = data;
o[x][1] = ch;
if (data !== attr) {
if (attr !== this.dattr) {
out += '\x1b[m';
}
if (data !== this.dattr) {
out += '\x1b[';
bg = data & 0x1ff;
fg = (data >> 9) & 0x1ff;
flags = data >> 18;
// bold
if (flags & 1) {
out += '1;';
}
// underline
if (flags & 2) {
out += '4;';
}
// blink
if (flags & 4) {
out += '5;';
}
// inverse
if (flags & 8) {
out += '7;';
}
// invisible
if (flags & 16) {
out += '8;';
}
if (bg !== 0x1ff) {
bg = this._reduceColor(bg);
if (bg < 16) {
if (bg < 8) {
bg += 40;
} else if (bg < 16) {
bg -= 8;
bg += 100;
}
out += bg + ';';
} else {
out += '48;5;' + bg + ';';
}
}
if (fg !== 0x1ff) {
fg = this._reduceColor(fg);
if (fg < 16) {
if (fg < 8) {
fg += 30;
} else if (fg < 16) {
fg -= 8;
fg += 90;
}
out += fg + ';';
} else {
out += '38;5;' + fg + ';';
}
}
if (out[out.length - 1] === ';') out = out.slice(0, -1);
out += 'm';
}
}
// If we find a double-width char, eat the next character which should be
// a space due to parseContent's behavior.
if (this.fullUnicode) {
// If this is a surrogate pair double-width char, we can ignore it
// because parseContent already counted it as length=2.
if (unicode.charWidth(line[x][1]) === 2) {
// NOTE: At cols=44, the bug that is avoided
// by the angles check occurs in widget-unicode:
// Might also need: `line[x + 1][0] !== line[x][0]`
// for borderless boxes?
if (x === line.length - 1 || angles[line[x + 1][1]]) {
// If we're at the end, we don't have enough space for a
// double-width. Overwrite it with a space and ignore.
ch = ' ';
o[x][1] = '\0';
} else {
// ALWAYS refresh double-width chars because this special cursor
// behavior is needed. There may be a more efficient way of doing
// this. See above.
o[x][1] = '\0';
// Eat the next character by moving forward and marking as a
// space (which it is).
o[++x][1] = '\0';
}
}
}
// Attempt to use ACS for supported characters.
// This is not ideal, but it's how ncurses works.
// There are a lot of terminals that support ACS
// *and UTF8, but do not declare U8. So ACS ends
// up being used (slower than utf8). Terminals
// that do not support ACS and do not explicitly
// support UTF8 get their unicode characters
// replaced with really ugly ascii characters.
// It is possible there is a terminal out there
// somewhere that does not support ACS, but
// supports UTF8, but I imagine it's unlikely.
// Maybe remove !this.tput.unicode check, however,
// this seems to be the way ncurses does it.
if (this.tput.strings.enter_alt_charset_mode
&& !this.tput.brokenACS && (this.tput.acscr[ch] || acs)) {
// Fun fact: even if this.tput.brokenACS wasn't checked here,
// the linux console would still work fine because the acs
// table would fail the check of: this.tput.acscr[ch]
if (this.tput.acscr[ch]) {
if (acs) {
ch = this.tput.acscr[ch];
} else {
ch = this.tput.smacs()
+ this.tput.acscr[ch];
acs = true;
}
} else if (acs) {
ch = this.tput.rmacs() + ch;
acs = false;
}
} else {
// U8 is not consistently correct. Some terminfo's
// terminals that do not declare it may actually
// support utf8 (e.g. urxvt), but if the terminal
// does not declare support for ACS (and U8), chances
// are it does not support UTF8. This is probably
// the "safest" way to do this. Should fix things
// like sun-color.
// NOTE: It could be the case that the $LANG
// is all that matters in some cases:
// if (!this.tput.unicode && ch > '~') {
if (!this.tput.unicode && this.tput.numbers.U8 !== 1 && ch > '~') {
ch = this.tput.utoa[ch] || '?';
}
}
out += ch;
attr = data;
}
if (attr !== this.dattr) {
out += '\x1b[m';
}
if (out) {
main += this.tput.cup(y, 0) + out;
}
}
if (acs) {
main += this.tput.rmacs();
acs = false;
}
if (main) {
pre = '';
post = '';
pre += this.tput.sc();
post += this.tput.rc();
if (!this.program.cursorHidden) {
pre += this.tput.civis();
post += this.tput.cnorm();
}
// this.program.flush();
// this.program._owrite(pre + main + post);
this.program._write(pre + main + post);
}
// this.emit('draw');
};
Screen.prototype._reduceColor = function(color) {
return colors.reduce(color, this.tput.colors);
};
// Convert an SGR string to our own attribute format.
Screen.prototype.attrCode = function(code, cur, def) {
var flags = (cur >> 18) & 0x1ff
, fg = (cur >> 9) & 0x1ff
, bg = cur & 0x1ff
, c
, i;
code = code.slice(2, -1).split(';');
if (!code[0]) code[0] = '0';
for (i = 0; i < code.length; i++) {
c = +code[i] || 0;
switch (c) {
case 0: // normal
bg = def & 0x1ff;
fg = (def >> 9) & 0x1ff;
flags = (def >> 18) & 0x1ff;
break;
case 1: // bold
flags |= 1;
break;
case 22:
flags = (def >> 18) & 0x1ff;
break;
case 4: // underline
flags |= 2;
break;
case 24:
flags = (def >> 18) & 0x1ff;
break;
case 5: // blink
flags |= 4;
break;
case 25:
flags = (def >> 18) & 0x1ff;
break;
case 7: // inverse
flags |= 8;
break;
case 27:
flags = (def >> 18) & 0x1ff;
break;
case 8: // invisible
flags |= 16;
break;
case 28:
flags = (def >> 18) & 0x1ff;
break;
case 39: // default fg
fg = (def >> 9) & 0x1ff;
break;
case 49: // default bg
bg = def & 0x1ff;
break;
case 100: // default fg/bg
fg = (def >> 9) & 0x1ff;
bg = def & 0x1ff;
break;
default: // color
if (c === 48 && +code[i+1] === 5) {
i += 2;
bg = +code[i];
break;
} else if (c === 48 && +code[i+1] === 2) {
i += 2;
bg = colors.match(+code[i], +code[i+1], +code[i+2]);
if (bg === -1) bg = def & 0x1ff;
i += 2;
break;
} else if (c === 38 && +code[i+1] === 5) {
i += 2;
fg = +code[i];
break;
} else if (c === 38 && +code[i+1] === 2) {
i += 2;
fg = colors.match(+code[i], +code[i+1], +code[i+2]);
if (fg === -1) fg = (def >> 9) & 0x1ff;
i += 2;
break;
}
if (c >= 40 && c <= 47) {
bg = c - 40;
} else if (c >= 100 && c <= 107) {
bg = c - 100;
bg += 8;
} else if (c === 49) {
bg = def & 0x1ff;
} else if (c >= 30 && c <= 37) {
fg = c - 30;
} else if (c >= 90 && c <= 97) {
fg = c - 90;
fg += 8;
} else if (c === 39) {
fg = (def >> 9) & 0x1ff;
} else if (c === 100) {
fg = (def >> 9) & 0x1ff;
bg = def & 0x1ff;
}
break;
}
}
return (flags << 18) | (fg << 9) | bg;
};
// Convert our own attribute format to an SGR string.
Screen.prototype.codeAttr = function(code) {
var flags = (code >> 18) & 0x1ff
, fg = (code >> 9) & 0x1ff
, bg = code & 0x1ff
, out = '';
// bold
if (flags & 1) {
out += '1;';
}
// underline
if (flags & 2) {
out += '4;';
}
// blink
if (flags & 4) {
out += '5;';
}
// inverse
if (flags & 8) {
out += '7;';
}
// invisible
if (flags & 16) {
out += '8;';
}
if (bg !== 0x1ff) {
bg = this._reduceColor(bg);
if (bg < 16) {
if (bg < 8) {
bg += 40;
} else if (bg < 16) {
bg -= 8;
bg += 100;
}
out += bg + ';';
} else {
out += '48;5;' + bg + ';';
}
}
if (fg !== 0x1ff) {
fg = this._reduceColor(fg);
if (fg < 16) {
if (fg < 8) {
fg += 30;
} else if (fg < 16) {
fg -= 8;
fg += 90;
}
out += fg + ';';
} else {
out += '38;5;' + fg + ';';
}
}
if (out[out.length - 1] === ';') out = out.slice(0, -1);
return '\x1b[' + out + 'm';
};
Screen.prototype.focusOffset = function(offset) {
var shown = this.keyable.filter(function(el) {
return !el.detached && el.visible;
}).length;
if (!shown || !offset) {
return;
}
var i = this.keyable.indexOf(this.focused);
if (!~i) return;
if (offset > 0) {
while (offset--) {
if (++i > this.keyable.length - 1) i = 0;
if (this.keyable[i].detached || !this.keyable[i].visible) offset++;
}
} else {
offset = -offset;
while (offset--) {
if (--i < 0) i = this.keyable.length - 1;
if (this.keyable[i].detached || !this.keyable[i].visible) offset++;
}
}
return this.keyable[i].focus();
};
Screen.prototype.focusPrev =
Screen.prototype.focusPrevious = function() {
return this.focusOffset(-1);
};
Screen.prototype.focusNext = function() {
return this.focusOffset(1);
};
Screen.prototype.focusPush = function(el) {
if (!el) return;
var old = this.history[this.history.length - 1];
if (this.history.length === 10) {
this.history.shift();
}
this.history.push(el);
this._focus(el, old);
};
Screen.prototype.focusPop = function() {
var old = this.history.pop();
if (this.history.length) {
this._focus(this.history[this.history.length - 1], old);
}
return old;
};
Screen.prototype.saveFocus = function() {
return this._savedFocus = this.focused;
};
Screen.prototype.restoreFocus = function() {
if (!this._savedFocus) return;
this._savedFocus.focus();
delete this._savedFocus;
return this.focused;
};
Screen.prototype.rewindFocus = function() {
var old = this.history.pop()
, el;
while (this.history.length) {
el = this.history.pop();
if (!el.detached && el.visible) {
this.history.push(el);
this._focus(el, old);
return el;
}
}
if (old) {
old.emit('blur');
}
};
Screen.prototype._focus = function(self, old) {
// Find a scrollable ancestor if we have one.
var el = self;
while (el = el.parent) {
if (el.scrollable) break;
}
// If we're in a scrollable element,
// automatically scroll to the focused element.
if (el && !el.detached) {
// NOTE: This is different from the other "visible" values - it needs the
// visible height of the scrolling element itself, not the element within
// it.
var visible = self.screen.height - el.atop - el.itop - el.abottom - el.ibottom;
if (self.rtop < el.childBase) {
el.scrollTo(self.rtop);
self.screen.render();
} else if (self.rtop + self.height - self.ibottom > el.childBase + visible) {
// Explanation for el.itop here: takes into account scrollable elements
// with borders otherwise the element gets covered by the bottom border:
el.scrollTo(self.rtop - (el.height - self.height) + el.itop, true);
self.screen.render();
}
}
if (old) {
old.emit('blur', self);
}
self.emit('focus', old);
};
/* Depricated
Screen.prototype.__defineGetter__('focused', function() {
return this.history[this.history.length - 1];
});
Screen.prototype.__defineSetter__('focused', function(el) {
return this.focusPush(el);
});
*/
Object.defineProperty(Screen.prototype,'focused',{
get: function () {return this.history[this.history.length - 1];},
set: function (el) {
return this.focusPush(el);
}
});
Screen.prototype.clearRegion = function(xi, xl, yi, yl, override) {
return this.fillRegion(this.dattr, ' ', xi, xl, yi, yl, override);
};
Screen.prototype.fillRegion = function(attr, ch, xi, xl, yi, yl, override) {
var lines = this.lines
, cell
, xx;
if (xi < 0) xi = 0;
if (yi < 0) yi = 0;
for (; yi < yl; yi++) {
if (!lines[yi]) break;
for (xx = xi; xx < xl; xx++) {
cell = lines[yi][xx];
if (!cell) break;
if (override || attr !== cell[0] || ch !== cell[1]) {
lines[yi][xx][0] = attr;
lines[yi][xx][1] = ch;
lines[yi].dirty = true;
}
}
}
};
Screen.prototype.key = function() {
return this.program.key.apply(this, arguments);
};
Screen.prototype.onceKey = function() {
return this.program.onceKey.apply(this, arguments);
};
Screen.prototype.unkey =
Screen.prototype.removeKey = function() {
return this.program.unkey.apply(this, arguments);
};
Screen.prototype.spawn = function(file, args, options) {
if (!Array.isArray(args)) {
options = args;
args = [];
}
var screen = this
, program = screen.program
, spawn = require('child_process').spawn
, mouse = program.mouseEnabled
, ps;
options = options || {};
options.stdio = options.stdio || 'inherit';
program.lsaveCursor('spawn');
// program.csr(0, program.rows - 1);
program.normalBuffer();
program.showCursor();
if (mouse) program.disableMouse();
var write = program.output.write;
program.output.write = function() {};
program.input.pause();
if (program.input.setRawMode) {
program.input.setRawMode(false);
}
var resume = function() {
if (resume.done) return;
resume.done = true;
if (program.input.setRawMode) {
program.input.setRawMode(true);
}
program.input.resume();
program.output.write = write;
program.alternateBuffer();
// program.csr(0, program.rows - 1);
if (mouse) {
program.enableMouse();
if (screen.options.sendFocus) {
screen.program.setMouse({ sendFocus: true }, true);
}
}
screen.alloc();
screen.render();
screen.program.lrestoreCursor('spawn', true);
};
ps = spawn(file, args, options);
ps.on('error', resume);
ps.on('exit', resume);
return ps;
};
Screen.prototype.exec = function(file, args, options, callback) {
var ps = this.spawn(file, args, options);
ps.on('error', function(err) {
if (!callback) return;
return callback(err, false);
});
ps.on('exit', function(code) {
if (!callback) return;
return callback(null, code === 0);
});
return ps;
};
Screen.prototype.readEditor = function(options, callback) {
if (typeof options === 'string') {
options = { editor: options };
}
if (!callback) {
callback = options;
options = null;
}
if (!callback) {
callback = function() {};
}
options = options || {};
var self = this
, editor = options.editor || process.env.EDITOR || 'vi'
, name = options.name || process.title || 'blessed'
, rnd = Math.random().toString(36).split('.').pop()
, file = '/tmp/' + name + '.' + rnd
, args = [file]
, opt;
opt = {
stdio: 'inherit',
env: process.env,
cwd: process.env.HOME
};
function writeFile(callback) {
if (!options.value) return callback();
return fs.writeFile(file, options.value, callback);
}
return writeFile(function(err) {
if (err) return callback(err);
return self.exec(editor, args, opt, function(err, success) {
if (err) return callback(err);
return fs.readFile(file, 'utf8', function(err, data) {
return fs.unlink(file, function() {
if (!success) return callback(new Error('Unsuccessful.'));
if (err) return callback(err);
return callback(null, data);
});
});
});
});
};
Screen.prototype.displayImage = function(file, callback) {
if (!file) {
if (!callback) return;
return callback(new Error('No image.'));
}
file = path.resolve(process.cwd(), file);
if (!~file.indexOf('://')) {
file = 'file://' + file;
}
var args = ['w3m', '-T', 'text/html'];
var input = '<title>press q to exit</title>'
+ '<img align="center" src="' + file + '">';
var opt = {
stdio: ['pipe', 1, 2],
env: process.env,
cwd: process.env.HOME
};
var ps = this.spawn(args[0], args.slice(1), opt);
ps.on('error', function(err) {
if (!callback) return;
return callback(err);
});
ps.on('exit', function(code) {
if (!callback) return;
if (code !== 0) return callback(new Error('Exit Code: ' + code));
return callback(null, code === 0);
});
ps.stdin.write(input + '\n');
ps.stdin.end();
};
Screen.prototype.setEffects = function(el, fel, over, out, effects, temp) {
if (!effects) return;
var tmp = {};
if (temp) el[temp] = tmp;
if (typeof el !== 'function') {
var _el = el;
el = function() { return _el; };
}
fel.on(over, function() {
var element = el();
Object.keys(effects).forEach(function(key) {
var val = effects[key];
if (val !== null && typeof val === 'object') {
tmp[key] = tmp[key] || {};
// element.style[key] = element.style[key] || {};
Object.keys(val).forEach(function(k) {
var v = val[k];
tmp[key][k] = element.style[key][k];
element.style[key][k] = v;
});
return;
}
tmp[key] = element.style[key];
element.style[key] = val;
});
element.screen.render();
});
fel.on(out, function() {
var element = el();
Object.keys(effects).forEach(function(key) {
var val = effects[key];
if (val !== null && typeof val === 'object') {
tmp[key] = tmp[key] || {};
// element.style[key] = element.style[key] || {};
Object.keys(val).forEach(function(k) {
if (tmp[key].hasOwnProperty(k)) {
element.style[key][k] = tmp[key][k];
}
});
return;
}
if (tmp.hasOwnProperty(key)) {
element.style[key] = tmp[key];
}
});
element.screen.render();
});
};
Screen.prototype.sigtstp = function(callback) {
var self = this;
this.program.sigtstp(function() {
self.alloc();
self.render();
self.program.lrestoreCursor('pause', true);
if (callback) callback();
});
};
Screen.prototype.copyToClipboard = function(text) {
return this.program.copyToClipboard(text);
};
Screen.prototype.cursorShape = function(shape, blink) {
var self = this;
this.cursor.shape = shape || 'block';
this.cursor.blink = blink || false;
this.cursor._set = true;
if (this.cursor.artificial) {
if (!this.program.hideCursor_old) {
var hideCursor = this.program.hideCursor;
this.program.hideCursor_old = this.program.hideCursor;
this.program.hideCursor = function() {
hideCursor.call(self.program);
self.cursor._hidden = true;
if (self.renders) self.render();
};
}
if (!this.program.showCursor_old) {
var showCursor = this.program.showCursor;
this.program.showCursor_old = this.program.showCursor;
this.program.showCursor = function() {
self.cursor._hidden = false;
if (self.program._exiting) showCursor.call(self.program);
if (self.renders) self.render();
};
}
if (!this._cursorBlink) {
this._cursorBlink = setInterval(function() {
if (!self.cursor.blink) return;
self.cursor._state ^= 1;
if (self.renders) self.render();
}, 500);
if (this._cursorBlink.unref) {
this._cursorBlink.unref();
}
}
return true;
}
return this.program.cursorShape(this.cursor.shape, this.cursor.blink);
};
Screen.prototype.cursorColor = function(color) {
this.cursor.color = color != null
? colors.convert(color)
: null;
this.cursor._set = true;
if (this.cursor.artificial) {
return true;
}
return this.program.cursorColor(colors.ncolors[this.cursor.color]);
};
Screen.prototype.cursorReset =
Screen.prototype.resetCursor = function() {
this.cursor.shape = 'block';
this.cursor.blink = false;
this.cursor.color = null;
this.cursor._set = false;
if (this.cursor.artificial) {
this.cursor.artificial = false;
if (this.program.hideCursor_old) {
this.program.hideCursor = this.program.hideCursor_old;
delete this.program.hideCursor_old;
}
if (this.program.showCursor_old) {
this.program.showCursor = this.program.showCursor_old;
delete this.program.showCursor_old;
}
if (this._cursorBlink) {
clearInterval(this._cursorBlink);
delete this._cursorBlink;
}
return true;
}
return this.program.cursorReset();
};
Screen.prototype._cursorAttr = function(cursor, dattr) {
var attr = dattr || this.dattr
, cattr
, ch;
if (cursor.shape === 'line') {
attr &= ~(0x1ff << 9);
attr |= 7 << 9;
ch = '\u2502';
} else if (cursor.shape === 'underline') {
attr &= ~(0x1ff << 9);
attr |= 7 << 9;
attr |= 2 << 18;
} else if (cursor.shape === 'block') {
attr &= ~(0x1ff << 9);
attr |= 7 << 9;
attr |= 8 << 18;
} else if (typeof cursor.shape === 'object' && cursor.shape) {
cattr = Element.prototype.sattr.call(cursor, cursor.shape);
if (cursor.shape.bold || cursor.shape.underline
|| cursor.shape.blink || cursor.shape.inverse
|| cursor.shape.invisible) {
attr &= ~(0x1ff << 18);
attr |= ((cattr >> 18) & 0x1ff) << 18;
}
if (cursor.shape.fg) {
attr &= ~(0x1ff << 9);
attr |= ((cattr >> 9) & 0x1ff) << 9;
}
if (cursor.shape.bg) {
attr &= ~(0x1ff << 0);
attr |= cattr & 0x1ff;
}
if (cursor.shape.ch) {
ch = cursor.shape.ch;
}
}
if (cursor.color != null) {
attr &= ~(0x1ff << 9);
attr |= cursor.color << 9;
}
return {
ch: ch,
attr: attr
};
};
Screen.prototype.screenshot = function(xi, xl, yi, yl, term) {
if (xi == null) xi = 0;
if (xl == null) xl = this.cols;
if (yi == null) yi = 0;
if (yl == null) yl = this.rows;
if (xi < 0) xi = 0;
if (yi < 0) yi = 0;
var x
, y
, line
, out
, ch
, data
, attr;
var sdattr = this.dattr;
if (term) {
this.dattr = term.defAttr;
}
var main = '';
for (y = yi; y < yl; y++) {
line = term
? term.lines[y]
: this.lines[y];
if (!line) break;
out = '';
attr = this.dattr;
for (x = xi; x < xl; x++) {
if (!line[x]) break;
data = line[x][0];
ch = line[x][1];
if (data !== attr) {
if (attr !== this.dattr) {
out += '\x1b[m';
}
if (data !== this.dattr) {
var _data = data;
if (term) {
if (((_data >> 9) & 0x1ff) === 257) _data |= 0x1ff << 9;
if ((_data & 0x1ff) === 256) _data |= 0x1ff;
}
out += this.codeAttr(_data);
}
}
if (this.fullUnicode) {
if (unicode.charWidth(line[x][1]) === 2) {
if (x === xl - 1) {
ch = ' ';
} else {
x++;
}
}
}
out += ch;
attr = data;
}
if (attr !== this.dattr) {
out += '\x1b[m';
}
if (out) {
main += (y > 0 ? '\n' : '') + out;
}
}
main = main.replace(/(?:\s*\x1b\[40m\s*\x1b\[m\s*)*$/, '') + '\n';
if (term) {
this.dattr = sdattr;
}
return main;
};
/**
* Positioning
*/
Screen.prototype._getPos = function() {
return this;
};
/**
* Angle Table
*/
var angles = {
'\u2518': true, // '┘'
'\u2510': true, // '┐'
'\u250c': true, // '┌'
'\u2514': true, // '└'
'\u253c': true, // '┼'
'\u251c': true, // '├'
'\u2524': true, // '┤'
'\u2534': true, // '┴'
'\u252c': true, // '┬'
'\u2502': true, // '│'
'\u2500': true // '─'
};
var langles = {
'\u250c': true, // '┌'
'\u2514': true, // '└'
'\u253c': true, // '┼'
'\u251c': true, // '├'
'\u2534': true, // '┴'
'\u252c': true, // '┬'
'\u2500': true // '─'
};
var uangles = {
'\u2510': true, // '┐'
'\u250c': true, // '┌'
'\u253c': true, // '┼'
'\u251c': true, // '├'
'\u2524': true, // '┤'
'\u252c': true, // '┬'
'\u2502': true // '│'
};
var rangles = {
'\u2518': true, // '┘'
'\u2510': true, // '┐'
'\u253c': true, // '┼'
'\u2524': true, // '┤'
'\u2534': true, // '┴'
'\u252c': true, // '┬'
'\u2500': true // '─'
};
var dangles = {
'\u2518': true, // '┘'
'\u2514': true, // '└'
'\u253c': true, // '┼'
'\u251c': true, // '├'
'\u2524': true, // '┤'
'\u2534': true, // '┴'
'\u2502': true // '│'
};
// var cdangles = {
// '\u250c': true // '┌'
// };
// Every ACS angle character can be
// represented by 4 bits ordered like this:
// [langle][uangle][rangle][dangle]
var angleTable = {
'0000': '', // ?
'0001': '\u2502', // '│' // ?
'0010': '\u2500', // '─' // ??
'0011': '\u250c', // '┌'
'0100': '\u2502', // '│' // ?
'0101': '\u2502', // '│'
'0110': '\u2514', // '└'
'0111': '\u251c', // '├'
'1000': '\u2500', // '─' // ??
'1001': '\u2510', // '┐'
'1010': '\u2500', // '─' // ??
'1011': '\u252c', // '┬'
'1100': '\u2518', // '┘'
'1101': '\u2524', // '┤'
'1110': '\u2534', // '┴'
'1111': '\u253c' // '┼'
};
Object.keys(angleTable).forEach(function(key) {
angleTable[parseInt(key, 2)] = angleTable[key];
delete angleTable[key];
});
/**
* Expose
*/
module.exports = Screen;
};
BundleModuleCode['term/unicode']=function (module,exports){
/**
* unicode.js - east asian width and surrogate pairs
* Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
* https://github.com/chjj/blessed
* Borrowed from vangie/east-asian-width, komagata/eastasianwidth,
* and mathiasbynens/String.prototype.codePointAt. Licenses below.
*/
// east-asian-width
//
// Copyright (c) 2015 Vangie Du
// https://github.com/vangie/east-asian-width
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// eastasianwidth
//
// Copyright (c) 2013, Masaki Komagata
// https://github.com/komagata/eastasianwidth
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// String.prototype.codePointAt
//
// Copyright Mathias Bynens <https://mathiasbynens.be/>
// https://github.com/mathiasbynens/String.prototype.codePointAt
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// String.fromCodePoint
//
// Copyright Mathias Bynens <https://mathiasbynens.be/>
// https://github.com/mathiasbynens/String.fromCodePoint
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
var stringFromCharCode = String.fromCharCode;
var floor = Math.floor;
/**
* Wide, Surrogates, and Combining
*/
exports.charWidth = function(str, i) {
var point = typeof str !== 'number'
? exports.codePointAt(str, i || 0)
: str;
// nul
if (point === 0) return 0;
// tab
if (point === 0x09) {
if (!exports.blessed) {
// TODO? ??? exports.blessed = require('../');
}
return exports.blessed.screen.global
? exports.blessed.screen.global.tabc.length
: 8;
}
// 8-bit control characters (2-width according to unicode??)
if (point < 32 || (point >= 0x7f && point < 0xa0)) {
return 0;
}
// search table of non-spacing characters
// is ucs combining or C0/C1 control character
if (exports.combining[point]) {
return 0;
}
// check for double-wide
// if (point >= 0x1100
// && (point <= 0x115f // Hangul Jamo init. consonants
// || point === 0x2329 || point === 0x232a
// || (point >= 0x2e80 && point <= 0xa4cf
// && point !== 0x303f) // CJK ... Yi
// || (point >= 0xac00 && point <= 0xd7a3) // Hangul Syllables
// || (point >= 0xf900 && point <= 0xfaff) // CJK Compatibility Ideographs
// || (point >= 0xfe10 && point <= 0xfe19) // Vertical forms
// || (point >= 0xfe30 && point <= 0xfe6f) // CJK Compatibility Forms
// || (point >= 0xff00 && point <= 0xff60) // Fullwidth Forms
// || (point >= 0xffe0 && point <= 0xffe6)
// || (point >= 0x20000 && point <= 0x2fffd)
// || (point >= 0x30000 && point <= 0x3fffd))) {
// return 2;
// }
// check for double-wide
if ((0x3000 === point)
|| (0xFF01 <= point && point <= 0xFF60)
|| (0xFFE0 <= point && point <= 0xFFE6)) {
return 2;
}
if ((0x1100 <= point && point <= 0x115F)
|| (0x11A3 <= point && point <= 0x11A7)
|| (0x11FA <= point && point <= 0x11FF)
|| (0x2329 <= point && point <= 0x232A)
|| (0x2E80 <= point && point <= 0x2E99)
|| (0x2E9B <= point && point <= 0x2EF3)
|| (0x2F00 <= point && point <= 0x2FD5)
|| (0x2FF0 <= point && point <= 0x2FFB)
|| (0x3001 <= point && point <= 0x303E)
|| (0x3041 <= point && point <= 0x3096)
|| (0x3099 <= point && point <= 0x30FF)
|| (0x3105 <= point && point <= 0x312D)
|| (0x3131 <= point && point <= 0x318E)
|| (0x3190 <= point && point <= 0x31BA)
|| (0x31C0 <= point && point <= 0x31E3)
|| (0x31F0 <= point && point <= 0x321E)
|| (0x3220 <= point && point <= 0x3247)
|| (0x3250 <= point && point <= 0x32FE)
|| (0x3300 <= point && point <= 0x4DBF)
|| (0x4E00 <= point && point <= 0xA48C)
|| (0xA490 <= point && point <= 0xA4C6)
|| (0xA960 <= point && point <= 0xA97C)
|| (0xAC00 <= point && point <= 0xD7A3)
|| (0xD7B0 <= point && point <= 0xD7C6)
|| (0xD7CB <= point && point <= 0xD7FB)
|| (0xF900 <= point && point <= 0xFAFF)
|| (0xFE10 <= point && point <= 0xFE19)
|| (0xFE30 <= point && point <= 0xFE52)
|| (0xFE54 <= point && point <= 0xFE66)
|| (0xFE68 <= point && point <= 0xFE6B)
|| (0x1B000 <= point && point <= 0x1B001)
|| (0x1F200 <= point && point <= 0x1F202)
|| (0x1F210 <= point && point <= 0x1F23A)
|| (0x1F240 <= point && point <= 0x1F248)
|| (0x1F250 <= point && point <= 0x1F251)
|| (0x20000 <= point && point <= 0x2F73F)
|| (0x2B740 <= point && point <= 0x2FFFD)
|| (0x30000 <= point && point <= 0x3FFFD)) {
return 2;
}
// CJK Ambiguous
// http://www.unicode.org/reports/tr11/
// http://www.unicode.org/reports/tr11/#Ambiguous
if (process.env.NCURSES_CJK_WIDTH) {
if ((0x00A1 === point)
|| (0x00A4 === point)
|| (0x00A7 <= point && point <= 0x00A8)
|| (0x00AA === point)
|| (0x00AD <= point && point <= 0x00AE)
|| (0x00B0 <= point && point <= 0x00B4)
|| (0x00B6 <= point && point <= 0x00BA)
|| (0x00BC <= point && point <= 0x00BF)
|| (0x00C6 === point)
|| (0x00D0 === point)
|| (0x00D7 <= point && point <= 0x00D8)
|| (0x00DE <= point && point <= 0x00E1)
|| (0x00E6 === point)
|| (0x00E8 <= point && point <= 0x00EA)
|| (0x00EC <= point && point <= 0x00ED)
|| (0x00F0 === point)
|| (0x00F2 <= point && point <= 0x00F3)
|| (0x00F7 <= point && point <= 0x00FA)
|| (0x00FC === point)
|| (0x00FE === point)
|| (0x0101 === point)
|| (0x0111 === point)
|| (0x0113 === point)
|| (0x011B === point)
|| (0x0126 <= point && point <= 0x0127)
|| (0x012B === point)
|| (0x0131 <= point && point <= 0x0133)
|| (0x0138 === point)
|| (0x013F <= point && point <= 0x0142)
|| (0x0144 === point)
|| (0x0148 <= point && point <= 0x014B)
|| (0x014D === point)
|| (0x0152 <= point && point <= 0x0153)
|| (0x0166 <= point && point <= 0x0167)
|| (0x016B === point)
|| (0x01CE === point)
|| (0x01D0 === point)
|| (0x01D2 === point)
|| (0x01D4 === point)
|| (0x01D6 === point)
|| (0x01D8 === point)
|| (0x01DA === point)
|| (0x01DC === point)
|| (0x0251 === point)
|| (0x0261 === point)
|| (0x02C4 === point)
|| (0x02C7 === point)
|| (0x02C9 <= point && point <= 0x02CB)
|| (0x02CD === point)
|| (0x02D0 === point)
|| (0x02D8 <= point && point <= 0x02DB)
|| (0x02DD === point)
|| (0x02DF === point)
|| (0x0300 <= point && point <= 0x036F)
|| (0x0391 <= point && point <= 0x03A1)
|| (0x03A3 <= point && point <= 0x03A9)
|| (0x03B1 <= point && point <= 0x03C1)
|| (0x03C3 <= point && point <= 0x03C9)
|| (0x0401 === point)
|| (0x0410 <= point && point <= 0x044F)
|| (0x0451 === point)
|| (0x2010 === point)
|| (0x2013 <= point && point <= 0x2016)
|| (0x2018 <= point && point <= 0x2019)
|| (0x201C <= point && point <= 0x201D)
|| (0x2020 <= point && point <= 0x2022)
|| (0x2024 <= point && point <= 0x2027)
|| (0x2030 === point)
|| (0x2032 <= point && point <= 0x2033)
|| (0x2035 === point)
|| (0x203B === point)
|| (0x203E === point)
|| (0x2074 === point)
|| (0x207F === point)
|| (0x2081 <= point && point <= 0x2084)
|| (0x20AC === point)
|| (0x2103 === point)
|| (0x2105 === point)
|| (0x2109 === point)
|| (0x2113 === point)
|| (0x2116 === point)
|| (0x2121 <= point && point <= 0x2122)
|| (0x2126 === point)
|| (0x212B === point)
|| (0x2153 <= point && point <= 0x2154)
|| (0x215B <= point && point <= 0x215E)
|| (0x2160 <= point && point <= 0x216B)
|| (0x2170 <= point && point <= 0x2179)
|| (0x2189 === point)
|| (0x2190 <= point && point <= 0x2199)
|| (0x21B8 <= point && point <= 0x21B9)
|| (0x21D2 === point)
|| (0x21D4 === point)
|| (0x21E7 === point)
|| (0x2200 === point)
|| (0x2202 <= point && point <= 0x2203)
|| (0x2207 <= point && point <= 0x2208)
|| (0x220B === point)
|| (0x220F === point)
|| (0x2211 === point)
|| (0x2215 === point)
|| (0x221A === point)
|| (0x221D <= point && point <= 0x2220)
|| (0x2223 === point)
|| (0x2225 === point)
|| (0x2227 <= point && point <= 0x222C)
|| (0x222E === point)
|| (0x2234 <= point && point <= 0x2237)
|| (0x223C <= point && point <= 0x223D)
|| (0x2248 === point)
|| (0x224C === point)
|| (0x2252 === point)
|| (0x2260 <= point && point <= 0x2261)
|| (0x2264 <= point && point <= 0x2267)
|| (0x226A <= point && point <= 0x226B)
|| (0x226E <= point && point <= 0x226F)
|| (0x2282 <= point && point <= 0x2283)
|| (0x2286 <= point && point <= 0x2287)
|| (0x2295 === point)
|| (0x2299 === point)
|| (0x22A5 === point)
|| (0x22BF === point)
|| (0x2312 === point)
|| (0x2460 <= point && point <= 0x24E9)
|| (0x24EB <= point && point <= 0x254B)
|| (0x2550 <= point && point <= 0x2573)
|| (0x2580 <= point && point <= 0x258F)
|| (0x2592 <= point && point <= 0x2595)
|| (0x25A0 <= point && point <= 0x25A1)
|| (0x25A3 <= point && point <= 0x25A9)
|| (0x25B2 <= point && point <= 0x25B3)
|| (0x25B6 <= point && point <= 0x25B7)
|| (0x25BC <= point && point <= 0x25BD)
|| (0x25C0 <= point && point <= 0x25C1)
|| (0x25C6 <= point && point <= 0x25C8)
|| (0x25CB === point)
|| (0x25CE <= point && point <= 0x25D1)
|| (0x25E2 <= point && point <= 0x25E5)
|| (0x25EF === point)
|| (0x2605 <= point && point <= 0x2606)
|| (0x2609 === point)
|| (0x260E <= point && point <= 0x260F)
|| (0x2614 <= point && point <= 0x2615)
|| (0x261C === point)
|| (0x261E === point)
|| (0x2640 === point)
|| (0x2642 === point)
|| (0x2660 <= point && point <= 0x2661)
|| (0x2663 <= point && point <= 0x2665)
|| (0x2667 <= point && point <= 0x266A)
|| (0x266C <= point && point <= 0x266D)
|| (0x266F === point)
|| (0x269E <= point && point <= 0x269F)
|| (0x26BE <= point && point <= 0x26BF)
|| (0x26C4 <= point && point <= 0x26CD)
|| (0x26CF <= point && point <= 0x26E1)
|| (0x26E3 === point)
|| (0x26E8 <= point && point <= 0x26FF)
|| (0x273D === point)
|| (0x2757 === point)
|| (0x2776 <= point && point <= 0x277F)
|| (0x2B55 <= point && point <= 0x2B59)
|| (0x3248 <= point && point <= 0x324F)
|| (0xE000 <= point && point <= 0xF8FF)
|| (0xFE00 <= point && point <= 0xFE0F)
|| (0xFFFD === point)
|| (0x1F100 <= point && point <= 0x1F10A)
|| (0x1F110 <= point && point <= 0x1F12D)
|| (0x1F130 <= point && point <= 0x1F169)
|| (0x1F170 <= point && point <= 0x1F19A)
|| (0xE0100 <= point && point <= 0xE01EF)
|| (0xF0000 <= point && point <= 0xFFFFD)
|| (0x100000 <= point && point <= 0x10FFFD)) {
return +process.env.NCURSES_CJK_WIDTH || 1;
}
}
return 1;
};
exports.strWidth = function(str) {
var width = 0;
for (var i = 0; i < str.length; i++) {
width += exports.charWidth(str, i);
if (exports.isSurrogate(str, i)) i++;
}
return width;
};
exports.isSurrogate = function(str, i) {
var point = typeof str !== 'number'
? exports.codePointAt(str, i || 0)
: str;
return point > 0x00ffff;
};
exports.combiningTable = [
[0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489],
[0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2],
[0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603],
[0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670],
[0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED],
[0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A],
[0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902],
[0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D],
[0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981],
[0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD],
[0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C],
[0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D],
[0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC],
[0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD],
[0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C],
[0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D],
[0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0],
[0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48],
[0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC],
[0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD],
[0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D],
[0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6],
[0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E],
[0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC],
[0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35],
[0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E],
[0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97],
[0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030],
[0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039],
[0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F],
[0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753],
[0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD],
[0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD],
[0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922],
[0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B],
[0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34],
[0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42],
[0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF],
[0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063],
[0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F],
[0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B],
[0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F],
[0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB],
[0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F],
[0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169],
[0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD],
[0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F],
[0xE0100, 0xE01EF]
];
exports.combining = exports.combiningTable.reduce(function(out, row) {
for (var i = row[0]; i <= row[1]; i++) {
out[i] = true;
}
return out;
}, {});
exports.isCombining = function(str, i) {
var point = typeof str !== 'number'
? exports.codePointAt(str, i || 0)
: str;
return exports.combining[point] === true;
};
/**
* Code Point Helpers
*/
exports.codePointAt = function(str, position) {
if (str == null) {
throw TypeError();
}
var string = String(str);
if (string.codePointAt) {
return string.codePointAt(position);
}
var size = string.length;
// `ToInteger`
var index = position ? Number(position) : 0;
if (index !== index) { // better `isNaN`
index = 0;
}
// Account for out-of-bounds indices:
if (index < 0 || index >= size) {
return undefined;
}
// Get the first code unit
var first = string.charCodeAt(index);
var second;
if ( // check if its the start of a surrogate pair
first >= 0xD800 && first <= 0xDBFF && // high surrogate
size > index + 1 // there is a next code unit
) {
second = string.charCodeAt(index + 1);
if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
}
}
return first;
};
// exports.codePointAt = function(str, position) {
// position = +position || 0;
// var x = str.charCodeAt(position);
// var y = str.length > 1 ? str.charCodeAt(position + 1) : 0;
// var point = x;
// if ((0xD800 <= x && x <= 0xDBFF) && (0xDC00 <= y && y <= 0xDFFF)) {
// x &= 0x3FF;
// y &= 0x3FF;
// point = (x << 10) | y;
// point += 0x10000;
// }
// return point;
// };
exports.fromCodePoint = function() {
if (String.fromCodePoint) {
return String.fromCodePoint.apply(String, arguments);
}
var MAX_SIZE = 0x4000;
var codeUnits = [];
var highSurrogate;
var lowSurrogate;
var index = -1;
var length = arguments.length;
if (!length) {
return '';
}
var result = '';
while (++index < length) {
var codePoint = Number(arguments[index]);
if (
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
codePoint < 0 || // not a valid Unicode code point
codePoint > 0x10FFFF || // not a valid Unicode code point
floor(codePoint) !== codePoint // not an integer
) {
throw RangeError('Invalid code point: ' + codePoint);
}
if (codePoint <= 0xFFFF) { // BMP code point
codeUnits.push(codePoint);
} else { // Astral code point; split in surrogate halves
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
codePoint -= 0x10000;
highSurrogate = (codePoint >> 10) + 0xD800;
lowSurrogate = (codePoint % 0x400) + 0xDC00;
codeUnits.push(highSurrogate, lowSurrogate);
}
if (index + 1 === length || codeUnits.length > MAX_SIZE) {
result += stringFromCharCode.apply(null, codeUnits);
codeUnits.length = 0;
}
}
return result;
};
/**
* Regexes
*/
exports.chars = {};
// Double width characters that are _not_ surrogate pairs.
// NOTE: 0x20000 - 0x2fffd and 0x30000 - 0x3fffd are not necessary for this
// regex anyway. This regex is used to put a blank char after wide chars to
// be eaten, however, if this is a surrogate pair, parseContent already adds
// the extra one char because its length equals 2 instead of 1.
exports.chars.wide = new RegExp('(['
+ '\\u1100-\\u115f' // Hangul Jamo init. consonants
+ '\\u2329\\u232a'
+ '\\u2e80-\\u303e\\u3040-\\ua4cf' // CJK ... Yi
+ '\\uac00-\\ud7a3' // Hangul Syllables
+ '\\uf900-\\ufaff' // CJK Compatibility Ideographs
+ '\\ufe10-\\ufe19' // Vertical forms
+ '\\ufe30-\\ufe6f' // CJK Compatibility Forms
+ '\\uff00-\\uff60' // Fullwidth Forms
+ '\\uffe0-\\uffe6'
+ '])', 'g');
// All surrogate pair wide chars.
exports.chars.swide = new RegExp('('
// 0x20000 - 0x2fffd:
+ '[\\ud840-\\ud87f][\\udc00-\\udffd]'
+ '|'
// 0x30000 - 0x3fffd:
+ '[\\ud880-\\ud8bf][\\udc00-\\udffd]'
+ ')', 'g');
// All wide chars including surrogate pairs.
exports.chars.all = new RegExp('('
+ exports.chars.swide.source.slice(1, -1)
+ '|'
+ exports.chars.wide.source.slice(1, -1)
+ ')', 'g');
// Regex to detect a surrogate pair.
exports.chars.surrogate = /[\ud800-\udbff][\udc00-\udfff]/g;
// Regex to find combining characters.
exports.chars.combining = exports.combiningTable.reduce(function(out, row) {
var low, high, range;
if (row[0] > 0x00ffff) {
low = exports.fromCodePoint(row[0]);
low = [
hexify(low.charCodeAt(0)),
hexify(low.charCodeAt(1))
];
high = exports.fromCodePoint(row[1]);
high = [
hexify(high.charCodeAt(0)),
hexify(high.charCodeAt(1))
];
range = '[\\u' + low[0] + '-' + '\\u' + high[0] + ']'
+ '[\\u' + low[1] + '-' + '\\u' + high[1] + ']';
if (!~out.indexOf('|')) out += ']';
out += '|' + range;
} else {
low = hexify(row[0]);
high = hexify(row[1]);
low = '\\u' + low;
high = '\\u' + high;
out += low + '-' + high;
}
return out;
}, '[');
exports.chars.combining = new RegExp(exports.chars.combining, 'g');
function hexify(n) {
n = n.toString(16);
while (n.length < 4) n = '0' + n;
return n;
}
/*
exports.chars.combining = new RegExp(
'['
+ '\\u0300-\\u036f'
+ '\\u0483-\\u0486'
+ '\\u0488-\\u0489'
+ '\\u0591-\\u05bd'
+ '\\u05bf-\\u05bf'
+ '\\u05c1-\\u05c2'
+ '\\u05c4-\\u05c5'
+ '\\u05c7-\\u05c7'
+ '\\u0600-\\u0603'
+ '\\u0610-\\u0615'
+ '\\u064b-\\u065e'
+ '\\u0670-\\u0670'
+ '\\u06d6-\\u06e4'
+ '\\u06e7-\\u06e8'
+ '\\u06ea-\\u06ed'
+ '\\u070f-\\u070f'
+ '\\u0711-\\u0711'
+ '\\u0730-\\u074a'
+ '\\u07a6-\\u07b0'
+ '\\u07eb-\\u07f3'
+ '\\u0901-\\u0902'
+ '\\u093c-\\u093c'
+ '\\u0941-\\u0948'
+ '\\u094d-\\u094d'
+ '\\u0951-\\u0954'
+ '\\u0962-\\u0963'
+ '\\u0981-\\u0981'
+ '\\u09bc-\\u09bc'
+ '\\u09c1-\\u09c4'
+ '\\u09cd-\\u09cd'
+ '\\u09e2-\\u09e3'
+ '\\u0a01-\\u0a02'
+ '\\u0a3c-\\u0a3c'
+ '\\u0a41-\\u0a42'
+ '\\u0a47-\\u0a48'
+ '\\u0a4b-\\u0a4d'
+ '\\u0a70-\\u0a71'
+ '\\u0a81-\\u0a82'
+ '\\u0abc-\\u0abc'
+ '\\u0ac1-\\u0ac5'
+ '\\u0ac7-\\u0ac8'
+ '\\u0acd-\\u0acd'
+ '\\u0ae2-\\u0ae3'
+ '\\u0b01-\\u0b01'
+ '\\u0b3c-\\u0b3c'
+ '\\u0b3f-\\u0b3f'
+ '\\u0b41-\\u0b43'
+ '\\u0b4d-\\u0b4d'
+ '\\u0b56-\\u0b56'
+ '\\u0b82-\\u0b82'
+ '\\u0bc0-\\u0bc0'
+ '\\u0bcd-\\u0bcd'
+ '\\u0c3e-\\u0c40'
+ '\\u0c46-\\u0c48'
+ '\\u0c4a-\\u0c4d'
+ '\\u0c55-\\u0c56'
+ '\\u0cbc-\\u0cbc'
+ '\\u0cbf-\\u0cbf'
+ '\\u0cc6-\\u0cc6'
+ '\\u0ccc-\\u0ccd'
+ '\\u0ce2-\\u0ce3'
+ '\\u0d41-\\u0d43'
+ '\\u0d4d-\\u0d4d'
+ '\\u0dca-\\u0dca'
+ '\\u0dd2-\\u0dd4'
+ '\\u0dd6-\\u0dd6'
+ '\\u0e31-\\u0e31'
+ '\\u0e34-\\u0e3a'
+ '\\u0e47-\\u0e4e'
+ '\\u0eb1-\\u0eb1'
+ '\\u0eb4-\\u0eb9'
+ '\\u0ebb-\\u0ebc'
+ '\\u0ec8-\\u0ecd'
+ '\\u0f18-\\u0f19'
+ '\\u0f35-\\u0f35'
+ '\\u0f37-\\u0f37'
+ '\\u0f39-\\u0f39'
+ '\\u0f71-\\u0f7e'
+ '\\u0f80-\\u0f84'
+ '\\u0f86-\\u0f87'
+ '\\u0f90-\\u0f97'
+ '\\u0f99-\\u0fbc'
+ '\\u0fc6-\\u0fc6'
+ '\\u102d-\\u1030'
+ '\\u1032-\\u1032'
+ '\\u1036-\\u1037'
+ '\\u1039-\\u1039'
+ '\\u1058-\\u1059'
+ '\\u1160-\\u11ff'
+ '\\u135f-\\u135f'
+ '\\u1712-\\u1714'
+ '\\u1732-\\u1734'
+ '\\u1752-\\u1753'
+ '\\u1772-\\u1773'
+ '\\u17b4-\\u17b5'
+ '\\u17b7-\\u17bd'
+ '\\u17c6-\\u17c6'
+ '\\u17c9-\\u17d3'
+ '\\u17dd-\\u17dd'
+ '\\u180b-\\u180d'
+ '\\u18a9-\\u18a9'
+ '\\u1920-\\u1922'
+ '\\u1927-\\u1928'
+ '\\u1932-\\u1932'
+ '\\u1939-\\u193b'
+ '\\u1a17-\\u1a18'
+ '\\u1b00-\\u1b03'
+ '\\u1b34-\\u1b34'
+ '\\u1b36-\\u1b3a'
+ '\\u1b3c-\\u1b3c'
+ '\\u1b42-\\u1b42'
+ '\\u1b6b-\\u1b73'
+ '\\u1dc0-\\u1dca'
+ '\\u1dfe-\\u1dff'
+ '\\u200b-\\u200f'
+ '\\u202a-\\u202e'
+ '\\u2060-\\u2063'
+ '\\u206a-\\u206f'
+ '\\u20d0-\\u20ef'
+ '\\u302a-\\u302f'
+ '\\u3099-\\u309a'
+ '\\ua806-\\ua806'
+ '\\ua80b-\\ua80b'
+ '\\ua825-\\ua826'
+ '\\ufb1e-\\ufb1e'
+ '\\ufe00-\\ufe0f'
+ '\\ufe20-\\ufe23'
+ '\\ufeff-\\ufeff'
+ '\\ufff9-\\ufffb'
+ ']'
+ '|[\\ud802-\\ud802][\\ude01-\\ude03]'
+ '|[\\ud802-\\ud802][\\ude05-\\ude06]'
+ '|[\\ud802-\\ud802][\\ude0c-\\ude0f]'
+ '|[\\ud802-\\ud802][\\ude38-\\ude3a]'
+ '|[\\ud802-\\ud802][\\ude3f-\\ude3f]'
+ '|[\\ud834-\\ud834][\\udd67-\\udd69]'
+ '|[\\ud834-\\ud834][\\udd73-\\udd82]'
+ '|[\\ud834-\\ud834][\\udd85-\\udd8b]'
+ '|[\\ud834-\\ud834][\\uddaa-\\uddad]'
+ '|[\\ud834-\\ud834][\\ude42-\\ude44]'
+ '|[\\udb40-\\udb40][\\udc01-\\udc01]'
+ '|[\\udb40-\\udb40][\\udc20-\\udc7f]'
+ '|[\\udb40-\\udb40][\\udd00-\\uddef]'
, 'g');
*/
};
BundleModuleCode['term/helpers']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2016, Christopher Jeffrey and contributors
** $CREATED: sbosse on 28-3-15.
** $VERSION: 1.9.1
**
** $INFO:
*
* helpers.js - helpers for blessed
*
** $ENDOFINFO
*/
/**
* Modules
*/
var fs = Require('fs');
var Comp = Require('com/compat');
var unicode = Require('term/unicode');
/**
* Helpers
*/
var helpers = exports;
helpers.asort = function(obj) {
return obj.sort(function(a, b) {
a = a.name.toLowerCase();
b = b.name.toLowerCase();
if (a[0] === '.' && b[0] === '.') {
a = a[1];
b = b[1];
} else {
a = a[0];
b = b[0];
}
return a > b ? 1 : (a < b ? -1 : 0);
});
};
helpers.attrToBinary = function(style, element) {
return helpers.Element.prototype.sattr.call(element || {}, style);
};
// Compute absolute position, width, height of a box
// Requires parent object with absolute bbox valies.
// Commonly parent is screen object.
// Supported formats: number,'half','center','%'
helpers.bbox = function (parent,options) {
function eval(a,b) {
if (a.indexOf('%')) return int(Number(a.substring(0,a.length-1))*b/100);
}
var bbox={width:options.width,height:options.height,top:options.top,left:options.left,right:options.right};
if (bbox.width=='half') bbox.width=int(parent.width/2);
if (typeof bbox.width == 'string' && bbox.width.indexOf('%')!=-1) bbox.width=eval(bbox.width,parent.width);
if (bbox.height=='half') bbox.height=int(parent.height/2);
if (typeof bbox.height == 'string' && bbox.height.indexOf('%')!=-1) bbox.height=eval(bbox.height,parent.height);
if (bbox.left=='center') bbox.left=int((parent.width/2)-(bbox.width/2));
if (bbox.top=='center') bbox.top=int((parent.height/2)-(bbox.height/2));
return bbox;
}
helpers.cleanTags = function(text) {
return helpers.stripTags(text).trim();
};
helpers.dropUnicode = function(text) {
if (!text) return '';
return text
.replace(unicode.chars.all, '??')
.replace(unicode.chars.combining, '')
.replace(unicode.chars.surrogate, '?');
};
helpers.findFile = function(start, target) {
return (function read(dir) {
var files, file, stat, out;
if (dir === '/dev' || dir === '/sys'
|| dir === '/proc' || dir === '/net') {
return null;
}
try {
files = fs.readdirSync(dir);
} catch (e) {
files = [];
}
for (var i = 0; i < files.length; i++) {
file = files[i];
if (file === target) {
return (dir === '/' ? '' : dir) + '/' + file;
}
try {
stat = fs.lstatSync((dir === '/' ? '' : dir) + '/' + file);
} catch (e) {
stat = null;
}
if (stat && stat.isDirectory() && !stat.isSymbolicLink()) {
out = read((dir === '/' ? '' : dir) + '/' + file);
if (out) return out;
}
}
return null;
})(start);
};
// Escape text for tag-enabled elements.
helpers.escape = function(text) {
return text.replace(/[{}]/g, function(ch) {
return ch === '{' ? '{open}' : '{close}';
});
};
helpers.generateTags = function(style, text) {
var open = ''
, close = '';
Object.keys(style || {}).forEach(function(key) {
var val = style[key];
if (typeof val === 'string') {
val = val.replace(/^light(?!-)/, 'light-');
val = val.replace(/^bright(?!-)/, 'bright-');
open = '{' + val + '-' + key + '}' + open;
close += '{/' + val + '-' + key + '}';
} else {
if (val === true) {
open = '{' + key + '}' + open;
close += '{/' + key + '}';
}
}
});
if (text != null) {
return open + text + close;
}
return {
open: open,
close: close
};
};
helpers.hsort = function(obj) {
return obj.sort(function(a, b) {
return b.index - a.index;
});
};
helpers.merge = function(a, b) {
Object.keys(b).forEach(function(key) {
a[key] = b[key];
});
return a;
};
helpers.parseTags = function(text, screen) {
return helpers.Element.prototype._parseTags.call(
{ parseTags: true, screen: screen || helpers.Screen.global }, text);
};
helpers.stripTags = function(text) {
if (!text) return '';
return text
.replace(/\{(\/?)([\w\-,;!#]*)\}/g, '')
.replace(/\x1b\[[\d;]*m/g, '');
};
/* Depricated
helpers.__defineGetter__('Screen', function() {
if (!helpers._screen) {
helpers._screen = Require('term/widgets/screen');
}
return helpers._screen;
});
helpers.__defineGetter__('Element', function() {
if (!helpers._element) {
helpers._element = Require('term/widgets/element');
}
return helpers._element;
});
*/
Object.defineProperty(helpers,'Screen',{
get: function () {
if (!helpers._screen) {
helpers._screen = Require('term/widgets/screen');
}
return helpers._screen;
}
});
Object.defineProperty(helpers,'Element',{
get: function () {
if (!helpers._element) {
helpers._element = Require('term/widgets/element');
}
return helpers._element;
}
});
};
BundleModuleCode['term/widgets/log']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2013-2016, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.3.2
**
** $INFO:
**
** Logging Widget with Scrollbars
**
** options: {
** scrollOnInput:boolean -- auto scroll
** arrows?: {up:'[-]',down:'[+]',width:3,height:1,fg:'red',bg:'default'}}
**
** Usage:
if (options.top == undefined) options.top=2;
if (options.left == undefined) options.left=1;
var obj = blessed.log({
top: options.top,
left: options.left,
width: options.width||(self.screen.width-options.left*2),
height: options.height||(self.screen.height-options.top-4),
label: options.label||'Log',
mouse:true,
keys:true,
scrollback:100,
border: {
type: 'line'
},
scrollbar: {
ch: ' ',
track: {
bg: 'yellow'
},
style: {
fg: 'cyan',
inverse: true
}
},
alwaysScroll:true,
scrollOnInput:true,
style: {
fg: 'white',
bg: 'black',
border: {
fg: 'green'
},
focus: {
border: {
fg: 'red'
}
}
}
});
screen.append(obj);
** $ENDOFINFO
*/
var options = {
version:'1.3.2'
}
/**
* Modules
*/
var Comp = Require('com/compat');
var util = require('util');
var Helpers = Require('term/helpers');
var Button = Require('term/widgets/button');
var Arrows = Require('term/widgets/arrows');
var nextTick = global.setImmediate || process.nextTick.bind(process);
var Node = Require('term/widgets/node');
var ScrollableText = Require('term/widgets/scrollabletext');
/**
* Log
*/
function Log(options) {
var self = this, bbox;
if (!instanceOf(this,Node)) {
return new Log(options);
}
options = options || {};
ScrollableText.call(this, options);
this.scrollback = options.scrollback != null
? options.scrollback
: Infinity;
this.scrollOnInput = options.scrollOnInput;
this._updating=false;
if (options.arrows)
Arrows(
self,
options,
function () { self.scroll(-2)},
function () { self.scroll(2)}
);
this.on('set content', function() {
if (!self._updating && !self._userScrolled && self.scrollOnInput) {
self._updating=true;
setTimeout(function() {
self.setScrollPerc(100);
self._userScrolled = false;
self._updating=false;
self.screen.render();
},20);
}
});
}
//Log.prototype.__proto__ = ScrollableText.prototype;
inheritPrototype(Log,ScrollableText);
Log.prototype.type = 'log';
Log.prototype.log =
Log.prototype.add = function() {
var args = Array.prototype.slice.call(arguments);
if (typeof args[0] === 'object') {
args[0] = util.inspect(args[0], true, 20, true);
}
var text = util.format.apply(util, args);
this.emit('log', text);
var ret = this.pushLine(text);
//if (this._clines.fake.length > this.scrollback) {
// this.shiftLine(0, (this.scrollback / 3) | 0);
// }
return ret;
};
Log.prototype.clear = function() {
if (this._userScrolled) this.setScrollPerc(100);
this.setContent('');
this.resetScroll();
}
Log.prototype._scroll = Log.prototype.scroll;
Log.prototype.scroll = function(offset, always) {
if (offset>this.getScrollHeight() || (this.getScrollHeight()+offset)<0) return;
if (offset === 0) return this._scroll(offset, always);
this._userScrolled = true;
var ret = this._scroll(offset, always);
if (this.getScrollPerc() === 100) {
this._userScrolled = false;
}
return ret;
};
/**
* Expose
*/
module.exports = Log;
Log.prototype.logold = function(str) {
var i;
this.logLines.push(str)
if (this.logLines.length==1) {
this.setContent(str);
}
else if (this.logLines.length>this.options.bufferLength) {
this.logLines.shift();
this.setContent(this.logLines[0]);
for(i=1;i<this.logLines.length;i++) {
this.insertLine(i,this.logLines[i]);
}
this.scrollBottom();
} else {
this.scrollBottom();
this.insertBottom(str);
this.scrollBottom();
}
// this.setItems(this.logLines)
// this.scrollTo(this.logLines.length)
}
};
BundleModuleCode['term/widgets/button']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.1.8
**
** $INFO:
**
** button.js - button element for blessed
**
** Options: {mouse:boolean,content,border,style,object}
**
** Events In: click keypress
** Events Out: press
**
** Usage:
var width;
if (Comp.obj.isString(options.width)) {
// relative value in %!
width=Comp.pervasives.int_of_string(options.width);
width=int(self.screen.width*width/100);
}
var obj = blessed.button({
width: options.width||(options.content.length+4),
left: (options.center?int(self.screen.width/2-width/2):options.left),
right : options.right,
top: options.top||0,
height: 3,
align: 'center',
content: options.content||'?',
mouse:true,
focus:false,
border: {
type: 'line'
},
style: {
fg: 'white',
bg: options.color||'blue',
bold:true,
border: {
fg: 'black'
},
hover: {
border: {
fg: 'red'
}
}
}
});
screen.append(obj);
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Input = Require('term/widgets/input');
/**
* Button
*/
function Button(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Button(options);
}
options = options || {};
if (options.autoFocus == null) {
options.autoFocus = false;
}
if (!options.style) options.style = {
fg: 'white',
bg: 'blue',
bold:true,
border: {
fg: 'black'
},
hover: {
border: {
fg: 'red'
}
},
focus : {
border: {
fg: 'red'
}
}
}
if (options.object) this.object=options.object;
Input.call(this, options);
this.on('keypress', function(ch, key) {
if (key.name == 'enter' || key.name == 'return') {
return self.press();
}
});
if (this.options.mouse) {
this.on('click', function() {
return self.press();
});
}
}
//Button.prototype.__proto__ = Input.prototype;
inheritPrototype(Button,Input);
Button.prototype.type = 'button';
Button.prototype.press = function() {
this.focus();
this.value = true;
var result = this.emit('press');
delete this.value;
return result;
};
/**
* Expose
*/
module.exports = Button;
};
BundleModuleCode['term/widgets/input']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.2
**
** $INFO:
**
** input.js - abstract input element for blessed
**
** Added:
** - Focus handling
**
** Usage:
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* Input
*/
function Input(options) {
if (!instanceOf(this,Node)) {
return new Input(options);
}
options = options || {};
Box.call(this, options);
}
//Input.prototype.__proto__ = Box.prototype;
inheritPrototype(Input,Box);
Input.prototype.type = 'input';
Input.prototype.focus = function() {
// Force focus for input field
this.screen.rewindFocus();
return this.screen.focused = this;
}
/**
* Expose
*/
module.exports = Input;
};
BundleModuleCode['term/widgets/box']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.1
**
** $INFO:
**
** box.js - box element for blessed
**
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Element = Require('term/widgets/element');
/**
* Box
*/
function Box(options) {
if (!instanceOf(this,Node)) {
return new Box(options);
}
options = options || {};
Element.call(this, options);
}
//Box.prototype.__proto__ = Element.prototype;
inheritPrototype(Box,Element);
Box.prototype.type = 'box';
/**
* Expose
*/
module.exports = Box;
};
BundleModuleCode['term/widgets/element']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2016, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2016-2021)
** $REVESIO: 1.4.3
**
** $INFO:
**
** Base element for blessed
**
** Event Out: change, resize, set content, show, hide
**
** Added:
** - 'setStyle({fg:..})' method
** - 'getLabel()' method
** - 'isfocus' method
** - relative right align attribute right:'60%'
**
** typeof this._clines = string [],rtof:number [], ftor:number [][], fake: value string
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var assert = Require('assert');
var colors = Require('term/colors')
, unicode = Require('term/unicode');
var nextTick = global.setImmediate || process.nextTick.bind(process);
var helpers = Require('term/helpers');
var Node = Require('term/widgets/node');
/**
* Element
*/
function Element(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Element(options);
}
options = options || {};
// Workaround to get a `scrollable` option.
if (options.scrollable && !this._ignore && this.type !== 'scrollable-box') {
var ScrollableBox = Require('term/widgets/scrollablebox');
Object.getOwnPropertyNames(ScrollableBox.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ScrollableBox.prototype, key));
}, this);
this._ignore = true;
ScrollableBox.call(this, options);
delete this._ignore;
// Workaround two: catch scrollbar mouse events
if (options.mouse) {
this.onScreenEvent('mouse',function (data) {
var x = data.x - self.aleft;
var y = data.y - self.atop;
if (!self.hidden && data.action=='mousedown' &&
x === self.width - self.iright - 1) {
self.focus();
var perc = (y - self.itop) / (self.height - self.iheight);
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
}
});
}
return this;
}
Node.call(this, options);
this.name = options.name;
options.position = options.position || {
left: options.left,
right: options.right,
top: options.top,
bottom: options.bottom,
width: options.width,
height: options.height
};
if (options.position.width === 'shrink'
|| options.position.height === 'shrink') {
if (options.position.width === 'shrink') {
delete options.position.width;
}
if (options.position.height === 'shrink') {
delete options.position.height;
}
options.shrink = true;
}
this.position = options.position;
this.noOverflow = options.noOverflow;
this.dockBorders = options.dockBorders;
this.shadow = options.shadow;
this.style = options.style;
if (!this.style) {
this.style = {};
this.style.fg = options.fg;
this.style.bg = options.bg;
this.style.bold = options.bold;
this.style.underline = options.underline;
this.style.blink = options.blink;
this.style.inverse = options.inverse;
this.style.invisible = options.invisible;
this.style.transparent = options.transparent;
}
this.hidden = options.hidden || false;
this.fixed = options.fixed || false;
this.align = options.align || 'left';
this.valign = options.valign || 'top';
this.wrap = options.wrap !== false;
this.shrink = options.shrink;
this.ch = options.ch || ' ';
if (typeof options.padding === 'number' || !options.padding) {
options.padding = {
left: options.padding,
top: options.padding,
right: options.padding,
bottom: options.padding
};
}
this.padding = {
left: options.padding.left || 0,
top: options.padding.top || 0,
right: options.padding.right || 0,
bottom: options.padding.bottom || 0
};
this.border = options.border;
if (this.border) {
if (typeof this.border === 'string') {
this.border = { type: this.border };
}
this.border.type = this.border.type || 'bg';
if (this.border.type === 'ascii') this.border.type = 'line';
this.border.ch = this.border.ch || ' ';
this.style.border = this.style.border || this.border.style;
if (!this.style.border) {
this.style.border = {};
this.style.border.fg = this.border.fg;
this.style.border.bg = this.border.bg;
}
//this.border.style = this.style.border;
if (this.border.left == null) this.border.left = true;
if (this.border.top == null) this.border.top = true;
if (this.border.right == null) this.border.right = true;
if (this.border.bottom == null) this.border.bottom = true;
}
// if (options.mouse || options.clickable) {
if (options.clickable) {
this.screen._listenMouse(this);
}
if (options.input || options.keyable) {
this.screen._listenKeys(this);
}
this.parseTags = options.parseTags || options.tags;
this.setContent(options.content || '', true);
if (options.label) {
this.setLabel(options.label);
}
if (options.hoverText) {
this.setHover(options.hoverText);
}
// TODO: Possibly move this to Node for onScreenEvent('mouse', ...).
this.on('newListener', function fn(type) {
// type = type.split(' ').slice(1).join(' ');
if (type === 'mouse'
|| type === 'click'
|| type === 'mouseover'
|| type === 'mouseout'
|| type === 'mousedown'
|| type === 'mouseup'
|| type === 'mousewheel'
|| type === 'wheeldown'
|| type === 'wheelup'
|| type === 'mousemove') {
self.screen._listenMouse(self);
} else if (type === 'keypress' || type.indexOf('key ') === 0) {
self.screen._listenKeys(self);
}
});
this.on('resize', function() {
self.parseContent();
});
this.on('attach', function() {
self.parseContent();
});
this.on('detach', function() {
delete self.lpos;
});
if (options.hoverBg != null) {
options.hoverEffects = options.hoverEffects || {};
options.hoverEffects.bg = options.hoverBg;
}
if (this.style.hover) {
options.hoverEffects = this.style.hover;
}
if (this.style.focus) {
options.focusEffects = this.style.focus;
}
if (options.effects) {
if (options.effects.hover) options.hoverEffects = options.effects.hover;
if (options.effects.focus) options.focusEffects = options.effects.focus;
}
[['hoverEffects', 'mouseover', 'mouseout', '_htemp'],
['focusEffects', 'focus', 'blur', '_ftemp']].forEach(function(props) {
var pname = props[0], over = props[1], out = props[2], temp = props[3];
self.screen.setEffects(self, self, over, out, self.options[pname], temp);
});
if (this.options.draggable) {
this.draggable = true;
}
if (options.focused) {
this.focus();
}
}
//Element.prototype.__proto__ = Node.prototype;
inheritPrototype(Element,Node);
Element.prototype.type = 'element';
/* Deprictaed
Element.prototype.__defineGetter__('focused', function() {
return this.screen.focused === this;
});
*/
Object.defineProperty(Element.prototype,'focused',{
get: function () {return this.screen.focused === this;}
});
Element.prototype.sattr = function(style, fg, bg) {
var bold = style.bold
, underline = style.underline
, blink = style.blink
, inverse = style.inverse
, invisible = style.invisible;
// if (arguments.length === 1) {
if (fg == null && bg == null) {
fg = style.fg;
bg = style.bg;
}
// This used to be a loop, but I decided
// to unroll it for performance's sake.
if (typeof bold === 'function') bold = bold(this);
if (typeof underline === 'function') underline = underline(this);
if (typeof blink === 'function') blink = blink(this);
if (typeof inverse === 'function') inverse = inverse(this);
if (typeof invisible === 'function') invisible = invisible(this);
if (typeof fg === 'function') fg = fg(this);
if (typeof bg === 'function') bg = bg(this);
// return (this.uid << 24)
// | ((this.dockBorders ? 32 : 0) << 18)
return ((invisible ? 16 : 0) << 18)
| ((inverse ? 8 : 0) << 18)
| ((blink ? 4 : 0) << 18)
| ((underline ? 2 : 0) << 18)
| ((bold ? 1 : 0) << 18)
| (colors.convert(fg) << 9)
| colors.convert(bg);
};
Element.prototype.onScreenEvent = function(type, handler) {
var listeners = this._slisteners = this._slisteners || [];
listeners.push({ type: type, handler: handler });
this.screen.on(type, handler);
};
Element.prototype.onceScreenEvent = function(type, handler) {
var listeners = this._slisteners = this._slisteners || [];
var entry = { type: type, handler: handler };
listeners.push(entry);
this.screen.once(type, function() {
var i = listeners.indexOf(entry);
if (~i) listeners.splice(i, 1);
return handler.apply(this, arguments);
});
};
Element.prototype.removeScreenEvent = function(type, handler) {
var listeners = this._slisteners = this._slisteners || [];
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
if (listener.type === type && listener.handler === handler) {
listeners.splice(i, 1);
if (this._slisteners.length === 0) {
delete this._slisteners;
}
break;
}
}
this.screen.removeListener(type, handler);
};
Element.prototype.free = function() {
var listeners = this._slisteners = this._slisteners || [];
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
this.screen.removeListener(listener.type, listener.handler);
}
delete this._slisteners;
};
Element.prototype.hide = function() {
if (this.hidden) return;
this.clearPos();
this.hidden = true;
this.emit('hide');
if (this.screen.focused === this) {
this.screen.rewindFocus();
}
};
Element.prototype.show = function() {
if (!this.hidden) return;
this.hidden = false;
this.emit('show');
};
Element.prototype.toggle = function() {
return this.hidden ? this.show() : this.hide();
};
Element.prototype.focus = function() {
return this.screen.focused = this;
};
Element.prototype.isfocus = function() {
return this.screen.focused == this;
};
Element.prototype.setStyle = function(styles) {
for(var p in styles) {
if (this.style[p]!=undefined) this.style[p]=styles[p];
}
};
Element.prototype.setContent = function(content, noClear, noTags) {
var changed=this.content!=content;
if (!noClear) this.clearPos();
this.content = content || '';
this.parseContent(noTags);
this.emit('set content');
if (changed) this.emit('change',content);
};
Element.prototype.getContent = function() {
if (!this._clines) return '';
return this._clines.fake.join('\n');
};
Element.prototype.setText = function(content, noClear) {
content = content || '';
content = content.replace(/\x1b\[[\d;]*m/g, '');
return this.setContent(content, noClear, true);
};
Element.prototype.getText = function() {
return this.getContent().replace(/\x1b\[[\d;]*m/g, '');
};
Element.prototype.parseContent = function(noTags) {
var self=this;
if (this.detached) return false;
var width = this.width - this.iwidth;
if (this._clines == null
|| this._clines.width !== width
|| this._clines.content !== this.content) {
var content = this.content;
content = content
.replace(/[\x00-\x08\x0b-\x0c\x0e-\x1a\x1c-\x1f\x7f]/g, '')
.replace(/\x1b(?!\[[\d;]*m)/g, '')
.replace(/\r\n|\r/g, '\n')
.replace(/\t/g, this.screen.tabc);
if (this.screen.fullUnicode) {
// double-width chars will eat the next char after render. create a
// blank character after it so it doesn't eat the real next char.
content = content.replace(unicode.chars.all, '$1\x03');
// iTerm2 cannot render combining characters properly.
if (this.screen.program.isiTerm2) {
content = content.replace(unicode.chars.combining, '');
}
} else {
// no double-width: replace them with question-marks.
content = content.replace(unicode.chars.all, '??');
// delete combining characters since they're 0-width anyway.
// NOTE: We could drop this, the non-surrogates would get changed to ? by
// the unicode filter, and surrogates changed to ? by the surrogate
// regex. however, the user might expect them to be 0-width.
// NOTE: Might be better for performance to drop!
content = content.replace(unicode.chars.combining, '');
// no surrogate pairs: replace them with question-marks.
content = content.replace(unicode.chars.surrogate, '?');
// XXX Deduplicate code here:
// content = helpers.dropUnicode(content);
}
if (!noTags) {
content = this._parseTags(content);
}
this._clines = this._wrapContent(content, width);
this._clines.width = width;
this._clines.content = this.content;
this._clines.attr = this._parseAttr(this._clines);
this._clines.ci = [];
this._clines.reduce(function(total, line) {
self._clines.ci.push(total);
return total + line.length + 1;
}, 0);
this._pcontent = this._clines.join('\n');
this.emit('parsed content');
return true;
}
// Need to calculate this every time because the default fg/bg may change.
this._clines.attr = this._parseAttr(this._clines) || this._clines.attr;
return false;
};
// Convert `{red-fg}foo{/red-fg}` to `\x1b[31mfoo\x1b[39m`.
Element.prototype._parseTags = function(text) {
if (!this.parseTags) return text;
if (!/\{\/?[\w\-,;!#]*\}/.test(text)) return text;
var program = this.screen.program
, out = ''
, state
, bg = []
, fg = []
, flag = []
, cap
, slash
, param
, attr
, esc;
for (;;) {
if (!esc && (cap = /^\{escape\}/.exec(text))) {
text = text.substring(cap[0].length);
esc = true;
continue;
}
if (esc && (cap = /^([\s\S]+?)\{\/escape\}/.exec(text))) {
text = text.substring(cap[0].length);
out += cap[1];
esc = false;
continue;
}
if (esc) {
// throw new Error('Unterminated escape tag.');
out += text;
break;
}
if (cap = /^\{(\/?)([\w\-,;!#]*)\}/.exec(text)) {
text = text.substring(cap[0].length);
slash = cap[1] === '/';
param = cap[2].replace(/-/g, ' ');
if (param === 'open') {
out += '{';
continue;
} else if (param === 'close') {
out += '}';
continue;
}
if (param.slice(-3) === ' bg') state = bg;
else if (param.slice(-3) === ' fg') state = fg;
else state = flag;
if (slash) {
if (!param) {
out += program._attr('normal');
bg.length = 0;
fg.length = 0;
flag.length = 0;
} else {
attr = program._attr(param, false);
if (attr == null) {
out += cap[0];
} else {
// if (param !== state[state.length - 1]) {
// throw new Error('Misnested tags.');
// }
state.pop();
if (state.length) {
out += program._attr(state[state.length - 1]);
} else {
out += attr;
}
}
}
} else {
if (!param) {
out += cap[0];
} else {
attr = program._attr(param);
if (attr == null) {
out += cap[0];
} else {
state.push(param);
out += attr;
}
}
}
continue;
}
if (cap = /^[\s\S]+?(?=\{\/?[\w\-,;!#]*\})/.exec(text)) {
text = text.substring(cap[0].length);
out += cap[0];
continue;
}
out += text;
break;
}
return out;
};
Element.prototype._parseAttr = function(lines) {
var dattr = this.sattr(this.style)
, attr = dattr
, attrs = []
, line
, i
, j
, c;
if (lines[0].attr === attr) {
return;
}
for (j = 0; j < lines.length; j++) {
line = lines[j];
attrs[j] = attr;
for (i = 0; i < line.length; i++) {
if (line[i] === '\x1b') {
if (c = /^\x1b\[[\d;]*m/.exec(line.substring(i))) {
attr = this.screen.attrCode(c[0], attr, dattr);
i += c[0].length - 1;
}
}
}
}
return attrs;
};
Element.prototype._align = function(line, width, align) {
if (!align) return line;
//if (!align && !~line.indexOf('{|}')) return line;
var cline = line.replace(/\x1b\[[\d;]*m/g, '')
, len = cline.length
, s = width - len;
if (this.shrink) {
s = 0;
}
if (len === 0) return line;
if (s < 0) return line;
if (align === 'center') {
s = Array(((s / 2) | 0) + 1).join(' ');
return s + line + s;
} else if (align === 'right') {
s = Array(s + 1).join(' ');
return s + line;
} else if (this.parseTags && ~line.indexOf('{|}')) {
var parts = line.split('{|}');
var cparts = cline.split('{|}');
s = Math.max(width - cparts[0].length - cparts[1].length, 0);
s = Array(s + 1).join(' ');
return parts[0] + s + parts[1];
}
return line;
};
Element.prototype._wrapContent = function(content, width) {
var tags = this.parseTags
, state = this.align
, wrap = this.wrap
, margin = 0
, rtof = [] // out-line mapping! (number [])
, ftor = [] // line-out mapping (number [][])
, out = [] // formatted output array with descriptor attributes
, no = 0
, line
, align
, cap
, total
, i
, part
, j
, lines
, rest;
lines = content.split('\n');
if (!content) {
out.push(content);
out.rtof = [0];
out.ftor = [[0]];
out.fake = lines;
out.real = out;
out.mwidth = 0;
return out;
}
if (this.scrollbar) margin++;
if (this.type === 'textarea') margin++;
if (width > margin) width -= margin;
main:
for (; no < lines.length; no++) {
line = lines[no];
align = state;
ftor.push([]);
// Handle alignment tags.
if (tags) {
if (cap = /^\{(left|center|right)\}/.exec(line)) {
line = line.substring(cap[0].length);
align = state = cap[1] !== 'left'
? cap[1]
: null;
}
if (cap = /\{\/(left|center|right)\}$/.exec(line)) {
line = line.slice(0, -cap[0].length);
//state = null;
state = this.align;
}
}
// If the string is apparently too long, wrap it.
while (line.length > width) {
// Measure the real width of the string.
if (this.break=='all')
i=width;
else for (i = 0, total = 0; i < line.length; i++) {
while (line[i] === '\x1b') {
while (line[i] && line[i++] !== 'm');
}
if (!line[i]) break;
if (++total === width) {
// If we're not wrapping the text, we have to finish up the rest of
// the control sequences before cutting off the line.
i++;
if (!wrap) {
rest = line.substring(i).match(/\x1b\[[^m]*m/g);
rest = rest ? rest.join('') : '';
out.push(this._align(line.substring(0, i) + rest, width, align));
ftor[no].push(out.length - 1);
rtof.push(no);
continue main;
}
if (!this.screen.fullUnicode) {
// Try to find a space to break on.
if (i !== line.length) {
j = i;
while (j > i - 10 && j > 0 && line[--j] !== ' ');
if (line[j] === ' ') i = j + 1;
}
} else {
// Try to find a character to break on.
if (i !== line.length) {
// <XXX>
// Compensate for surrogate length
// counts on wrapping (experimental):
// NOTE: Could optimize this by putting
// it in the parent for loop.
if (unicode.isSurrogate(line, i)) i--;
for (var s = 0, n = 0; n < i; n++) {
if (unicode.isSurrogate(line, n)) s++, n++;
}
i += s;
// </XXX>
j = i;
// Break _past_ space.
// Break _past_ double-width chars.
// Break _past_ surrogate pairs.
// Break _past_ combining chars.
while (j > i - 10 && j > 0) {
j--;
if (line[j] === ' '
|| line[j] === '\x03'
|| (unicode.isSurrogate(line, j - 1) && line[j + 1] !== '\x03')
|| unicode.isCombining(line, j)) {
break;
}
}
if (line[j] === ' '
|| line[j] === '\x03'
|| (unicode.isSurrogate(line, j - 1) && line[j + 1] !== '\x03')
|| unicode.isCombining(line, j)) {
i = j + 1;
}
}
}
break;
}
}
part = line.substring(0, i);
line = line.substring(i);
out.push(this._align(part, width, align));
ftor[no].push(out.length - 1);
rtof.push(no);
// Make sure we didn't wrap the line to the very end, otherwise
// we get a pointless empty line after a newline.
if (line === '') continue main;
// If only an escape code got cut off, at it to `part`.
if (/^(?:\x1b[\[\d;]*m)+$/.test(line)) {
out[out.length - 1] += line;
continue main;
}
}
out.push(this._align(line, width, align));
ftor[no].push(out.length - 1);
rtof.push(no);
}
out.rtof = rtof;
out.ftor = ftor;
out.fake = lines;
out.real = out;
out.mwidth = out.reduce(function(current, line) {
line = line.replace(/\x1b\[[\d;]*m/g, '');
return line.length > current
? line.length
: current;
}, 0);
return out;
};
/* Depricated
Element.prototype.__defineGetter__('visible', function() {
var el = this;
do {
if (el.detached) return false;
if (el.hidden) return false;
// if (!el.lpos) return false;
// if (el.position.width === 0 || el.position.height === 0) return false;
} while (el = el.parent);
return true;
});
Element.prototype.__defineGetter__('_detached', function() {
var el = this;
do {
if (el.type === 'screen') return false;
if (!el.parent) return true;
} while (el = el.parent);
return false;
});
*/
Object.defineProperty(Element.prototype,'visible',{
get: function () {
var el = this;
do {
if (el.detached) return false;
if (el.hidden) return false;
// if (!el.lpos) return false;
// if (el.position.width === 0 || el.position.height === 0) return false;
} while (el = el.parent);
return true;
}
});
Object.defineProperty(Element.prototype,'_detached',{
get: function () {
var el = this;
do {
if (el.type === 'screen') return false;
if (!el.parent) return true;
} while (el = el.parent);
return false;
}
});
Element.prototype.enableMouse = function() {
this.screen._listenMouse(this);
};
Element.prototype.enableKeys = function() {
this.screen._listenKeys(this);
};
Element.prototype.enableInput = function() {
this.screen._listenMouse(this);
this.screen._listenKeys(this);
};
/* Depricated:
Element.prototype.__defineGetter__('draggable', function() {
return this._draggable === true;
});
Element.prototype.__defineSetter__('draggable', function(draggable) {
return draggable ? this.enableDrag(draggable) : this.disableDrag();
});
*/
Object.defineProperty(Element.prototype,'draggable',{
get: function () {
return this._draggable === true;
},
set: function (draggable) {
return draggable ? this.enableDrag(draggable) : this.disableDrag();
}
});
Element.prototype.enableDrag = function(verify) {
var self = this;
if (this._draggable) return true;
if (typeof verify !== 'function') {
verify = function() { return true; };
}
this.enableMouse();
this.on('mousedown', this._dragMD = function(data) {
if (self.screen._dragging) return;
if (!verify(data)) return;
self.screen._dragging = self;
self._drag = {
x: data.x - self.aleft,
y: data.y - self.atop
};
self.setFront();
});
this.onScreenEvent('mouse', this._dragM = function(data) {
if (self.screen._dragging !== self) return;
if (data.action !== 'mousedown' && data.action !== 'mousemove') {
delete self.screen._dragging;
delete self._drag;
return;
}
// This can happen in edge cases where the user is
// already dragging and element when it is detached.
if (!self.parent) return;
var ox = self._drag.x
, oy = self._drag.y
, px = self.parent.aleft
, py = self.parent.atop
, x = data.x - px - ox
, y = data.y - py - oy;
if (self.position.right != null) {
if (self.position.left != null) {
self.width = '100%-' + (self.parent.width - self.width);
}
self.position.right = null;
}
if (self.position.bottom != null) {
if (self.position.top != null) {
self.height = '100%-' + (self.parent.height - self.height);
}
self.position.bottom = null;
}
self.rleft = x;
self.rtop = y;
self.screen.render();
});
return this._draggable = true;
};
Element.prototype.disableDrag = function() {
if (!this._draggable) return false;
delete this.screen._dragging;
delete this._drag;
this.removeListener('mousedown', this._dragMD);
this.removeScreenEvent('mouse', this._dragM);
return this._draggable = false;
};
Element.prototype.key = function() {
return this.screen.program.key.apply(this, arguments);
};
Element.prototype.onceKey = function() {
return this.screen.program.onceKey.apply(this, arguments);
};
Element.prototype.unkey =
Element.prototype.removeKey = function() {
return this.screen.program.unkey.apply(this, arguments);
};
Element.prototype.setIndex = function(index) {
if (!this.parent) return;
if (index < 0) {
index = this.parent.children.length + index;
}
index = Math.max(index, 0);
index = Math.min(index, this.parent.children.length - 1);
var i = this.parent.children.indexOf(this);
if (!~i) return;
var item = this.parent.children.splice(i, 1)[0];
this.parent.children.splice(index, 0, item);
};
Element.prototype.setFront = function() {
return this.setIndex(-1);
};
Element.prototype.setBack = function() {
return this.setIndex(0);
};
Element.prototype.clearPos = function(get, override) {
if (this.detached) return;
var lpos = this._getCoords(get);
if (!lpos) return;
this.screen.clearRegion(
lpos.xi, lpos.xl,
lpos.yi, lpos.yl,
override);
};
Element.prototype.getLabel = function() {
if (this._label) return this._label.getContent();
}
Element.prototype.setLabel = function(options) {
var self = this;
var Box = Require('term/widgets/box');
if (typeof options === 'string') {
options = { text: options };
}
if (this._label) {
this._label.setContent(options.text);
if (options.side !== 'right') {
this._label.rleft = 2 + (this.border ? -1 : 0);
this._label.position.right = undefined;
if (!this.screen.autoPadding) {
this._label.rleft = 2;
}
} else {
this._label.rright = 2 + (this.border ? -1 : 0);
this._label.position.left = undefined;
if (!this.screen.autoPadding) {
this._label.rright = 2;
}
}
return;
}
this._label = new Box({
screen: this.screen,
parent: this,
content: options.text,
top: -this.itop,
tags: this.parseTags,
shrink: true,
style: this.style.label
});
if (options.side !== 'right') {
this._label.rleft = 2 - this.ileft;
} else {
this._label.rright = 2 - this.iright;
}
if (this.border && this.border.type=='none') this._label.rleft=0;
this._label._isLabel = true;
if (!this.screen.autoPadding) {
if (options.side !== 'right') {
this._label.rleft = 2;
} else {
this._label.rright = 2;
}
this._label.rtop = 0;
}
var reposition = function() {
self._label.rtop = (self.childBase || 0) - self.itop;
if (!self.screen.autoPadding) {
self._label.rtop = (self.childBase || 0);
}
self.screen.render();
};
this.on('scroll', this._labelScroll = function() {
reposition();
});
this.on('resize', this._labelResize = function() {
nextTick(function() {
reposition();
});
});
};
Element.prototype.removeLabel = function() {
if (!this._label) return;
this.removeListener('scroll', this._labelScroll);
this.removeListener('resize', this._labelResize);
this._label.detach();
delete this._labelScroll;
delete this._labelResize;
delete this._label;
};
Element.prototype.setHover = function(options) {
if (typeof options === 'string') {
options = { text: options };
}
this._hoverOptions = options;
this.enableMouse();
this.screen._initHover();
};
Element.prototype.removeHover = function() {
delete this._hoverOptions;
if (!this.screen._hoverText || this.screen._hoverText.detached) return;
this.screen._hoverText.detach();
this.screen.render();
};
/**
* Positioning
*/
// The below methods are a bit confusing: basically
// whenever Box.render is called `lpos` gets set on
// the element, an object containing the rendered
// coordinates. Since these don't update if the
// element is moved somehow, they're unreliable in
// that situation. However, if we can guarantee that
// lpos is good and up to date, it can be more
// accurate than the calculated positions below.
// In this case, if the element is being rendered,
// it's guaranteed that the parent will have been
// rendered first, in which case we can use the
// parant's lpos instead of recalculating it's
// position (since that might be wrong because
// it doesn't handle content shrinkage).
Element.prototype._getPos = function() {
var pos = this.lpos;
assert.ok(pos);
if (pos.aleft != null) return pos;
pos.aleft = pos.xi;
pos.atop = pos.yi;
pos.aright = this.screen.cols - pos.xl;
pos.abottom = this.screen.rows - pos.yl;
pos.width = pos.xl - pos.xi;
pos.height = pos.yl - pos.yi;
return pos;
};
/**
* Position Getters
*/
Element.prototype._getWidth = function(get) {
var parent = get ? this.parent._getPos() : this.parent
, width = this.position.width
, left
, expr;
if (typeof width === 'string') {
if (width === 'half') width = '50%';
expr = width.split(/(?=\+|-)/);
width = expr[0];
width = +width.slice(0, -1) / 100;
width = parent.width * width | 0;
width += +(expr[1] || 0);
return width;
}
// This is for if the element is being streched or shrunken.
// Although the width for shrunken elements is calculated
// in the render function, it may be calculated based on
// the content width, and the content width is initially
// decided by the width the element, so it needs to be
// calculated here.
if (width == null) {
left = this.position.left || 0;
if (typeof left === 'string') {
if (left === 'center') left = '50%';
expr = left.split(/(?=\+|-)/);
left = expr[0];
left = +left.slice(0, -1) / 100;
left = parent.width * left | 0;
left += +(expr[1] || 0);
}
width = parent.width - (this.position.right || 0) - left;
if (this.screen.autoPadding) {
if ((this.position.left != null || this.position.right == null)
&& this.position.left !== 'center') {
width -= this.parent.ileft;
}
width -= this.parent.iright;
}
}
return width;
};
/* Depricated:
Element.prototype.__defineGetter__('width', function() {
return this._getWidth(false);
});
*/
Object.defineProperty(Element.prototype,'width',{
get: function () {return this._getWidth(false);},
set: function (val) {
if (this.position.width === val) return;
if (/^\d+$/.test(val)) val = +val;
this.emit('resize');
this.clearPos();
return this.position.width = val;
}
});
Element.prototype._getHeight = function(get) {
var parent = get ? this.parent._getPos() : this.parent
, height = this.position.height
, top
, expr;
if (typeof height === 'string') {
if (height === 'half') height = '50%';
expr = height.split(/(?=\+|-)/);
height = expr[0];
height = +height.slice(0, -1) / 100;
height = parent.height * height | 0;
height += +(expr[1] || 0);
return height;
}
// This is for if the element is being streched or shrunken.
// Although the width for shrunken elements is calculated
// in the render function, it may be calculated based on
// the content width, and the content width is initially
// decided by the width the element, so it needs to be
// calculated here.
if (height == null) {
top = this.position.top || 0;
if (typeof top === 'string') {
if (top === 'center') top = '50%';
expr = top.split(/(?=\+|-)/);
top = expr[0];
top = +top.slice(0, -1) / 100;
top = parent.height * top | 0;
top += +(expr[1] || 0);
}
height = parent.height - (this.position.bottom || 0) - top;
if (this.screen.autoPadding) {
if ((this.position.top != null
|| this.position.bottom == null)
&& this.position.top !== 'center') {
height -= this.parent.itop;
}
height -= this.parent.ibottom;
}
}
return height;
};
/* Depricated
Element.prototype.__defineGetter__('height', function() {
return this._getHeight(false);
});
*/
Object.defineProperty(Element.prototype,'height',{
get: function () {return this._getHeight(false);},
set: function (val) {
if (this.position.height === val) return;
if (/^\d+$/.test(val)) val = +val;
this.emit('resize');
this.clearPos();
return this.position.height = val;
}
});
Element.prototype._getLeft = function(get) {
var parent = get ? this.parent._getPos() : this.parent
, left = this.position.left || 0
, expr;
if (typeof left === 'string') {
if (left === 'center') left = '50%';
expr = left.split(/(?=\+|-)/);
left = expr[0];
left = +left.slice(0, -1) / 100;
left = parent.width * left | 0;
left += +(expr[1] || 0);
if (this.position.left === 'center') {
left -= this._getWidth(get) / 2 | 0;
}
}
if (this.position.left == null && this.position.right != null) {
return this.screen.cols - this._getWidth(get) - this._getRight(get);
}
if (this.screen.autoPadding) {
if ((this.position.left != null
|| this.position.right == null)
&& this.position.left !== 'center') {
left += this.parent.ileft;
}
}
return (parent.aleft || 0) + left;
};
/* Depricated:
Element.prototype.__defineGetter__('aleft', function() {
return this._getLeft(false);
});
*/
Object.defineProperty(Element.prototype,'aleft',{
get: function () {return this._getLeft(false);},
set: function (val) {
var expr;
if (typeof val === 'string') {
if (val === 'center') {
val = this.screen.width / 2 | 0;
val -= this.width / 2 | 0;
} else {
expr = val.split(/(?=\+|-)/);
val = expr[0];
val = +val.slice(0, -1) / 100;
val = this.screen.width * val | 0;
val += +(expr[1] || 0);
}
}
val -= this.parent.aleft;
if (this.position.left === val) return;
this.emit('move');
this.clearPos();
return this.position.left = val;
}
});
Element.prototype._getRight = function(get) {
var parent = get ? this.parent._getPos() : this.parent
, right= this.position.right || 0;
// @blab
if (typeof right === 'string') {
// Hack; relative right align of elements; e.g., 50% of parent width
// usually used with one left and one right aligned element in a row (both relative alignments)
expr = right.split(/(?=\+|-)/);
right = expr[0];
right = +right.slice(0, -1) / 100;
right = Math.ceil(parent.width * right) | 0;
return right;
}
if (this.position.right == null && this.position.left != null) {
right = this.screen.cols - (this._getLeft(get) + this._getWidth(get));
if (this.screen.autoPadding) {
right += this.parent.iright;
}
return right;
}
right = (parent.aright || 0) + (this.position.right || 0);
if (this.screen.autoPadding) {
right += this.parent.iright;
}
return right;
};
/* Depricated
Element.prototype.__defineGetter__('aright', function() {
return this._getRight(false);
});
*/
Object.defineProperty(Element.prototype,'aright',{
get: function () {return this._getRight(false);},
set: function (val) {
val -= this.parent.aright;
if (this.position.right === val) return;
this.emit('move');
this.clearPos();
return this.position.right = val;
}
});
Element.prototype._getTop = function(get) {
var parent = get ? this.parent._getPos() : this.parent
, top = this.position.top || 0
, expr;
if (typeof top === 'string') {
if (top === 'center') top = '50%';
expr = top.split(/(?=\+|-)/);
top = expr[0];
top = +top.slice(0, -1) / 100;
top = parent.height * top | 0;
top += +(expr[1] || 0);
if (this.position.top === 'center') {
top -= this._getHeight(get) / 2 | 0;
}
}
if (this.position.top == null && this.position.bottom != null) {
return this.screen.rows - this._getHeight(get) - this._getBottom(get);
}
if (this.screen.autoPadding) {
if ((this.position.top != null
|| this.position.bottom == null)
&& this.position.top !== 'center') {
top += this.parent.itop;
}
}
return (parent.atop || 0) + top;
};
/* Depricated
Element.prototype.__defineGetter__('atop', function() {
return this._getTop(false);
});
*/
Object.defineProperty(Element.prototype,'atop',{
get: function () {return this._getTop(false);},
set: function (val) {
var expr;
if (typeof val === 'string') {
if (val === 'center') {
val = this.screen.height / 2 | 0;
val -= this.height / 2 | 0;
} else {
expr = val.split(/(?=\+|-)/);
val = expr[0];
val = +val.slice(0, -1) / 100;
val = this.screen.height * val | 0;
val += +(expr[1] || 0);
}
}
val -= this.parent.atop;
if (this.position.top === val) return;
this.emit('move');
this.clearPos();
return this.position.top = val;
}
});
Element.prototype._getBottom = function(get) {
var parent = get ? this.parent._getPos() : this.parent
, bottom;
if (this.position.bottom == null && this.position.top != null) {
bottom = this.screen.rows - (this._getTop(get) + this._getHeight(get));
if (this.screen.autoPadding) {
bottom += this.parent.ibottom;
}
return bottom;
}
bottom = (parent.abottom || 0) + (this.position.bottom || 0);
if (this.screen.autoPadding) {
bottom += this.parent.ibottom;
}
return bottom;
};
/* Depricated
Element.prototype.__defineGetter__('abottom', function() {
return this._getBottom(false);
});
Element.prototype.__defineGetter__('rleft', function() {
return this.aleft - this.parent.aleft;
});
Element.prototype.__defineGetter__('rright', function() {
return this.aright - this.parent.aright;
});
Element.prototype.__defineGetter__('rtop', function() {
return this.atop - this.parent.atop;
});
Element.prototype.__defineGetter__('rbottom', function() {
return this.abottom - this.parent.abottom;
});
*/
Object.defineProperty(Element.prototype,'abottom',{
get: function () {return this._getBottom(false);},
set: function (val) {
val -= this.parent.abottom;
if (this.position.bottom === val) return;
this.emit('move');
this.clearPos();
return this.position.bottom = val;
}
});
Object.defineProperty(Element.prototype,'rleft',{
get: function () {return this.aleft - this.parent.aleft;},
set: function (val) {
if (this.position.left === val) return;
if (/^\d+$/.test(val)) val = +val;
this.emit('move');
this.clearPos();
return this.position.left = val;
}
});
Object.defineProperty(Element.prototype,'rright',{
get: function () {return this.aright - this.parent.aright;},
set: function (val) {
if (this.position.right === val) return;
this.emit('move');
this.clearPos();
return this.position.right = val;
}
});
Object.defineProperty(Element.prototype,'rtop',{
get: function () {return this.atop - this.parent.atop;},
set: function (val) {
if (this.position.top === val) return;
if (/^\d+$/.test(val)) val = +val;
this.emit('move');
this.clearPos();
return this.position.top = val;
}
});
Object.defineProperty(Element.prototype,'rbottom',{
get: function () {return this.abottom - this.parent.abottom;},
set: function (val) {
if (this.position.bottom === val) return;
this.emit('move');
this.clearPos();
return this.position.bottom = val;
}
});
/**
* Position Setters
*/
// NOTE:
// For aright, abottom, right, and bottom:
// If position.bottom is null, we could simply set top instead.
// But it wouldn't replicate bottom behavior appropriately if
// the parent was resized, etc.
/* Depricated
Element.prototype.__defineSetter__('width', function(val) {
if (this.position.width === val) return;
if (/^\d+$/.test(val)) val = +val;
this.emit('resize');
this.clearPos();
return this.position.width = val;
});
Element.prototype.__defineSetter__('height', function(val) {
if (this.position.height === val) return;
if (/^\d+$/.test(val)) val = +val;
this.emit('resize');
this.clearPos();
return this.position.height = val;
});
Element.prototype.__defineSetter__('aleft', function(val) {
var expr;
if (typeof val === 'string') {
if (val === 'center') {
val = this.screen.width / 2 | 0;
val -= this.width / 2 | 0;
} else {
expr = val.split(/(?=\+|-)/);
val = expr[0];
val = +val.slice(0, -1) / 100;
val = this.screen.width * val | 0;
val += +(expr[1] || 0);
}
}
val -= this.parent.aleft;
if (this.position.left === val) return;
this.emit('move');
this.clearPos();
return this.position.left = val;
});
Element.prototype.__defineSetter__('aright', function(val) {
val -= this.parent.aright;
if (this.position.right === val) return;
this.emit('move');
this.clearPos();
return this.position.right = val;
});
Element.prototype.__defineSetter__('atop', function(val) {
var expr;
if (typeof val === 'string') {
if (val === 'center') {
val = this.screen.height / 2 | 0;
val -= this.height / 2 | 0;
} else {
expr = val.split(/(?=\+|-)/);
val = expr[0];
val = +val.slice(0, -1) / 100;
val = this.screen.height * val | 0;
val += +(expr[1] || 0);
}
}
val -= this.parent.atop;
if (this.position.top === val) return;
this.emit('move');
this.clearPos();
return this.position.top = val;
});
Element.prototype.__defineSetter__('abottom', function(val) {
val -= this.parent.abottom;
if (this.position.bottom === val) return;
this.emit('move');
this.clearPos();
return this.position.bottom = val;
});
Element.prototype.__defineSetter__('rleft', function(val) {
if (this.position.left === val) return;
if (/^\d+$/.test(val)) val = +val;
this.emit('move');
this.clearPos();
return this.position.left = val;
});
Element.prototype.__defineSetter__('rright', function(val) {
if (this.position.right === val) return;
this.emit('move');
this.clearPos();
return this.position.right = val;
});
Element.prototype.__defineSetter__('rtop', function(val) {
if (this.position.top === val) return;
if (/^\d+$/.test(val)) val = +val;
this.emit('move');
this.clearPos();
return this.position.top = val;
});
Element.prototype.__defineSetter__('rbottom', function(val) {
if (this.position.bottom === val) return;
this.emit('move');
this.clearPos();
return this.position.bottom = val;
});
*/
/* Depricated
Element.prototype.__defineGetter__('ileft', function() {
return (this.border ? 1 : 0) + this.padding.left;
// return (this.border && this.border.left ? 1 : 0) + this.padding.left;
});
Element.prototype.__defineGetter__('itop', function() {
return (this.border ? 1 : 0) + this.padding.top;
// return (this.border && this.border.top ? 1 : 0) + this.padding.top;
});
Element.prototype.__defineGetter__('iright', function() {
return (this.border ? 1 : 0) + this.padding.right;
// return (this.border && this.border.right ? 1 : 0) + this.padding.right;
});
Element.prototype.__defineGetter__('ibottom', function() {
return (this.border ? 1 : 0) + this.padding.bottom;
// return (this.border && this.border.bottom ? 1 : 0) + this.padding.bottom;
});
Element.prototype.__defineGetter__('iwidth', function() {
// return (this.border
// ? ((this.border.left ? 1 : 0) + (this.border.right ? 1 : 0)) : 0)
// + this.padding.left + this.padding.right;
return (this.border ? 2 : 0) + this.padding.left + this.padding.right;
});
Element.prototype.__defineGetter__('iheight', function() {
// return (this.border
// ? ((this.border.top ? 1 : 0) + (this.border.bottom ? 1 : 0)) : 0)
// + this.padding.top + this.padding.bottom;
return (this.border ? 2 : 0) + this.padding.top + this.padding.bottom;
});
Element.prototype.__defineGetter__('tpadding', function() {
return this.padding.left + this.padding.top
+ this.padding.right + this.padding.bottom;
});
*/
Object.defineProperty(Element.prototype,'ileft',{
get: function () {
return (this.border ? 1 : 0) + this.padding.left;
// return (this.border && this.border.left ? 1 : 0) + this.padding.left;
}
});
Object.defineProperty(Element.prototype,'itop',{
get: function () {
return (this.border ? 1 : 0) + this.padding.top;
// return (this.border && this.border.top ? 1 : 0) + this.padding.top;
}
});
Object.defineProperty(Element.prototype,'iright',{
get: function () {
return (this.border ? 1 : 0) + this.padding.right;
// return (this.border && this.border.right ? 1 : 0) + this.padding.right;
}
});
Object.defineProperty(Element.prototype,'ibottom',{
get: function () {
return (this.border ? 1 : 0) + this.padding.bottom;
// return (this.border && this.border.bottom ? 1 : 0) + this.padding.bottom;
}
});
Object.defineProperty(Element.prototype,'iwidth',{
get: function () {
// return (this.border
// ? ((this.border.left ? 1 : 0) + (this.border.right ? 1 : 0)) : 0)
// + this.padding.left + this.padding.right;
return (this.border ? 2 : 0) + this.padding.left + this.padding.right;
}
});
Object.defineProperty(Element.prototype,'iheight',{
get: function () {
// return (this.border
// ? ((this.border.top ? 1 : 0) + (this.border.bottom ? 1 : 0)) : 0)
// + this.padding.top + this.padding.bottom;
return (this.border ? 2 : 0) + this.padding.top + this.padding.bottom;
}
});
Object.defineProperty(Element.prototype,'tpadding',{
get: function () {
return this.padding.left + this.padding.top
+ this.padding.right + this.padding.bottom;
}
});
/**
* Relative coordinates as default properties
*/
/* Depricated
Element.prototype.__defineGetter__('left', function() {
return this.rleft;
});
Element.prototype.__defineGetter__('right', function() {
return this.rright;
});
Element.prototype.__defineGetter__('top', function() {
return this.rtop;
});
Element.prototype.__defineGetter__('bottom', function() {
return this.rbottom;
});
*/
Object.defineProperty(Element.prototype,'left',{
get: function () {return this.rleft;},
set: function (val) {
return this.rleft = val;
}
});
Object.defineProperty(Element.prototype,'right',{
get: function () {return this.rright;},
set: function (val) {
return this.rright = val;
}
});
Object.defineProperty(Element.prototype,'top',{
get: function () {return this.rtop;},
set: function (val) {
return this.rtop = val;
}
});
Object.defineProperty(Element.prototype,'bottom',{
get: function () {return this.rbottom;},
set: function (val) {
return this.rbottom = val;
}
});
/* Depricated
Element.prototype.__defineSetter__('left', function(val) {
return this.rleft = val;
});
Element.prototype.__defineSetter__('right', function(val) {
return this.rright = val;
});
Element.prototype.__defineSetter__('top', function(val) {
return this.rtop = val;
});
Element.prototype.__defineSetter__('bottom', function(val) {
return this.rbottom = val;
});
*/
/**
* Rendering - here be dragons
*/
Element.prototype._getShrinkBox = function(xi, xl, yi, yl, get) {
if (!this.children.length) {
return { xi: xi, xl: xi + 1, yi: yi, yl: yi + 1 };
}
var i, el, ret, mxi = xi, mxl = xi + 1, myi = yi, myl = yi + 1;
// This is a chicken and egg problem. We need to determine how the children
// will render in order to determine how this element renders, but it in
// order to figure out how the children will render, they need to know
// exactly how their parent renders, so, we can give them what we have so
// far.
var _lpos;
if (get) {
_lpos = this.lpos;
this.lpos = { xi: xi, xl: xl, yi: yi, yl: yl };
//this.shrink = false;
}
for (i = 0; i < this.children.length; i++) {
el = this.children[i];
ret = el._getCoords(get);
// Or just (seemed to work, but probably not good):
// ret = el.lpos || this.lpos;
if (!ret) continue;
// Since the parent element is shrunk, and the child elements think it's
// going to take up as much space as possible, an element anchored to the
// right or bottom will inadvertantly make the parent's shrunken size as
// large as possible. So, we can just use the height and/or width the of
// element.
// if (get) {
if (el.position.left == null && el.position.right != null) {
ret.xl = xi + (ret.xl - ret.xi);
ret.xi = xi;
if (this.screen.autoPadding) {
// Maybe just do this no matter what.
ret.xl += this.ileft;
ret.xi += this.ileft;
}
}
if (el.position.top == null && el.position.bottom != null) {
ret.yl = yi + (ret.yl - ret.yi);
ret.yi = yi;
if (this.screen.autoPadding) {
// Maybe just do this no matter what.
ret.yl += this.itop;
ret.yi += this.itop;
}
}
if (ret.xi < mxi) mxi = ret.xi;
if (ret.xl > mxl) mxl = ret.xl;
if (ret.yi < myi) myi = ret.yi;
if (ret.yl > myl) myl = ret.yl;
}
if (get) {
this.lpos = _lpos;
//this.shrink = true;
}
if (this.position.width == null
&& (this.position.left == null
|| this.position.right == null)) {
if (this.position.left == null && this.position.right != null) {
xi = xl - (mxl - mxi);
if (!this.screen.autoPadding) {
xi -= this.padding.left + this.padding.right;
} else {
xi -= this.ileft;
}
} else {
xl = mxl;
if (!this.screen.autoPadding) {
xl += this.padding.left + this.padding.right;
// XXX Temporary workaround until we decide to make autoPadding default.
// See widget-listtable.js for an example of why this is necessary.
// XXX Maybe just to this for all this being that this would affect
// width shrunken normal shrunken lists as well.
// if (this._isList) {
if (this.type === 'list-table') {
xl -= this.padding.left + this.padding.right;
xl += this.iright;
}
} else {
//xl += this.padding.right;
xl += this.iright;
}
}
}
if (this.position.height == null
&& (this.position.top == null
|| this.position.bottom == null)
&& (!this.scrollable || this._isList)) {
// NOTE: Lists get special treatment if they are shrunken - assume they
// want all list items showing. This is one case we can calculate the
// height based on items/boxes.
if (this._isList) {
myi = 0 - this.itop;
myl = this.items.length + this.ibottom;
}
if (this.position.top == null && this.position.bottom != null) {
yi = yl - (myl - myi);
if (!this.screen.autoPadding) {
yi -= this.padding.top + this.padding.bottom;
} else {
yi -= this.itop;
}
} else {
yl = myl;
if (!this.screen.autoPadding) {
yl += this.padding.top + this.padding.bottom;
} else {
yl += this.ibottom;
}
}
}
return { xi: xi, xl: xl, yi: yi, yl: yl };
};
Element.prototype._getShrinkContent = function(xi, xl, yi, yl) {
// @blab+
if (!this._clines) return { xi: xi, xl: xl, yi: yi, yl: yl };
var h = this._clines.length
, w = this._clines.mwidth || 1;
if (this.position.width == null
&& (this.position.left == null
|| this.position.right == null)) {
if (this.position.left == null && this.position.right != null) {
xi = xl - w - this.iwidth;
} else {
xl = xi + w + this.iwidth;
}
}
if (this.position.height == null
&& (this.position.top == null
|| this.position.bottom == null)
&& (!this.scrollable || this._isList)) {
if (this.position.top == null && this.position.bottom != null) {
yi = yl - h - this.iheight;
} else {
yl = yi + h + this.iheight;
}
}
return { xi: xi, xl: xl, yi: yi, yl: yl };
};
Element.prototype._getShrink = function(xi, xl, yi, yl, get) {
var shrinkBox = this._getShrinkBox(xi, xl, yi, yl, get)
, shrinkContent = this._getShrinkContent(xi, xl, yi, yl, get)
, xll = xl
, yll = yl;
// Figure out which one is bigger and use it.
if (shrinkBox.xl - shrinkBox.xi > shrinkContent.xl - shrinkContent.xi) {
xi = shrinkBox.xi;
xl = shrinkBox.xl;
} else {
xi = shrinkContent.xi;
xl = shrinkContent.xl;
}
if (shrinkBox.yl - shrinkBox.yi > shrinkContent.yl - shrinkContent.yi) {
yi = shrinkBox.yi;
yl = shrinkBox.yl;
} else {
yi = shrinkContent.yi;
yl = shrinkContent.yl;
}
// Recenter shrunken elements.
if (xl < xll && this.position.left === 'center') {
xll = (xll - xl) / 2 | 0;
xi += xll;
xl += xll;
}
if (yl < yll && this.position.top === 'center') {
yll = (yll - yl) / 2 | 0;
yi += yll;
yl += yll;
}
return { xi: xi, xl: xl, yi: yi, yl: yl };
};
Element.prototype._getCoords = function(get, noscroll) {
if (this.hidden) return;
// if (this.parent._rendering) {
// get = true;
// }
var xi = this._getLeft(get)
, xl = xi + this._getWidth(get)
, yi = this._getTop(get)
, yl = yi + this._getHeight(get)
, base = this.childBase || 0
, el = this
, fixed = this.fixed
, coords
, v
, noleft
, noright
, notop
, nobot
, ppos
, b;
// Attempt to shrink the element base on the
// size of the content and child elements.
if (this.shrink) {
coords = this._getShrink(xi, xl, yi, yl, get);
xi = coords.xi, xl = coords.xl;
yi = coords.yi, yl = coords.yl;
}
// Find a scrollable ancestor if we have one.
while (el = el.parent) {
if (el.scrollable) {
if (fixed) {
fixed = false;
continue;
}
break;
}
}
// Check to make sure we're visible and
// inside of the visible scroll area.
// NOTE: Lists have a property where only
// the list items are obfuscated.
// Old way of doing things, this would not render right if a shrunken element
// with lots of boxes in it was within a scrollable element.
// See: $ node test/widget-shrink-fail.js
// var thisparent = this.parent;
var thisparent = el;
if (el && !noscroll) {
ppos = thisparent.lpos;
// The shrink option can cause a stack overflow
// by calling _getCoords on the child again.
// if (!get && !thisparent.shrink) {
// ppos = thisparent._getCoords();
// }
if (!ppos) return;
// TODO: Figure out how to fix base (and cbase to only
// take into account the *parent's* padding.
yi -= ppos.base;
yl -= ppos.base;
b = thisparent.border ? 1 : 0;
// XXX
// Fixes non-`fixed` labels to work with scrolling (they're ON the border):
// if (this.position.left < 0
// || this.position.right < 0
// || this.position.top < 0
// || this.position.bottom < 0) {
if (this._isLabel) {
b = 0;
}
if (yi < ppos.yi + b) {
if (yl - 1 < ppos.yi + b) {
// Is above.
return;
} else {
// Is partially covered above.
notop = true;
v = ppos.yi - yi;
if (this.border) v--;
if (thisparent.border) v++;
base += v;
yi += v;
}
} else if (yl > ppos.yl - b) {
if (yi > ppos.yl - 1 - b) {
// Is below.
return;
} else {
// Is partially covered below.
nobot = true;
v = yl - ppos.yl;
if (this.border) v--;
if (thisparent.border) v++;
yl -= v;
}
}
// Shouldn't be necessary.
// assert.ok(yi < yl);
if (yi >= yl) return;
// Could allow overlapping stuff in scrolling elements
// if we cleared the pending buffer before every draw.
if (xi < el.lpos.xi) {
xi = el.lpos.xi;
noleft = true;
if (this.border) xi--;
if (thisparent.border) xi++;
}
if (xl > el.lpos.xl) {
xl = el.lpos.xl;
noright = true;
if (this.border) xl++;
if (thisparent.border) xl--;
}
//if (xi > xl) return;
if (xi >= xl) return;
}
if (this.noOverflow && this.parent.lpos) {
if (xi < this.parent.lpos.xi + this.parent.ileft) {
xi = this.parent.lpos.xi + this.parent.ileft;
}
if (xl > this.parent.lpos.xl - this.parent.iright) {
xl = this.parent.lpos.xl - this.parent.iright;
}
if (yi < this.parent.lpos.yi + this.parent.itop) {
yi = this.parent.lpos.yi + this.parent.itop;
}
if (yl > this.parent.lpos.yl - this.parent.ibottom) {
yl = this.parent.lpos.yl - this.parent.ibottom;
}
}
// if (this.parent.lpos) {
// this.parent.lpos._scrollBottom = Math.max(
// this.parent.lpos._scrollBottom, yl);
// }
return {
xi: xi,
xl: xl,
yi: yi,
yl: yl,
base: base,
noleft: noleft,
noright: noright,
notop: notop,
nobot: nobot,
renders: this.screen.renders
};
};
Element.prototype.render = function() {
this._emit('prerender');
this.parseContent();
var coords = this._getCoords(true);
if (!coords) {
delete this.lpos;
return;
}
if (coords.xl - coords.xi <= 0) {
coords.xl = Math.max(coords.xl, coords.xi);
return;
}
if (coords.yl - coords.yi <= 0) {
coords.yl = Math.max(coords.yl, coords.yi);
return;
}
var lines = this.screen.lines
, xi = coords.xi
, xl = coords.xl
, yi = coords.yi
, yl = coords.yl
, x
, y
, cell
, attr
, ch
, content = this._pcontent
, ci = this._clines.ci[coords.base]
, battr
, dattr
, c
, visible
, i
, bch = this.ch;
// Clip content if it's off the edge of the screen
// if (xi + this.ileft < 0 || yi + this.itop < 0) {
// var clines = this._clines.slice();
// if (xi + this.ileft < 0) {
// for (var i = 0; i < clines.length; i++) {
// var t = 0;
// var csi = '';
// var csis = '';
// for (var j = 0; j < clines[i].length; j++) {
// while (clines[i][j] === '\x1b') {
// csi = '\x1b';
// while (clines[i][j++] !== 'm') csi += clines[i][j];
// csis += csi;
// }
// if (++t === -(xi + this.ileft) + 1) break;
// }
// clines[i] = csis + clines[i].substring(j);
// }
// }
// if (yi + this.itop < 0) {
// clines = clines.slice(-(yi + this.itop));
// }
// content = clines.join('\n');
// }
if (coords.base >= this._clines.ci.length) {
ci = this._pcontent.length;
}
this.lpos = coords;
if (this.border && this.border.type === 'line') {
this.screen._borderStops[coords.yi] = true;
this.screen._borderStops[coords.yl - 1] = true;
// if (!this.screen._borderStops[coords.yi]) {
// this.screen._borderStops[coords.yi] = { xi: coords.xi, xl: coords.xl };
// } else {
// if (this.screen._borderStops[coords.yi].xi > coords.xi) {
// this.screen._borderStops[coords.yi].xi = coords.xi;
// }
// if (this.screen._borderStops[coords.yi].xl < coords.xl) {
// this.screen._borderStops[coords.yi].xl = coords.xl;
// }
// }
// this.screen._borderStops[coords.yl - 1] = this.screen._borderStops[coords.yi];
}
dattr = this.sattr(this.style);
attr = dattr;
// If we're in a scrollable text box, check to
// see which attributes this line starts with.
if (ci > 0) {
attr = this._clines.attr[Math.min(coords.base, this._clines.length - 1)];
}
if (this.border) xi++, xl--, yi++, yl--;
// If we have padding/valign, that means the
// content-drawing loop will skip a few cells/lines.
// To deal with this, we can just fill the whole thing
// ahead of time. This could be optimized.
if (this.tpadding || (this.valign && this.valign !== 'top')) {
if (this.style.transparent) {
for (y = Math.max(yi, 0); y < yl; y++) {
if (!lines[y]) break;
for (x = Math.max(xi, 0); x < xl; x++) {
if (!lines[y][x]) break;
lines[y][x][0] = colors.blend(attr, lines[y][x][0]);
// lines[y][x][1] = bch;
lines[y].dirty = true;
}
}
} else {
this.screen.fillRegion(dattr, bch, xi, xl, yi, yl);
}
}
if (this.tpadding) {
xi += this.padding.left, xl -= this.padding.right;
yi += this.padding.top, yl -= this.padding.bottom;
}
// Determine where to place the text if it's vertically aligned.
if (this.valign === 'middle' || this.valign === 'bottom') {
visible = yl - yi;
if (this._clines.length < visible) {
if (this.valign === 'middle') {
visible = visible / 2 | 0;
visible -= this._clines.length / 2 | 0;
} else if (this.valign === 'bottom') {
visible -= this._clines.length;
}
ci -= visible * (xl - xi);
}
}
// Draw the content and background.
for (y = yi; y < yl; y++) {
if (!lines[y]) {
if (y >= this.screen.height || yl < this.ibottom) {
break;
} else {
continue;
}
}
for (x = xi; x < xl; x++) {
cell = lines[y][x];
if (!cell) {
if (x >= this.screen.width || xl < this.iright) {
break;
} else {
continue;
}
}
ch = content[ci++] || bch;
// if (!content[ci] && !coords._contentEnd) {
// coords._contentEnd = { x: x - xi, y: y - yi };
// }
// Handle escape codes.
while (ch === '\x1b') {
if (c = /^\x1b\[[\d;]*m/.exec(content.substring(ci - 1))) {
ci += c[0].length - 1;
attr = this.screen.attrCode(c[0], attr, dattr);
// Ignore foreground changes for selected items.
if (this.parent._isList && this.parent.interactive
&& this.parent.items[this.parent.selected] === this
&& this.parent.options.invertSelected !== false) {
attr = (attr & ~(0x1ff << 9)) | (dattr & (0x1ff << 9));
}
ch = content[ci] || bch;
ci++;
} else {
break;
}
}
// Handle newlines.
if (ch === '\t') ch = bch;
if (ch === '\n') {
// If we're on the first cell and we find a newline and the last cell
// of the last line was not a newline, let's just treat this like the
// newline was already "counted".
if (x === xi && y !== yi && content[ci - 2] !== '\n') {
x--;
continue;
}
// We could use fillRegion here, name the
// outer loop, and continue to it instead.
ch = bch;
for (; x < xl; x++) {
cell = lines[y][x];
if (!cell) break;
if (this.style.transparent) {
lines[y][x][0] = colors.blend(attr, lines[y][x][0]);
if (content[ci]) lines[y][x][1] = ch;
lines[y].dirty = true;
} else {
if (attr !== cell[0] || ch !== cell[1]) {
lines[y][x][0] = attr;
lines[y][x][1] = ch;
lines[y].dirty = true;
}
}
}
continue;
}
if (this.screen.fullUnicode && content[ci - 1]) {
var point = unicode.codePointAt(content, ci - 1);
// Handle combining chars:
// Make sure they get in the same cell and are counted as 0.
if (unicode.combining[point]) {
if (point > 0x00ffff) {
ch = content[ci - 1] + content[ci];
ci++;
}
if (x - 1 >= xi) {
lines[y][x - 1][1] += ch;
} else if (y - 1 >= yi) {
lines[y - 1][xl - 1][1] += ch;
}
x--;
continue;
}
// Handle surrogate pairs:
// Make sure we put surrogate pair chars in one cell.
if (point > 0x00ffff) {
ch = content[ci - 1] + content[ci];
ci++;
}
}
if (this._noFill) continue;
if (this.style.transparent) {
lines[y][x][0] = colors.blend(attr, lines[y][x][0]);
if (content[ci]) lines[y][x][1] = ch;
lines[y].dirty = true;
} else {
if (attr !== cell[0] || ch !== cell[1]) {
lines[y][x][0] = attr;
lines[y][x][1] = ch;
lines[y].dirty = true;
}
}
}
}
// Draw the scrollbar.
// Could possibly draw this after all child elements.
if (this.scrollbar) {
// XXX
// i = this.getScrollHeight();
i = Math.max(this._clines.length, this._scrollBottom());
}
if (coords.notop || coords.nobot) i = -Infinity;
if (this.scrollbar && (yl - yi) < i) {
x = xl - 1;
if (this.scrollbar.ignoreBorder && this.border) x++;
if (this.alwaysScroll) {
y = this.childBase / (i - (yl - yi));
} else {
y = (this.childBase + this.childOffset) / (i - 1);
}
y = yi + ((yl - yi) * y | 0);
if (y >= yl) y = yl - 1;
cell = lines[y] && lines[y][x];
if (cell) {
if (this.track) {
ch = this.track.ch || ' ';
attr = this.sattr(this.style.track,
this.style.track.fg || this.style.fg,
this.style.track.bg || this.style.bg);
this.screen.fillRegion(attr, ch, x, x + 1, yi, yl);
}
ch = this.scrollbar.ch || ' ';
attr = this.sattr(this.style.scrollbar,
this.style.scrollbar.fg || this.style.fg,
this.style.scrollbar.bg || this.style.bg);
if (attr !== cell[0] || ch !== cell[1]) {
lines[y][x][0] = attr;
lines[y][x][1] = ch;
lines[y].dirty = true;
}
}
}
if (this.border) xi--, xl++, yi--, yl++;
if (this.tpadding) {
xi -= this.padding.left, xl += this.padding.right;
yi -= this.padding.top, yl += this.padding.bottom;
}
// Draw the border.
if (this.border) {
battr = this.sattr(this.style.border);
y = yi;
if (coords.notop) y = -1;
for (x = xi; x < xl; x++) {
if (!lines[y]) break;
if (coords.noleft && x === xi) continue;
if (coords.noright && x === xl - 1) continue;
cell = lines[y][x];
if (!cell) continue;
if (this.border.type === 'line') {
if (x === xi) {
if (!this.border.double) ch = '\u250c'; else ch = '\u2554'; // '┌'
if (!this.border.left) {
if (this.border.top) {
if (!this.border.double) ch = '\u2500'; else ch = '\u2550'; // '─'
} else {
continue;
}
} else {
if (!this.border.top) {
if (!this.border.double) ch = '\u2502'; else ch = '\u2551'; // '│'
}
}
} else if (x === xl - 1) {
if (!this.border.double) ch = '\u2510'; else ch = '\u2557'; // '┐'
if (!this.border.right) {
if (this.border.top) {
if (!this.border.double) ch = '\u2500'; else ch = '\u2550'; // '─'
} else {
continue;
}
} else {
if (!this.border.top) {
if (!this.border.double) ch = '\u2502'; else ch = '\u2551'; // '│'
}
}
} else {
if (!this.border.double) ch = '\u2500'; else ch = '\u2550';// '─'
}
} else if (this.border.type === 'bg') {
ch = this.border.ch;
}
if (!this.border.top && x !== xi && x !== xl - 1) {
ch = ' ';
if (dattr !== cell[0] || ch !== cell[1]) {
lines[y][x][0] = dattr;
lines[y][x][1] = ch;
lines[y].dirty = true;
continue;
}
}
if (battr !== cell[0] || ch !== cell[1]) {
lines[y][x][0] = battr;
lines[y][x][1] = ch;
lines[y].dirty = true;
}
}
y = yi + 1;
for (; y < yl - 1; y++) {
if (!lines[y]) continue;
cell = lines[y][xi];
if (cell) {
if (this.border.left) {
if (this.border.type === 'line') {
if (!this.border.double) ch = '\u2502'; else ch = '\u2551'; // '│'
} else if (this.border.type === 'bg') {
ch = this.border.ch;
}
if (!coords.noleft)
if (battr !== cell[0] || ch !== cell[1]) {
lines[y][xi][0] = battr;
lines[y][xi][1] = ch;
lines[y].dirty = true;
}
} else {
ch = ' ';
if (dattr !== cell[0] || ch !== cell[1]) {
lines[y][xi][0] = dattr;
lines[y][xi][1] = ch;
lines[y].dirty = true;
}
}
}
cell = lines[y][xl - 1];
if (cell) {
if (this.border.right) {
if (this.border.type === 'line') {
if (!this.border.double) ch = '\u2502'; else ch = '\u2551'; // '│'
} else if (this.border.type === 'bg') {
ch = this.border.ch;
}
if (!coords.noright)
if (battr !== cell[0] || ch !== cell[1]) {
lines[y][xl - 1][0] = battr;
lines[y][xl - 1][1] = ch;
lines[y].dirty = true;
}
} else {
ch = ' ';
if (dattr !== cell[0] || ch !== cell[1]) {
lines[y][xl - 1][0] = dattr;
lines[y][xl - 1][1] = ch;
lines[y].dirty = true;
}
}
}
}
y = yl - 1;
if (coords.nobot) y = -1;
for (x = xi; x < xl; x++) {
if (!lines[y]) break;
if (coords.noleft && x === xi) continue;
if (coords.noright && x === xl - 1) continue;
cell = lines[y][x];
if (!cell) continue;
if (this.border.type === 'line') {
if (x === xi) {
if (!this.border.double) ch = '\u2514'; else ch = '\u255a'; // '└'
if (!this.border.left) {
if (this.border.bottom) {
if (!this.border.double) ch = '\u2500'; else ch = '\u2550'; // '─'
} else {
continue;
}
} else {
if (!this.border.bottom) {
if (!this.border.double) ch = '\u2502'; else ch = '\u2551'; // '│'
}
}
} else if (x === xl - 1) {
if (!this.border.double) ch = '\u2518'; else ch = '\u255d'; // '┘'
if (!this.border.right) {
if (this.border.bottom) {
if (!this.border.double) ch = '\u2500'; else ch = '\u2550'; // '─'
} else {
continue;
}
} else {
if (!this.border.bottom) {
if (!this.border.double) ch = '\u2502'; else ch = '\u2551'; // '│'
}
}
} else {
if (!this.border.double) ch = '\u2500'; else ch = '\u2550'; // '─'
}
} else if (this.border.type === 'bg') {
ch = this.border.ch;
}
if (!this.border.bottom && x !== xi && x !== xl - 1) {
ch = ' ';
if (dattr !== cell[0] || ch !== cell[1]) {
lines[y][x][0] = dattr;
lines[y][x][1] = ch;
lines[y].dirty = true;
}
continue;
}
if (battr !== cell[0] || ch !== cell[1]) {
lines[y][x][0] = battr;
lines[y][x][1] = ch;
lines[y].dirty = true;
}
}
}
if (this.shadow) {
// right
y = Math.max(yi + 1, 0);
for (; y < yl + 1; y++) {
if (!lines[y]) break;
x = xl;
for (; x < xl + 2; x++) {
if (!lines[y][x]) break;
// lines[y][x][0] = colors.blend(this.dattr, lines[y][x][0]);
lines[y][x][0] = colors.blend(lines[y][x][0]);
lines[y].dirty = true;
}
}
// bottom
y = yl;
for (; y < yl + 1; y++) {
if (!lines[y]) break;
for (x = Math.max(xi + 1, 0); x < xl; x++) {
if (!lines[y][x]) break;
// lines[y][x][0] = colors.blend(this.dattr, lines[y][x][0]);
lines[y][x][0] = colors.blend(lines[y][x][0]);
lines[y].dirty = true;
}
}
}
this.children.forEach(function(el) {
if (el.screen._ci !== -1) {
el.index = el.screen._ci++;
}
// if (el.screen._rendering) {
// el._rendering = true;
// }
el.render();
// if (el.screen._rendering) {
// el._rendering = false;
// }
});
this._emit('render', [coords]);
return coords;
};
Element.prototype._render = Element.prototype.render;
/**
* Content Methods
*/
Element.prototype.insertLine = function(i, line) {
if (typeof line === 'string') line = line.split('\n');
if (i !== i || i == null) {
i = this._clines.ftor.length;
}
i = Math.max(i, 0);
while (this._clines.fake.length < i) {
this._clines.fake.push('');
this._clines.ftor.push([this._clines.push('') - 1]);
this._clines.rtof(this._clines.fake.length - 1);
}
// NOTE: Could possibly compare the first and last ftor line numbers to see
// if they're the same, or if they fit in the visible region entirely.
var start = this._clines.length
, diff
, real;
if (i >= this._clines.ftor.length) {
real = this._clines.ftor[this._clines.ftor.length - 1];
real = real[real.length - 1] + 1;
} else {
real = this._clines.ftor[i][0];
}
for (var j = 0; j < line.length; j++) {
this._clines.fake.splice(i + j, 0, line[j]);
}
this.setContent(this._clines.fake.join('\n'), true);
diff = this._clines.length - start;
if (diff > 0) {
var pos = this._getCoords();
if (!pos) return;
var height = pos.yl - pos.yi - this.iheight
, base = this.childBase || 0
, visible = real >= base && real - base < height;
if (pos && visible && this.screen.cleanSides(this)) {
this.screen.insertLine(diff,
pos.yi + this.itop + real - base,
pos.yi,
pos.yl - this.ibottom - 1);
}
}
};
Element.prototype.deleteLine = function(i, n) {
n = n || 1;
if (i !== i || i == null) {
i = this._clines.ftor.length - 1;
}
i = Math.max(i, 0);
i = Math.min(i, this._clines.ftor.length - 1);
// NOTE: Could possibly compare the first and last ftor line numbers to see
// if they're the same, or if they fit in the visible region entirely.
var start = this._clines.length
, diff
, real = this._clines.ftor[i][0];
while (n--) {
this._clines.fake.splice(i, 1);
}
this.setContent(this._clines.fake.join('\n'), true);
diff = start - this._clines.length;
// XXX clearPos() without diff statement?
var height = 0;
if (diff > 0) {
var pos = this._getCoords();
if (!pos) return;
height = pos.yl - pos.yi - this.iheight;
var base = this.childBase || 0
, visible = real >= base && real - base < height;
if (pos && visible && this.screen.cleanSides(this)) {
this.screen.deleteLine(diff,
pos.yi + this.itop + real - base,
pos.yi,
pos.yl - this.ibottom - 1);
}
}
if (this._clines.length < height) {
this.clearPos();
}
};
Element.prototype.insertTop = function(line) {
var fake = this._clines.rtof[this.childBase || 0];
return this.insertLine(fake, line);
};
Element.prototype.insertBottom = function(line) {
var h = (this.childBase || 0) + this.height - this.iheight
, i = Math.min(h, this._clines.length)
, fake = this._clines.rtof[i - 1] + 1;
return this.insertLine(fake, line);
};
Element.prototype.deleteTop = function(n) {
var fake = this._clines.rtof[this.childBase || 0];
return this.deleteLine(fake, n);
};
Element.prototype.deleteBottom = function(n) {
var h = (this.childBase || 0) + this.height - 1 - this.iheight
, i = Math.min(h, this._clines.length - 1)
, fake = this._clines.rtof[i];
n = n || 1;
return this.deleteLine(fake - (n - 1), n);
};
Element.prototype.setLine = function(i, line) {
i = Math.max(i, 0);
while (this._clines.fake.length < i) {
this._clines.fake.push('');
}
this._clines.fake[i] = line;
return this.setContent(this._clines.fake.join('\n'), true);
};
Element.prototype.setBaseLine = function(i, line) {
var fake = this._clines.rtof[this.childBase || 0];
return this.setLine(fake + i, line);
};
Element.prototype.getLine = function(i) {
i = Math.max(i, 0);
i = Math.min(i, this._clines.fake.length - 1);
return this._clines.fake[i];
};
Element.prototype.getBaseLine = function(i) {
var fake = this._clines.rtof[this.childBase || 0];
return this.getLine(fake + i);
};
Element.prototype.clearLine = function(i) {
i = Math.min(i, this._clines.fake.length - 1);
return this.setLine(i, '');
};
Element.prototype.clearBaseLine = function(i) {
var fake = this._clines.rtof[this.childBase || 0];
return this.clearLine(fake + i);
};
Element.prototype.unshiftLine = function(line) {
return this.insertLine(0, line);
};
Element.prototype.shiftLine = function(n) {
return this.deleteLine(0, n);
};
Element.prototype.pushLine = function(line) {
if (!this.content) return this.setLine(0, line);
return this.insertLine(this._clines.fake.length, line);
};
Element.prototype.popLine = function(n) {
return this.deleteLine(this._clines.fake.length - 1, n);
};
Element.prototype.getLines = function() {
return this._clines.fake.slice();
};
Element.prototype.getScreenLines = function() {
return this._clines.slice();
};
Element.prototype.strWidth = function(text) {
text = this.parseTags
? helpers.stripTags(text)
: text;
return this.screen.fullUnicode
? unicode.strWidth(text)
: helpers.dropUnicode(text).length;
};
Element.prototype.screenshot = function(xi, xl, yi, yl) {
xi = this.lpos.xi + this.ileft + (xi || 0);
if (xl != null) {
xl = this.lpos.xi + this.ileft + (xl || 0);
} else {
xl = this.lpos.xl - this.iright;
}
yi = this.lpos.yi + this.itop + (yi || 0);
if (yl != null) {
yl = this.lpos.yi + this.itop + (yl || 0);
} else {
yl = this.lpos.yl - this.ibottom;
}
return this.screen.screenshot(xi, xl, yi, yl);
};
/**
* Expose
*/
module.exports = Element;
};
BundleModuleCode['term/widgets/arrows']=function (module,exports){
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var Helpers = Require('term/helpers');
var Button = Require('term/widgets/button');
// Add up and down arrow buttons on the right outside of a widget
// options.arrows: {up:'[-]',down:'[+]',width:3,height:1,fg:'red',bg:'default'}}
module.exports = function (parent,options,up,down,nocontrol) {
var bbox;
// Bbox computing for button positions; relatives can only be resolved to screen
// coordinates?
bbox=Helpers.bbox(parent.screen,options);
parent._.up = new Button({
screen: parent.screen,
top: bbox.top+1,
height: options.arrows.height||1,
left: bbox.left+bbox.width,
width: options.arrows.width||3,
content: options.arrows.up||'[-]',
align: 'center',
style: {
fg:options.arrows.fg||'red',
bg: options.arrows.bg||'white',
bold:true,
},
autoFocus: false,
hidden:options.hidden,
mouse: true
});
parent._.up.on('press',up);
parent.screen.append(parent._.up);
parent._.down = new Button({
screen: this.screen,
top: bbox.top+bbox.height-1-(options.arrows.height||1),
height: options.arrows.height||1,
left: bbox.left+bbox.width,
width: options.arrows.width||3,
content: options.arrows.down||'[+]',
align: 'center',
style: {
fg:options.arrows.fg||'red',
bg: options.arrows.bg||'white',
bold:true,
},
autoFocus: false,
hidden:options.hidden,
mouse: true
});
parent._.down.on('press',down);
parent.screen.append(parent._.down);
if (!nocontrol) {
parent._hide=parent.hide;
parent.hide = function() {
parent._hide();
if (parent._.up) parent._.up.hide();
if (parent._.down) parent._.down.hide();
parent.screen.render();
}
parent._show = parent.show;
parent.show = function() {
parent._show();
if (parent._.up) parent._.up.show();
if (parent._.down) parent._.down.show();
parent.screen.render();
}
}
}
};
BundleModuleCode['term/widgets/scrollabletext']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.1
**
** $INFO:
**
** scrollabletext.js - scrollable text element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var ScrollableBox = Require('term/widgets/scrollablebox');
/**
* ScrollableText
*/
function ScrollableText(options) {
if (!instanceOf(this,Node)) {
return new ScrollableText(options);
}
options = options || {};
options.alwaysScroll = true;
ScrollableBox.call(this, options);
}
//ScrollableText.prototype.__proto__ = ScrollableBox.prototype;
inheritPrototype(ScrollableText,ScrollableBox);
ScrollableText.prototype.type = 'scrollable-text';
/**
* Expose
*/
module.exports = ScrollableText;
};
BundleModuleCode['term/widgets/scrollablebox']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2016, Christopher Jeffrey and contributors
** $MODIFIED: sbosse (2017-2021).
** $VERSION: 1.2.3
**
** $INFO:
*
* scrollablebox.js - scrollable box element for blessed
*
* events: 'mouse', 'click' (low level), 'clicked' (high level)
*
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* ScrollableBox
*/
function ScrollableBox(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new ScrollableBox(options);
}
options = options || {};
Box.call(this, options);
if (options.scrollable === false) {
return this;
}
this.scrollable = true;
this.childOffset = 0;
this.childBase = 0;
this.baseLimit = options.baseLimit || Infinity;
this.alwaysScroll = options.alwaysScroll;
this.scrollbar = options.scrollbar;
if (this.scrollbar) {
this.scrollbar.ch = this.scrollbar.ch || ' ';
this.style.scrollbar = this.style.scrollbar || this.scrollbar.style;
if (!this.style.scrollbar) {
this.style.scrollbar = {};
this.style.scrollbar.fg = this.scrollbar.fg;
this.style.scrollbar.bg = this.scrollbar.bg;
this.style.scrollbar.bold = this.scrollbar.bold;
this.style.scrollbar.underline = this.scrollbar.underline;
this.style.scrollbar.inverse = this.scrollbar.inverse;
this.style.scrollbar.invisible = this.scrollbar.invisible;
}
//this.scrollbar.style = this.style.scrollbar;
if (this.track || this.scrollbar.track) {
this.track = this.scrollbar.track || this.track;
this.style.track = this.style.scrollbar.track || this.style.track;
this.track.ch = this.track.ch || ' ';
this.style.track = this.style.track || this.track.style;
if (!this.style.track) {
this.style.track = {};
this.style.track.fg = this.track.fg;
this.style.track.bg = this.track.bg;
this.style.track.bold = this.track.bold;
this.style.track.underline = this.track.underline;
this.style.track.inverse = this.track.inverse;
this.style.track.invisible = this.track.invisible;
}
this.track.style = this.style.track;
}
// Allow controlling of the scrollbar via the mouse:
if (options.mouse) {
this.on('mousedown', function(data) {
if (self._scrollingBar) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
return;
}
var x = data.x - self.aleft;
var y = data.y - self.atop;
self.emit('clicked',{x:x-1,y:y-1,ev:'click'});
if (x === self.width - self.iright - 1) {
// Do not allow dragging on the scrollbar:
delete self.screen._dragging;
delete self._drag;
var perc = (y - self.itop) / (self.height - self.iheight);
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
var smd, smu;
self._scrollingBar = true;
self.onScreenEvent('mousedown', smd = function(data) {
var y = data.y - self.atop;
var perc = y / self.height;
self.setScrollPerc(perc * 100 | 0);
self.screen.render();
});
// If mouseup occurs out of the window, no mouseup event fires, and
// scrollbar will drag again on mousedown until another mouseup
// occurs.
self.onScreenEvent('mouseup', smu = function() {
self._scrollingBar = false;
self.removeScreenEvent('mousedown', smd);
self.removeScreenEvent('mouseup', smu);
});
}
});
}
}
if (options.mouse) {
this.on('wheeldown', function() {
self.scroll(self.height / 2 | 0 || 1);
self.screen.render();
});
this.on('wheelup', function() {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
});
}
if (options.keys && !options.ignoreKeys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.scroll(-1);
self.screen.render();
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.scroll(1);
self.screen.render();
return;
}
if (key.name === 'pageup') {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
return;
}
if (key.name === 'pagedown') {
self.scroll(self.height / 2 | 0 || 1);
self.screen.render();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.scroll(-(self.height / 2 | 0) || -1);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.scroll(self.height / 2 | 0 || 1);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.scroll(-self.height || -1);
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.scroll(self.height || 1);
self.screen.render();
return;
}
if (options.vi && key.name === 'g' && !key.shift) {
self.scrollTo(0);
self.screen.render();
return;
}
if (options.vi && key.name === 'g' && key.shift) {
self.scrollTo(self.getScrollHeight());
self.screen.render();
return;
}
});
}
this.on('parsed content', function() {
self._recalculateIndex();
});
self._recalculateIndex();
}
//ScrollableBox.prototype.__proto__ = Box.prototype;
inheritPrototype(ScrollableBox,Box);
ScrollableBox.prototype.type = 'scrollable-box';
/* depricated
// XXX Potentially use this in place of scrollable checks elsewhere.
ScrollableBox.prototype.__defineGetter__('reallyScrollable', function() {
if (this.shrink) return this.scrollable;
return this.getScrollHeight() > this.height;
});
*/
Object.defineProperty(ScrollableBox.prototype,'reallyScrollable',{
get: function () {
if (this.shrink) return this.scrollable;
return this.getScrollHeight() > this.height;
},
set: function (val) {
}
});
ScrollableBox.prototype._scrollBottom = function() {
if (!this.scrollable) return 0;
// We could just calculate the children, but we can
// optimize for lists by just returning the items.length.
if (this._isList) {
return this.items ? this.items.length : 0;
}
if (this.lpos && this.lpos._scrollBottom) {
return this.lpos._scrollBottom;
}
var bottom = this.children.reduce(function(current, el) {
// el.height alone does not calculate the shrunken height, we need to use
// getCoords. A shrunken box inside a scrollable element will not grow any
// larger than the scrollable element's context regardless of how much
// content is in the shrunken box, unless we do this (call getCoords
// without the scrollable calculation):
// See: $ node test/widget-shrink-fail-2.js
if (!el.detached) {
var lpos = el._getCoords(false, true);
if (lpos) {
return Math.max(current, el.rtop + (lpos.yl - lpos.yi));
}
}
return Math.max(current, el.rtop + el.height);
}, 0);
// XXX Use this? Makes .getScrollHeight() useless!
// if (bottom < this._clines.length) bottom = this._clines.length;
if (this.lpos) this.lpos._scrollBottom = bottom;
return bottom;
};
ScrollableBox.prototype.setScroll =
ScrollableBox.prototype.scrollTo = function(offset, always) {
// XXX
// At first, this appeared to account for the first new calculation of childBase:
this.scroll(0);
return this.scroll(offset - (this.childBase + this.childOffset), always);
};
ScrollableBox.prototype.getScroll = function() {
return this.childBase + this.childOffset;
};
ScrollableBox.prototype.scroll = function(offset, always) {
if (!this.scrollable) return;
if (this.detached) return;
// Handle scrolling.
var visible = this.height - this.iheight
, base = this.childBase
, d
, p
, t
, b
, max
, emax;
if (this.alwaysScroll || always) {
// Semi-workaround
this.childOffset = offset > 0
? visible - 1 + offset
: offset;
} else {
this.childOffset += offset;
}
if (this.childOffset > visible - 1) {
d = this.childOffset - (visible - 1);
this.childOffset -= d;
this.childBase += d;
} else if (this.childOffset < 0) {
d = this.childOffset;
this.childOffset += -d;
this.childBase += d;
}
if (this.childBase < 0) {
this.childBase = 0;
} else if (this.childBase > this.baseLimit) {
this.childBase = this.baseLimit;
}
// Find max "bottom" value for
// content and descendant elements.
// Scroll the content if necessary.
if (this.childBase === base) {
return this.emit('scroll');
}
// When scrolling text, we want to be able to handle SGR codes as well as line
// feeds. This allows us to take preformatted text output from other programs
// and put it in a scrollable text box.
this.parseContent();
// XXX
// max = this.getScrollHeight() - (this.height - this.iheight);
max = this._clines.length - (this.height - this.iheight);
if (max < 0) max = 0;
emax = this._scrollBottom() - (this.height - this.iheight);
if (emax < 0) emax = 0;
this.childBase = Math.min(this.childBase, Math.max(emax, max));
if (this.childBase < 0) {
this.childBase = 0;
} else if (this.childBase > this.baseLimit) {
this.childBase = this.baseLimit;
}
// Optimize scrolling with CSR + IL/DL.
p = this.lpos;
// Only really need _getCoords() if we want
// to allow nestable scrolling elements...
// or if we **really** want shrinkable
// scrolling elements.
// p = this._getCoords();
if (p && this.childBase !== base && this.screen.cleanSides(this)) {
t = p.yi + this.itop;
b = p.yl - this.ibottom - 1;
d = this.childBase - base;
if (d > 0 && d < visible) {
// scrolled down
this.screen.deleteLine(d, t, t, b);
} else if (d < 0 && -d < visible) {
// scrolled up
d = -d;
this.screen.insertLine(d, t, t, b);
}
}
return this.emit('scroll');
};
ScrollableBox.prototype.scrollBottom = function () {
// Workaround: inserting lines when scrollbar was manually set
// breaks scroll window (can't usee _scrollBottom...)
this.scrollTo(10000000);
};
ScrollableBox.prototype._recalculateIndex = function() {
var max, emax;
if (this.detached || !this.scrollable) {
return 0;
}
// XXX
// max = this.getScrollHeight() - (this.height - this.iheight);
max = this._clines.length - (this.height - this.iheight);
if (max < 0) max = 0;
emax = this._scrollBottom() - (this.height - this.iheight);
if (emax < 0) emax = 0;
this.childBase = Math.min(this.childBase, Math.max(emax, max));
if (this.childBase < 0) {
this.childBase = 0;
} else if (this.childBase > this.baseLimit) {
this.childBase = this.baseLimit;
}
};
ScrollableBox.prototype.resetScroll = function() {
if (!this.scrollable) return;
this.childOffset = 0;
this.childBase = 0;
return this.emit('scroll');
};
ScrollableBox.prototype.getScrollHeight = function() {
return Math.max(this._clines.length, this._scrollBottom());
};
ScrollableBox.prototype.getScrollPerc = function(s) {
var pos = this.lpos || this._getCoords();
if (!pos) return s ? -1 : 0;
var height = (pos.yl - pos.yi) - this.iheight
, i = this.getScrollHeight()
, p;
if (height < i) {
if (this.alwaysScroll) {
p = this.childBase / (i - height);
} else {
p = (this.childBase + this.childOffset) / (i - 1);
}
return p * 100;
}
return s ? -1 : 0;
};
ScrollableBox.prototype.setScrollPerc = function(i) {
// XXX
// var m = this.getScrollHeight();
var m = Math.max(this._clines.length, this._scrollBottom());
return this.scrollTo((i / 100) * m | 0);
};
/**
* Expose
*/
module.exports = ScrollableBox;
};
BundleModuleCode['term/widgets/chat']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2018, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2017-2021)
** $REVESIO: 1.5.7
**
** $INFO:
**
** chat.js - interactive chat terminal shell based on textarea element for blessed
**
** events: 'eval' (high level passing command line after enter key was hit)
**
** public methods:
**
** print(string)
** ask(ask,options,callback)
**
** typeof options = { choices? : string [], mutual?: boolean,
** range? : [number,number],
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var unicode = Require('term/unicode');
var nextTick = global.setImmediate || process.nextTick.bind(process);
var Node = Require('term/widgets/node');
var Input = Require('term/widgets/input');
function setCharAt(str,index,chr) {
if(index > str.length-1) return str;
return str.substring(0,index) + chr + str.substring(index+1);
}
/**
* Chat
*/
function Chat(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Chat(options);
}
options = options || {};
options.scrollable = options.scrollable !== false;
Input.call(this, options);
this.screen._listenKeys(this);
this.value = options.value || '';
// cursor position
this.cpos = {x:-1,y:-1};
this.cursorControl=true;
this.multiline=options.multiline;
this.tags=true; // we need formatting tag support
this.input='text';
this.history=[];
this.historyTop=0;
this.__updateCursor = this._updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
if (options.inputOnFocus) {
this.on('focus', this.readInput.bind(this, null));
}
if (!options.inputOnFocus && options.keys) {
this.on('keypress', function(ch, key) {
if (self._reading) return;
if (key.name === 'enter' || (options.vi && key.name === 'i')) {
return self.readInput();
}
if (key.name === 'e') {
return self.readEditor();
}
});
}
if (options.mouse) {
this.on('click', function(data) {
if (self._reading) return;
if (data.button !== 'right') return;
self.readEditor();
});
}
var offsetY = 0;
if (this._clines) offsetY=this._clines.length-(this.childBase||0);
if (options.prompt) {
this.value=options.prompt;
this.prompt=options.prompt;
this.inputRange={x0:options.prompt.length,y0:offsetY,y1:offsetY,last:0,line:0};
} else {
this.inputRange={x0:0,y0:offsetY,y1:offsetY,last:0,line:0};
this.prompt='';
}
}
//Chat.prototype.__proto__ = Input.prototype;
inheritPrototype(Chat,Input);
Chat.prototype.type = 'terminal';
Chat.prototype._updateCursor = function(get) {
var offsetY=this.childBase||0;
if (this.screen.focused !== this) {
// at least update cursor to its bound
this.cpos.y=Math.min(this._clines.length - 1 - offsetY,this.cpos.y);
return;
}
var lpos = get ? this.lpos : this._getCoords();
if (!lpos) return;
var last = this._clines[this._clines.length - 1]
, program = this.screen.program
, line
, offsetY = this.childBase||0
, cx
, cy;
// Stop a situation where the textarea begins scrolling
// and the last cline appears to always be empty from the
// _typeScroll `+ '\n'` thing.
// Maybe not necessary anymore?
if (last === '' && this.value[this.value.length - 1] !== '\n') {
last = this._clines[this._clines.length - 2] || '';
}
line = Math.min(
this._clines.length - 1 - (this.childBase || 0),
(lpos.yl - lpos.yi) - this.iheight - 1);
// When calling clearValue() on a full textarea with a border, the first
// argument in the above Math.min call ends up being -2. Make sure we stay
// positive.
line = Math.max(0, line);
if (this.cpos.x==-1 || !this.cursorControl) this.cpos.x = this.strWidth(last);
if (this.cpos.y==-1 || !this.cursorControl) this.cpos.y = line;
this.cpos.y = Math.min(this.cpos.y,line);
this.cpos.x = Math.min(this.cpos.x,this.strWidth(this._clines[offsetY+this.cpos.y]));
cx = lpos.xi + this.ileft + this.cpos.x;
cy = lpos.yi + this.itop + this.cpos.y;
// XXX Not sure, but this may still sometimes
// cause problems when leaving editor.
if (cy === program.y && cx === program.x) {
return;
}
if (cy === program.y) {
if (cx > program.x) {
program.cuf(cx - program.x);
} else if (cx < program.x) {
program.cub(program.x - cx);
}
} else if (cx === program.x) {
if (cy > program.y) {
program.cud(cy - program.y);
} else if (cy < program.y) {
program.cuu(program.y - cy);
}
} else {
program.cup(cy, cx);
}
};
Chat.prototype.input =
Chat.prototype.setInput =
Chat.prototype.readInput = function(callback) {
var self = this
, focused = this.screen.focused === this;
if (this._reading) return;
this._reading = true;
this._callback = callback;
if (!focused) {
this.screen.saveFocus();
this.focus();
}
this.screen.grabKeys = true;
this._updateCursor();
this.screen.program.showCursor();
//this.screen.program.sgr('normal');
this._done = function fn(err, value) {
if (!self._reading) return;
if (fn.done) return;
fn.done = true;
self._reading = false;
delete self._callback;
delete self._done;
self.removeListener('keypress', self.__listener);
delete self.__listener;
self.removeListener('blur', self.__done);
delete self.__done;
self.screen.program.hideCursor();
self.screen.grabKeys = false;
if (!focused) {
self.screen.restoreFocus();
}
if (self.options.inputOnFocus) {
self.screen.rewindFocus();
}
// Ugly
if (err === 'stop') return;
if (err) {
self.emit('error', err);
} else if (value != null) {
self.emit('submit', value);
} else {
self.emit('cancel', value);
}
self.emit('action', value);
if (!callback) return;
return err
? callback(err)
: callback(null, value);
};
// Put this in a nextTick so the current
// key event doesn't trigger any keys input.
nextTick(function() {
if (self.__listener) {
// double fired?
return;
}
self.__listener = self._listener.bind(self);
self.on('keypress', self.__listener);
});
this.__done = this._done.bind(this, null, null);
this.on('blur', this.__done);
};
// Ask request
Chat.prototype.ask = function (message,options,callback) {
var offsetY = this.childBase||0,
prompt = this.prompt,
self=this;
options=options||{};
if (this.request) {
// pending request; queue new request
return;
}
// restore point
var restorePos = offsetY+this.cpos.y;
function ask(command,restore) {
offsetY = self.childBase||0;
var start = self.getLinearPos(self.value,offsetY+self.inputRange.y0,0),
end = self.getLinearPos(self.value,offsetY+self.inputRange.y1,
self._clines[offsetY+self.inputRange.y1].length);
var line;
if (restore) start=self.getLinearPos(self.value,restorePos,0);
self.value=self.value.slice(0,start)+self.prompt+command+self.value.slice(end);
self.screen.render();
self.scrollBottom();
self.cpos.x=self._clines[self._clines.length-1].length;
self.cpos.y += 10; // bottom
self._updateCursor(true);
self.inputRange.y1=self.cpos.y;
offsetY = self.childBase||0;
// find start y
var y0=self.cpos.y;
// Log(self.cpos,offsetY,y0);
self.inputRange.x0=self.prompt.length;
self.inputRange.x1=null;
while (self._clines[offsetY+y0].indexOf(self.prompt)!=0) y0--;
self.inputRange.y0=y0;
self.inputRange.last=self.inputRange.y1-self.inputRange.y0;
}
this.print('{bold}'+message+'{/bold}');
// choices
if (options.choices) {
// form can be multiline
var choices = options.choices.slice();
// Make buttons (with click handler)
this.prompt='? ';
var delim = options.layout=='vertical'?'\n':' ',
indent = options.layout=='vertical'?' ':'';
if (!options.mutable) {
line=choices.map(function (s,i) { return (i>0?indent:'')+'[ ] '+s}).join(delim);
line += (delim+indent+'[Ok] [Cancel]');
choices.push(restore1); choices.push(restore1);
} else {
line=choices.map(function (s,i) { return (i>0?indent:'')+'['+s+']'}).join(delim);
line += (delim+indent+'[Cancel]');
choices.push(restore1);
}
ask(line);
this.input='mouse';
this.screen.program.hideCursor(true);
// get selector positions, install clicked handler
var y=this.inputRange.y0,x=this.inputRange.x0;
line=this._clines[offsetY+y];
var actions = choices.map(function (choice,index) {
// find [ ]/[...] in _clines
while (line[x] && line[x] != '[') {
x++;
if (!line[x] && y<self.inputRange.y1) {
x=0;
y++;
line=self._clines[offsetY+y];
}
}
if (line[x] && line[x+1]==' ') {
x++;
return { choice:choice, index:index, fake: self.getLinearPos(self.value, offsetY+y,x), pos:{x:x,y:y}};
} else {
x++;
var start = { x:x,y:y };
// find end
while (line[x] && line[x] != ']') {
x++;
if (!line[x] && y<self.inputRange.y1) {
x=0;
y++;
line=self._clines[offsetY+y];
}
}
var end = { x:x-1,y:y }
return typeof choice=='string'?{ choice:choice, index:index, pos:[start,end]}:
{ action:choice, index:index, pos:[start,end]};
}
});
// Log(actions)
function clicked1(pos) {
var offsetY=self.childBase||0;
function within (o) {
if (typeof o.x != 'undefined') {
return pos.x==o.x && pos.y==o.y
} else if (o.length==2) {
if (o[0].y==o[1].y && o[0].y==pos.y)
return pos.x>=o[0].x&&pos.x<=o[1].x;
else if (pos.y>=o[0].y && pos.y<=o[1].y) // multi-line
return pos.x>=o[0].x&&pos.x<=o[1].x
}
}
actions.forEach(function (action) {
if (!action) return;
if (within(action.pos)) {
// fired
if (action.choice) {
if (self.selected.indexOf(action.choice)!=-1) {
// deselect
self.selected=self.selected.filter(function (choice) { return choice!=action.choice });
self.value=setCharAt(self.value,action.fake,' ');
self.screen.render();
} else {
// select
if (!options.mutable) {
self.value=setCharAt(self.value,action.fake,'X');
self.screen.render();
changed1();
}
self.selected.push(action.choice);
if (options.mutable) restore1();
}
}
if (action.action) {
action.action();
}
}
})
};
function changed1() {
if (options.timeout) {
if (self.timer) clearTimeout(self.timer);
self.timer=setTimeout(restore1,options.timeout);
}
}
function restore1 (timeout) {
var result = callback?callback(self.selected):null;
if (self.selected != undefined && self.selected.length)
self.print(self.selected.join(', '),self.style.user);
if (result) self.print(result);
// restore text line
self.prompt=prompt;
ask('',!result && self.selected.length==0);
self.input='text';
self.removeListener('clicked',clicked1);
if (!timeout && self.timer) clearTimeout(self.timer);
self.timer=null;
self.request=null;
}
this.on('clicked',clicked1);
this.selected=[];
if (options.timeout) {
this.timer=setTimeout(restore1,options.timeout);
}
}
// range //
if (options.range) {
// assumption: entire form fits in one ine
var sign=options.range[0]<0,
step=options.step||1,
digits = Math.floor(Math.log10(options.range[1]-options.range[0]+1))+1;
if (sign) digits++;
var value=options.default||options.value||options.range[0];
line = '[-] '+Comp.printf.sprintf("%"+digits+"d",value)+' [+] [Ok] [Cancel]';
this.prompt='? ';
ask(line);
var x0=this._clines[offsetY+this.cpos.y].indexOf('[-] ')+4;
var x1=this._clines[offsetY+this.cpos.y].indexOf(' [+]')-1;
this.cpos.x=x1;
this.inputRange.x0=x0;
this.inputRange.x1=x1;
this.inputRange.right=true;
this.inputRange.action=function () {
var _value = Number(self._clines[offsetY+self.cpos.y].slice(x0,x1+1));
if (_value>= options.range[0] && _value<= options.range[1]) {
self.selected=value;
restore2();
} else {
// invalid; try again
value=options.range[0];
line = '[-] '+Comp.printf.sprintf("%"+digits+"d",value)+' [+] [Ok] [Cancel]';
ask(line);
self.inputRange.x0=x0;
self.inputRange.x1=x1;
self.cpos.x=x1;
self._updateCursor();
}
}
this._updateCursor();
actions=[
{pos:{x:this._clines[offsetY+this.cpos.y].indexOf('[-]')+1,y:this.cpos.y},action:function () {
// decrease value
value=Math.max(options.range[0],value-step);
line = '[-] '+Comp.printf.sprintf("%"+digits+"d",value)+' [+] [Ok] [Cancel]';
ask(line);
self.inputRange.x0=x0;
self.inputRange.x1=x1;
self.cpos.x=x1;
self._updateCursor();
}},
{pos:{x:this._clines[offsetY+this.cpos.y].indexOf('[+]')+1,y:this.cpos.y},action:function () {
// increase value
value=Math.min(options.range[1],value+step);
line = '[-] '+Comp.printf.sprintf("%"+digits+"d",value)+' [+] [Ok] [Cancel]';
ask(line);
self.inputRange.x0=x0;
self.inputRange.x1=x1;
self.cpos.x=x1;
self._updateCursor();
}},
{pos:[{x:this._clines[offsetY+this.cpos.y].indexOf('[Ok]')+1,y:this.cpos.y},
{x:this._clines[offsetY+this.cpos.y].indexOf('[Ok]')+2,y:this.cpos.y}],action:function () {
// Ok
var _value = Number(self._clines[offsetY+self.cpos.y].slice(x0,x1+1));
if (_value>= options.range[0] && _value<= options.range[1]) {
self.selected=value;
restore2();
} else {
// invalid; try again
value=options.range[0];
line = '[-] '+Comp.printf.sprintf("%"+digits+"d",value)+' [+] [Ok] [Cancel]';
ask(line);
self.inputRange.x0=x0;
self.inputRange.x1=x1;
self.cpos.x=x1;
self._updateCursor();
}
}},
{pos:[{x:this._clines[offsetY+this.cpos.y].indexOf('[Cancel]')+1,y:this.cpos.y},
{x:this._clines[offsetY+this.cpos.y].indexOf('[Cancel]')+6,y:this.cpos.y}],action:function () {
// Cancel
restore2()
}}
]
function clicked2(pos) {
var offsetY=self.childBase||0;
function within (o) {
if (typeof o.x != 'undefined') {
return pos.x==o.x && pos.y==o.y
} else if (o.length==2) {
if (o[0].y==o[1].y && o[0].y==pos.y)
return pos.x>=o[0].x&&pos.x<=o[1].x;
else if (pos.y>=o[0].y && pos.y<=o[1].y) // multi-line
return pos.x>=o[0].x&&pos.x<=o[1].x
}
}
actions.forEach(function (action) {
if (!action) return;
if (within(action.pos)) action.action ();
})
}
function changed2() {
var _value = Number(self._clines[offsetY+self.cpos.y].slice(x0,x1+1));
if (isNaN(_value) || _value < options.range[0] || _value > options.range[1]) {
} else value=_value;
if (options.timeout) {
if (self.timer) clearTimeout(self.timer);
self.timer=setTimeout(restore2,options.timeout);
}
}
function restore2 (timeout) {
var result = callback?callback(self.selected):null;
if (self.selected != undefined) self.print(self.selected,self.style.user);
if (result) self.print(result);
// restore text line
self.prompt=prompt;
ask('',!result && self.selected == undefined);
self.removeListener('clicked',clicked2);
self.removeListener('modified',changed2);
if (!timeout && self.timer) clearTimeout(self.timer);
self.timer=null;
self.inputRange.action=null;
self.inputRange.right=null;
self.request=null;
}
// this.on('clicked',clicked);
this.selected=null;
this.on('clicked',clicked2);
this.on('modified',changed2);
if (options.timeout) {
this.timer=setTimeout(restore2,options.timeout);
}
}
if (options.text) {
this.selected=null;
function changed3() {
var offsetY=self.childBase||0,
vpos = self.getLinearPos(self.value,offsetY+self.inputRange.y0,self.inputRange.x0);
self.selected= value = self.value.slice(vpos,1000000);
if (options.timeout) {
if (self.timer) clearTimeout(self.timer);
self.timer=setTimeout(restore3,options.timeout);
}
}
function restore3 (timeout) {
var result = callback?callback(self.selected):null;
if (self.selected != undefined) self.print(self.selected,self.style.user);
if (result) self.print(result);
// restore text line
self.prompt=prompt;
// Log(self.inputRange,self.value)
ask('',!result && self.selected == undefined);
self.removeListener('modified',changed3);
if (!timeout && self.timer) clearTimeout(self.timer);
self.timer=null;
self.inputRange.action=null;
self.request=null;
}
this.inputRange.action=function () {
restore3()
}
this.on('modified',changed3);
if (options.timeout) {
this.timer=setTimeout(restore3,options.timeout);
}
}
this.request={message:message,options:options,callback:callback};
}
// Public API: Bot message
Chat.prototype.message = function (line,style) {
return this.print(line,style||this.style.bot);
}
// Print ONE line (call mutiple times for multi-line text). Auto wrapping is supprted, though.
Chat.prototype.print = function (line,style) {
// Log(this.inputRange,this._clines.length);
var offsetY = this.childBase||0,
cn1 = this._clines.length, y0=this.inputRange.y0,
start = this.getLinearPos(this.value,offsetY+this.inputRange.y0,0),
end = this.getLinearPos(this.value,offsetY+this.inputRange.y1,
this._clines[offsetY+this.inputRange.y1].length);
//Log(this.cpos,this.inputRange,this.value)
if (style) {
if (style.color) line = '{'+style.color+'-fg}'+line+'{/'+style.color+'-fg}';
if (style.align=='right') line = '{right}'+line+'{/right}';
}
var command = this.value.slice(start,end);
this.value=this.value.slice(0,start)+line+'\n'+command+this.value.slice(end);
this.screen.render();
// Update inputRange
var cn2= this._clines.length;
this.scrollBottom();
this.cpos.y += 10;
this.cpos.x=this.inputRange.x0=this.prompt.length;
this._updateCursor(true);
this.inputRange.y0=Math.min(this._clines.length-1,this.cpos.y-this.inputRange.last);
this.inputRange.y1=Math.min(this._clines.length-1,this.cpos.y);
// Log(this.cpos,this.inputRange,this.value)
}
Chat.prototype._listener = function(ch, key) {
// Cursor position must be synced with scrollablebox and vice versa (if scrollable)! A real mess.
var done = this._done
, self = this
, value = this.value
, clinesLength=this._clines.length
, offsetY = this.childBase||0 // scrollable line offset if any
, newline = false
, lastchar = false
, backspace = false
, controlkey = false
, lastline = (this.cpos.y+offsetY+1) == clinesLength;
// Log(key)
if (key.name === 'return') return;
if (key.name === 'delete') {
if (this.inputRange.x1 != undefined) {
// ranged edit
vpos=this.getLinearPos(this.value,offsetY+this.cpos.y, this.cpos.x);
// shift right x0..x1 text, delete current char at this position
var vpos0 = vpos-(this.cpos.x-this.inputRange.x0);
this.value = this.value.substr(0,vpos0)+' '+
this.value.substr(vpos0,vpos-vpos0)+
this.value.substr(vpos+1,1000000);
this.screen.render();
}
return;
}
if (key.name === 'enter') {
// Log(this._clines)
if (this.inputRange.action) return this.inputRange.action();
// clear input line; execute command; create new input line
var start = this.getLinearPos(this.value,offsetY+this.inputRange.y0,0),
end = this.getLinearPos(this.value,offsetY+this.inputRange.y1,
this._clines[offsetY+this.inputRange.y1].length);
// Log(this.inputRange,start,end,this.value,this._clines[0])
var command = this.value.slice(start+this.prompt.length,end);
if (command && command != this.history[this.historyTop-1]) {
this.history.push(command);
this.historyTop=this.history.length;
}
this.value=this.value.slice(0,start)+this.prompt+this.value.slice(end);
this.screen.render();
offsetY = this.childBase||0;
self.cpos.y += 10; // bottom
this._updateCursor(true);
this.cpos.x=self._clines[offsetY+self.cpos.y].length;
this.inputRange.y0=this.inputRange.y1=this.cpos.y; this.inputRange.last=0;
this.print(command,this.style.user);
this.emit('eval',command);
return;
}
if (this.input!='text') return;
function history(delta) {
if (self.historyTop+delta<0 ) return self.scrollBottom();
self.historyTop += delta;
var start = self.getLinearPos(self.value,offsetY+self.inputRange.y0,0),
end = self.getLinearPos(self.value,offsetY+self.inputRange.y1,
self._clines[offsetY+self.inputRange.y1].length);
var command = self.history[self.historyTop]||'';
self.historyTop = Math.min(Math.max(0,self.historyTop),self.history.length);
self.value=self.value.slice(0,start)+self.prompt+command+self.value.slice(end);
self.screen.render();
self.scrollBottom();
self.cpos.x=self._clines[self._clines.length-1].length;
self.cpos.y += 10; // bottom
self._updateCursor(true);
self.inputRange.y1=self.cpos.y;
offsetY = self.childBase||0;
// find start y
var y0=self.cpos.y; while (self._clines[offsetY+y0].indexOf(self.prompt)!=0) y0--;
self.inputRange.y0=y0;
self.inputRange.last=self.inputRange.y1-self.inputRange.y0;
}
// Handle cursor positiong by keys.
if (this.cursorControl) switch (key.name) {
case 'left':
controlkey=true;
if (this.cpos.y==this.inputRange.y0 && this.cpos.x>this.inputRange.x0) this.cpos.x--;
else if (this.cpos.y!=this.inputRange.y0 && this.cpos.x>0) this.cpos.x--;
else if (this.cpos.y!=this.inputRange.y0 && this.cpos.x==0) {
this.cpos.y--;
this.cpos.x=this._clines[offsetY+this.cpos.y].length;
}
this._updateCursor(true);
break;
case 'right':
controlkey=true;
if (this.inputRange.x1 != undefined && this.cpos.x>=this.inputRange.x1) return;
if (this.cpos.y>=this.inputRange.y0 && this.cpos.y<=this.inputRange.y1 &&
this.cpos.x<this._clines[offsetY+this.cpos.y].length-1) {
this.cpos.x++;
} else if (this.cpos.y>=this.inputRange.y0 && this.cpos.y<this.inputRange.y1 &&
this.cpos.x==this._clines[offsetY+this.cpos.y].length-1) {
this.cpos.x=0;
this.cpos.y++;
} else if (this.cpos.y>=this.inputRange.y0 && this.cpos.y==this.inputRange.y1 &&
this.cpos.x<this._clines[offsetY+this.cpos.y].length) {
this.cpos.x++;
}
this._updateCursor(true);
break;
case 'up':
controlkey=true;
history(-1);
return;
break;
case 'down':
controlkey=true;
history(1);
return;
break;
}
if (this.options.keys && key.ctrl && key.name === 'e') {
return this.readEditor();
}
// Challenge: sync with line wrapping and adjust cursor and scrolling (done in element._wrapContent)
// TODO: Optimize typing by writing directly
// to the screen and screen buffer here.
if (key.name === 'escape') {
done(null, null);
} else if (key.name === 'backspace') {
backspace=true;
if (this.cpos.y==this.inputRange.y0 && this.cpos.x<=this.inputRange.x0) return;
if (this.inputRange.x1 != undefined) {
// ranged edit
vpos=this.getLinearPos(this.value,offsetY+this.cpos.y, this.cpos.x);
// shift right x0..x1 text, delete current char at this position
var vpos0 = vpos-(this.cpos.x-this.inputRange.x0);
this.value = this.value.substr(0,vpos0)+' '+
this.value.substr(vpos0,vpos-vpos0)+
this.value.substr(vpos+1,1000000);
this.screen.render();
return;
}
if (this.value.length) {
if (this.screen.fullUnicode) {
if (unicode.isSurrogate(this.value, this.value.length - 2)) {
// || unicode.isCombining(this.value, this.value.length - 1)) {
this.value = this.value.slice(0, -2);
} else {
this.value = this.value.slice(0, -1);
}
} else {
if (!this.cursorControl ||
this.cpos.x==-1 ||
(this.cpos.x==this._clines[offsetY+this.cpos.y].length &&
this.cpos.y==this._clines.length-1-offsetY)) {
// Delete last char of last line
this.value = this.value.slice(0, -1);
} else {
// Delete char at current cursor position
vpos=this.getLinearPos(this.value,offsetY+this.cpos.y, this.cpos.x);
// vpos+= this.cpos.x;
this.value = this.value.substr(0,vpos-1)+
this.value.substr(vpos,1000000);
}
}
if (this.cpos.x>0) this.cpos.x--;
else {this.cpos.x=-1; if (offsetY==0 && this.cpos.y>0 && lastline) this.cpos.y--; };
}
} else if (!controlkey && ch && this.inputRange.x1==undefined) {
if (!/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(ch)) {
if (!this.cursorControl ||
this.cpos.x==-1 ||
(this.cpos.x==this._clines[offsetY+this.cpos.y].length &&
this.cpos.y==this._clines.length-1-offsetY)) {
// Append new char at end of (last) line
lastchar=true;
this.value += ch;
} else {
// Insert new char into line at current cursor position
vpos=this.getLinearPos(this.value,offsetY+this.cpos.y, this.cpos.x);
// vpos+= this.cpos.x;
this.value = this.value.substr(0,vpos)+ch+
this.value.substr(vpos,1000000);
}
if (newline) {
this.cpos.x=0; // first left position is zero!
this.cpos.y++;
} else
this.cpos.x++;
}
} else if (!controlkey && ch && this.inputRange.x1!=undefined) {
// Range Edit (e.g., numerical form field)
//////////////
// shift or overwrite mode in limited char range (e.g., range selector)
if (!/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(ch)) {
vpos=this.getLinearPos(this.value,offsetY+this.cpos.y, this.cpos.x);
if (this.cpos.x!=this.inputRange.x1) {
// Replace new char into line at current cursor position
// vpos+= this.cpos.x;
this.value = setCharAt(this.value,vpos,ch);
} else {
// shift left x0..x1 text, insert new char at right position
var vpos0 = vpos-(this.inputRange.x1-this.inputRange.x0);
this.value = this.value.substr(0,vpos0)+
this.value.substr(vpos0+1,vpos-vpos0)+ch+
this.value.substr(vpos+1,1000000);
}
if (this.cpos.x < this.inputRange.x1) this.cpos.x++;
}
}
var rmline=this.cpos.x==-1;
// TODO: clean up this mess; use rtof and ftor attributes of _clines
// to determine where we are (react correctly on line wrap extension and reduction)
if (this.value !== value) {
var cn0=clinesLength,
endofline=this.cpos.x==this._clines[offsetY+this.cpos.y].length+1,
cn1=this._clines.length;
var linelength1=this._clines[offsetY+this.cpos.y] && this._clines[offsetY+this.cpos.y].length;
this.screen.render();
var linelength2=this._clines[offsetY+this.cpos.y] && this._clines[offsetY+this.cpos.y].length;
var cn2=this._clines.length;
// Log(this.cpos,linelength1,linelength2,cn0,cn2,this.inputRange,lastline,lastchar,endofline);
// Log('L',this.cpos,lastline,lastchar,endofline,linelength1,linelength2);
if (cn2>cn0 && endofline) {
this.scrollBottom();
// wrap expansion
this.cpos.y++;
this.inputRange.last++;
if (this._clines[offsetY+this.cpos.y] && lastchar) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
else this.cpos.x=linelength1-linelength2-1;
this._updateCursor(true);
this.inputRange.y0=this.cpos.y-this.inputRange.last;
this.inputRange.y1=this.cpos.y;
} else if (cn2<cn0 && !rmline) {
// wrap reduction
if (this.cpos.y>0 && !lastline && lastchar) this.cpos.y--;
this.inputRange.last--;
if (this._clines[offsetY+this.cpos.y]) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
this._updateCursor(true);
this.inputRange.y0=this.cpos.y-this.inputRange.last;
this.inputRange.y1=this.cpos.y;
offsetY = this.childBase||0;
this.cpos.x=this._clines[offsetY+this.cpos.y].length;
this._updateCursor(true);
} else if (linelength2<linelength1 && !backspace) {
// wrap shift
this.cpos.y++;
this.cpos.x=linelength1-linelength2;
this._updateCursor(true);
}
if (offsetY>0 && backspace) {
// @fix line deleted; refresh again due to miscalculation of height in scrollablebox!
this.scroll(0);
this.screen.render();
if (rmline) {
if (this._clines[offsetY+this.cpos.y]) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
else if (this._clines[offsetY+this.cpos.y-1]) this.cpos.x=this._clines[offsetY+this.cpos.y-1].length;
this._updateCursor(true);
}
}
this.emit('modified');
}
};
// Return start position of nth (c)line in linear value string
Chat.prototype.getLinearPos = function(v,clineIndex,cposx) {
var lines=v.split('\n'),
line=this._clines[clineIndex], // assuming search in plain text line (no ctrl/spaces aligns)
flinenum=this._clines.rtof[clineIndex],
vpos=flinenum>0?
lines.slice(0,flinenum)
.map(function (line) { return line.length+1 })
.reduce(function (a,b) { return a+b }):0;
// console.log(clineIndex,lines.length,flinenum,lines[flinenum],line);
// TODO: search from a start position in line estimated by _clines.ftor array
function search(part,line) {
// assuming search offset in plain text line (no ctrl/spaces aligns)
var i=line.indexOf(part);
if (i==-1) return 0;
else return i;
}
if (lines[flinenum]) {
return vpos+search(line,lines[flinenum])+cposx;
} else
return vpos+cposx;
}
Chat.prototype._typeScroll = function() {
// XXX Workaround
var height = this.height - this.iheight;
// Scroll down?
// if (typeof Log != 'undefined') Log(this.childBase,this.childOffset,this.cpos.y,height);
//if (this._clines.length - this.childBase > height) {
if (this.cpos.y == height) {
this.scroll(this._clines.length);
}
};
Chat.prototype.getValue = function() {
return this.value;
};
Chat.prototype.setValue = function(value) {
if (value == null) {
value = this.value;
}
if (this._value !== value) {
this.value = value;
this._value = value;
this.setContent(this.value);
this._typeScroll();
this._updateCursor();
}
};
Chat.prototype.clearInput =
Chat.prototype.clearValue = function() {
return this.setValue('');
};
Chat.prototype.submit = function() {
if (!this.__listener) return;
return this.__listener('\x1b', { name: 'escape' });
};
Chat.prototype.cancel = function() {
if (!this.__listener) return;
return this.__listener('\x1b', { name: 'escape' });
};
Chat.prototype.render = function() {
this.setValue();
return this._render();
};
Chat.prototype.editor =
Chat.prototype.setEditor =
Chat.prototype.readEditor = function(callback) {
var self = this;
if (this._reading) {
var _cb = this._callback
, cb = callback;
this._done('stop');
callback = function(err, value) {
if (_cb) _cb(err, value);
if (cb) cb(err, value);
};
}
if (!callback) {
callback = function() {};
}
return this.screen.readEditor({ value: this.value }, function(err, value) {
if (err) {
if (err.message === 'Unsuccessful.') {
self.screen.render();
return self.readInput(callback);
}
self.screen.render();
self.readInput(callback);
return callback(err);
}
self.setValue(value);
self.screen.render();
return self.readInput(callback);
});
};
/**
* Expose
*/
module.exports = Chat;
};
BundleModuleCode['term/widgets/text']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2016-2017)
** $REVESIO: 1.2.1
**
** $INFO:
**
** text.js - text element for blessed
**
** Usage:
var obj = blessed.text({
width: options.width||(options.content.length),
left: (options.center?int(screen.width/2-options.content.length/2):options.left),
right : options.right,
top: options.top||0,
height: 3,
focus:false,
align: 'center',
content: options.content||'?',
style: {
bold:true
}
});
screen.append(obj);
obj.setContent('New text');
** $ENDOFINFO
*/
var Comp = Require('com/compat');
/**
* Modules
*/
var Node = Require('term/widgets/node');
var Element = Require('term/widgets/element');
/**
* Text
*/
function Text(options) {
if (!instanceOf(this, Node)) {
return new Text(options);
}
options = options || {};
options.shrink = true;
Element.call(this, options);
}
//Text.prototype.__proto__ = Element.prototype;
inheritPrototype(Text,Element);
Text.prototype.type = 'text';
/**
* Expose
*/
module.exports = Text;
};
BundleModuleCode['term/widgets/line']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.1
**
** $INFO:
**
** line.js - line element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* Line
*/
function Line(options) {
if (!instanceOf(this,Node)) {
return new Line(options);
}
options = options || {};
var orientation = options.orientation || 'vertical';
delete options.orientation;
if (orientation === 'vertical') {
options.width = 1;
} else {
options.height = 1;
}
Box.call(this, options);
this.ch = !options.type || options.type === 'line'
? orientation === 'horizontal' ? '─' : '│'
: options.ch || ' ';
this.border = {
type: 'bg',
__proto__: this
};
this.style.border = this.style;
}
//Line.prototype.__proto__ = Box.prototype;
inheritPrototype(Line,Box);
Line.prototype.type = 'line';
/**
* Expose
*/
module.exports = Line;
};
BundleModuleCode['term/widgets/bigtext']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.1
**
** $INFO:
**
** bigtext.js - bigtext element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var fs = require('fs');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* BigText
*/
function BigText(options) {
if (!instanceOf(this instanceof Node)) {
return new BigText(options);
}
options = options || {};
options.font = options.font
|| __dirname + '/../../usr/fonts/ter-u14n.json';
options.fontBold = options.font
|| __dirname + '/../../usr/fonts/ter-u14b.json';
this.fch = options.fch;
this.ratio = {};
this.font = this.loadFont(options.font);
this.fontBold = this.loadFont(options.font);
Box.call(this, options);
if (this.style.bold) {
this.font = this.fontBold;
}
}
//BigText.prototype.__proto__ = Box.prototype;
inheritPrototype(BigText,Box);
BigText.prototype.type = 'bigtext';
BigText.prototype.loadFont = function(filename) {
var self = this
, data
, font;
data = JSON.parse(fs.readFileSync(filename, 'utf8'));
this.ratio.width = data.width;
this.ratio.height = data.height;
function convertLetter(ch, lines) {
var line, i;
while (lines.length > self.ratio.height) {
lines.shift();
lines.pop();
}
lines = lines.map(function(line) {
var chs = line.split('');
chs = chs.map(function(ch) {
return ch === ' ' ? 0 : 1;
});
while (chs.length < self.ratio.width) {
chs.push(0);
}
return chs;
});
while (lines.length < self.ratio.height) {
line = [];
for (i = 0; i < self.ratio.width; i++) {
line.push(0);
}
lines.push(line);
}
return lines;
}
font = Object.keys(data.glyphs).reduce(function(out, ch) {
var lines = data.glyphs[ch].map;
out[ch] = convertLetter(ch, lines);
return out;
}, {});
delete font[' '];
return font;
};
BigText.prototype.setContent = function(content) {
this.content = '';
this.text = content || '';
};
BigText.prototype.render = function() {
if (this.position.width == null || this._shrinkWidth) {
// if (this.width - this.iwidth < this.ratio.width * this.text.length + 1) {
this.position.width = this.ratio.width * this.text.length + 1;
this._shrinkWidth = true;
// }
}
if (this.position.height == null || this._shrinkHeight) {
// if (this.height - this.iheight < this.ratio.height + 0) {
this.position.height = this.ratio.height + 0;
this._shrinkHeight = true;
// }
}
var coords = this._render();
if (!coords) return;
var lines = this.screen.lines
, left = coords.xi + this.ileft
, top = coords.yi + this.itop
, right = coords.xl - this.iright
, bottom = coords.yl - this.ibottom;
var dattr = this.sattr(this.style)
, bg = dattr & 0x1ff
, fg = (dattr >> 9) & 0x1ff
, flags = (dattr >> 18) & 0x1ff
, attr = (flags << 18) | (bg << 9) | fg;
for (var x = left, i = 0; x < right; x += this.ratio.width, i++) {
var ch = this.text[i];
if (!ch) break;
var map = this.font[ch];
if (!map) continue;
for (var y = top; y < Math.min(bottom, top + this.ratio.height); y++) {
if (!lines[y]) continue;
var mline = map[y - top];
if (!mline) continue;
for (var mx = 0; mx < this.ratio.width; mx++) {
var mcell = mline[mx];
if (mcell == null) break;
if (this.fch && this.fch !== ' ') {
lines[y][x + mx][0] = dattr;
lines[y][x + mx][1] = mcell === 1 ? this.fch : this.ch;
} else {
lines[y][x + mx][0] = mcell === 1 ? attr : dattr;
lines[y][x + mx][1] = mcell === 1 ? ' ' : this.ch;
}
}
lines[y].dirty = true;
}
}
return coords;
};
/**
* Expose
*/
module.exports = BigText;
};
BundleModuleCode['term/widgets/list']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR.
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2017-2018)
** $REVESIO: 1.2.5
**
** $INFO:
**
** list.js - list element for blessed
**
** Added:
** - 'arrows', arrow buttons
**
** Options: {selectlast,selectoffset,label, border, style, arrows?}
**
** selectlast:boolean (try to) select always last selected item after modification
** selectoffset:number additional (positive, downto) scroll shift on selection
** (otherwise selected line jumps to bottom of window)
**
** Events In: click, keypress, element wheeldown, element wheelup, resize, adopt, remove
** Events Out: action(item,selected), select(item,selected), selected(item)
** Item content text: item.content
**
**
** Usage:
** Create list: list=new List({..});
** Update list: list.setItems([content1:string,content2:string,..]);
** Get selected item content (label): list.getSelected().getContent();
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Io = Require('com/io');
var helpers = Require('term/helpers');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var Button = Require('term/widgets/button');
var Arrows = Require('term/widgets/arrows');
/**
* List
*/
function List(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new List(options);
}
options = options || {};
options.ignoreKeys = true;
// Possibly put this here: this.items = [];
options.scrollable = true;
Box.call(this, options);
this.value = '';
this.items = [];
this.ritems = [];
this.selected = 0;
this._isList = true;
if (!this.style.selected) {
this.style.selected = {};
this.style.selected.bg = options.selectedBg;
this.style.selected.fg = options.selectedFg;
this.style.selected.bold = options.selectedBold;
this.style.selected.underline = options.selectedUnderline;
this.style.selected.blink = options.selectedBlink;
this.style.selected.inverse = options.selectedInverse;
this.style.selected.invisible = options.selectedInvisible;
}
if (!this.style.item) {
this.style.item = {};
this.style.item.bg = options.itemBg;
this.style.item.fg = options.itemFg;
this.style.item.bold = options.itemBold;
this.style.item.underline = options.itemUnderline;
this.style.item.blink = options.itemBlink;
this.style.item.inverse = options.itemInverse;
this.style.item.invisible = options.itemInvisible;
}
// Legacy: for apps written before the addition of item attributes.
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
if (self.style[name] != null && self.style.item[name] == null) {
self.style.item[name] = self.style[name];
}
});
if (this.options.itemHoverBg) {
this.options.itemHoverEffects = { bg: this.options.itemHoverBg };
}
if (this.options.itemHoverEffects) {
this.style.item.hover = this.options.itemHoverEffects;
}
if (this.options.itemFocusEffects) {
this.style.item.focus = this.options.itemFocusEffects;
}
this.interactive = options.interactive !== false;
this.mouse = options.mouse || false;
if (options.items) {
this.ritems = options.items;
options.items.forEach(this.add.bind(this));
}
this.select(0);
if (options.mouse) {
this.screen._listenMouse(this);
this.on('element wheeldown', function() {
self.select(self.selected + 2);
self.screen.render();
});
this.on('element wheelup', function() {
self.select(self.selected - 2);
self.screen.render();
});
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'up' || (options.vi && key.name === 'k')) {
self.up();
self.screen.render();
self.emit('selected', self.items[self.selected]);
return;
}
if (key.name === 'down' || (options.vi && key.name === 'j')) {
self.down();
self.screen.render();
self.emit('selected', self.items[self.selected]);
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'l' && !key.shift)) {
self.enterSelected();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.cancelSelected();
return;
}
if (options.vi && key.name === 'u' && key.ctrl) {
self.move(-((self.height - self.iheight) / 2) | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'd' && key.ctrl) {
self.move((self.height - self.iheight) / 2 | 0);
self.screen.render();
return;
}
if (options.vi && key.name === 'b' && key.ctrl) {
self.move(-(self.height - self.iheight));
self.screen.render();
return;
}
if (options.vi && key.name === 'f' && key.ctrl) {
self.move(self.height - self.iheight);
self.screen.render();
return;
}
if (options.vi && key.name === 'h' && key.shift) {
self.move(self.childBase - self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'm' && key.shift) {
// TODO: Maybe use Math.min(this.items.length,
// ... for calculating visible items elsewhere.
var visible = Math.min(
self.height - self.iheight,
self.items.length) / 2 | 0;
self.move(self.childBase + visible - self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'l' && key.shift) {
// XXX This goes one too far on lists with an odd number of items.
self.down(self.childBase
+ Math.min(self.height - self.iheight, self.items.length)
- self.selected);
self.screen.render();
return;
}
if (options.vi && key.name === 'g' && !key.shift) {
self.select(0);
self.screen.render();
return;
}
if (options.vi && key.name === 'g' && key.shift) {
self.select(self.items.length - 1);
self.screen.render();
return;
}
if (options.vi && (key.ch === '/' || key.ch === '?')) {
if (typeof self.options.search !== 'function') {
return;
}
return self.options.search(function(err, value) {
if (typeof err === 'string' || typeof err === 'function'
|| typeof err === 'number' || (err && err.test)) {
value = err;
err = null;
}
if (err || !value) return self.screen.render();
self.select(self.fuzzyFind(value, key.ch === '?'));
self.screen.render();
});
}
});
}
this.on('resize', function() {
var visible = self.height - self.iheight;
// if (self.selected < visible - 1) {
if (visible >= self.selected + 1) {
self.childBase = 0;
self.childOffset = self.selected;
} else {
// Is this supposed to be: self.childBase = visible - self.selected + 1; ?
self.childBase = self.selected - visible + 1;
self.childOffset = visible - 1;
}
});
this.on('adopt', function(el) {
if (!~self.items.indexOf(el)) {
el.fixed = true;
}
});
// Ensure children are removed from the
// item list if they are items.
this.on('remove', function(el) {
self.removeItem(el);
});
if (options.arrows)
Arrows(
self,
options,
function () { self.select(self.selected - 2); self.screen.render()},
function () { self.select(self.selected + 2); self.screen.render()}
);
}
//List.prototype.__proto__ = Box.prototype;
inheritPrototype(List,Box);
List.prototype.type = 'list';
List.prototype.createItem = function(content) {
var self = this;
// Note: Could potentially use Button here.
var options = {
screen: this.screen,
content: content,
align: this.align || 'left',
top: 0,
left: 0,
right: (this.scrollbar ? 1 : 0),
tags: this.parseTags,
height: 1,
hoverEffects: this.mouse ? this.style.item.hover : null,
focusEffects: this.mouse ? this.style.item.focus : null,
autoFocus: false
};
if (!this.screen.autoPadding) {
options.top = 1;
options.left = this.ileft;
options.right = this.iright + (this.scrollbar ? 1 : 0);
}
// if (this.shrink) {
// XXX NOTE: Maybe just do this on all shrinkage once autoPadding is default?
if (this.shrink && this.options.normalShrink) {
delete options.right;
options.width = 'shrink';
}
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
options[name] = function() {
var attr = self.items[self.selected] === item && self.interactive
? self.style.selected[name]
: self.style.item[name];
if (typeof attr === 'function') attr = attr(item);
return attr;
};
});
if (this.style.transparent) {
options.transparent = true;
}
var item = new Box(options);
if (this.mouse) {
item.on('click', function() {
self.focus();
if (self.items[self.selected] === item) {
self.emit('action', item, self.selected);
self.emit('select', item, self.selected);
return;
}
self.select(item);
self.emit('selected', self.items[self.selected]);
self.screen.render();
});
}
this.emit('create item');
return item;
};
List.prototype.add =
List.prototype.addItem =
List.prototype.appendItem = function(content) {
content = typeof content === 'string' ? content : content.getContent();
var item = this.createItem(content);
item.position.top = this.items.length;
if (!this.screen.autoPadding) {
item.position.top = this.itop + this.items.length;
}
this.ritems.push(content);
this.items.push(item);
this.append(item);
if (this.items.length === 1) {
this.select(0);
}
this.emit('add item');
return item;
};
List.prototype.removeItem = function(child) {
var i = this.getItemIndex(child);
if (~i && this.items[i]) {
child = this.items.splice(i, 1)[0];
this.ritems.splice(i, 1);
this.remove(child);
for (var j = i; j < this.items.length; j++) {
this.items[j].position.top--;
}
if (i === this.selected) {
this.select(i - 1);
}
}
this.emit('remove item');
return child;
};
List.prototype.insertItem = function(child, content) {
content = typeof content === 'string' ? content : content.getContent();
var i = this.getItemIndex(child);
if (!~i) return;
if (i >= this.items.length) return this.appendItem(content);
var item = this.createItem(content);
for (var j = i; j < this.items.length; j++) {
this.items[j].position.top++;
}
item.position.top = i + (!this.screen.autoPadding ? 1 : 0);
this.ritems.splice(i, 0, content);
this.items.splice(i, 0, item);
this.append(item);
if (i === this.selected) {
this.select(i + 1);
}
this.emit('insert item');
};
List.prototype.getItem = function(child) {
return this.items[this.getItemIndex(child)];
};
List.prototype.setItem = function(child, content) {
content = typeof content === 'string' ? content : content.getContent();
var i = this.getItemIndex(child);
if (!~i) return;
this.items[i].setContent(content);
this.ritems[i] = content;
};
List.prototype.clearItems = function() {
return this.setItems([]);
};
List.prototype.setItems = function(items) {
var original = this.items.slice()
, selected = this.selected
, sel = this.ritems[this.selected]
, i = 0;
items = items.slice();
this.select(0);
for (; i < items.length; i++) {
if (this.items[i]) {
this.items[i].setContent(items[i]);
} else {
this.add(items[i]);
}
}
for (; i < original.length; i++) {
this.remove(original[i]);
}
this.ritems = items;
// Try to find our old item if it still exists.
// But how to deal with ambiquous string items? indexOf can point to wrong item!?
sel = items.indexOf(sel);
if (this.options.selectlast) this.select(selected);
/*
if (~sel) {
this.select(sel);
} else */ if (items.length === original.length) {
this.select(selected);
} else {
this.select(Math.min(selected, items.length - 1));
}
this.emit('set items');
};
List.prototype.pushItem = function(content) {
this.appendItem(content);
return this.items.length;
};
List.prototype.popItem = function() {
return this.removeItem(this.items.length - 1);
};
List.prototype.unshiftItem = function(content) {
this.insertItem(0, content);
return this.items.length;
};
List.prototype.shiftItem = function() {
return this.removeItem(0);
};
List.prototype.spliceItem = function(child, n) {
var self = this;
var i = this.getItemIndex(child);
if (!~i) return;
var items = Array.prototype.slice.call(arguments, 2);
var removed = [];
while (n--) {
removed.push(this.removeItem(i));
}
items.forEach(function(item) {
self.insertItem(i++, item);
});
return removed;
};
List.prototype.find =
List.prototype.fuzzyFind = function(search, back) {
var start = this.selected + (back ? -1 : 1)
, i;
if (typeof search === 'number') search += '';
if (search && search[0] === '/' && search[search.length - 1] === '/') {
try {
search = new RegExp(search.slice(1, -1));
} catch (e) {
;
}
}
var test = typeof search === 'string'
? function(item) { return !!~item.indexOf(search); }
: (search.test ? search.test.bind(search) : search);
if (typeof test !== 'function') {
if (this.screen.options.debug) {
throw new Error('fuzzyFind(): `test` is not a function.');
}
return this.selected;
}
if (!back) {
for (i = start; i < this.ritems.length; i++) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
for (i = 0; i < start; i++) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
} else {
for (i = start; i >= 0; i--) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
for (i = this.ritems.length - 1; i > start; i--) {
if (test(helpers.cleanTags(this.ritems[i]))) return i;
}
}
return this.selected;
};
List.prototype.getItemIndex = function(child) {
if (typeof child === 'number') {
return child;
} else if (typeof child === 'string') {
var i = this.ritems.indexOf(child);
if (~i) return i;
for (i = 0; i < this.ritems.length; i++) {
if (helpers.cleanTags(this.ritems[i]) === child) {
return i;
}
}
return -1;
} else {
return this.items.indexOf(child);
}
};
List.prototype.getSelected = function() {
return this.items[this.selected];
}
List.prototype.select = function(index) {
var lastindex=this.selected;
if (!this.interactive) {
return;
}
if (!this.items.length) {
this.selected = 0;
this.value = '';
this.scrollTo(0);
return;
}
if (typeof index === 'object') {
index = this.items.indexOf(index);
}
if (index < 0) {
index = 0;
} else if (index >= this.items.length) {
index = this.items.length - 1;
}
if (this.selected === index && this._listInitialized) return;
this._listInitialized = true;
this.selected = index;
this.value = helpers.cleanTags(this.ritems[this.selected]);
if (!this.parent) return;
if (index>=lastindex)
this.scrollTo(this.selected+(this.options.selectoffset||0));
else
this.scrollTo(this.selected);
// XXX Move `action` and `select` events here.
this.emit('select item', this.items[this.selected], this.selected);
};
List.prototype.move = function(offset) {
this.select(this.selected + offset);
};
List.prototype.up = function(offset) {
this.move(-(offset || 1));
};
List.prototype.down = function(offset) {
this.move(offset || 1);
};
List.prototype.pick = function(label, callback) {
if (!callback) {
callback = label;
label = null;
}
if (!this.interactive) {
return callback();
}
var self = this;
var focused = this.screen.focused;
if (focused && focused._done) focused._done('stop');
this.screen.saveFocus();
// XXX Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
this.focus();
this.show();
this.select(0);
if (label) this.setLabel(label);
this.screen.render();
this.once('action', function(el, selected) {
if (label) self.removeLabel();
self.screen.restoreFocus();
self.hide();
self.screen.render();
if (!el) return callback();
return callback(null, helpers.cleanTags(self.ritems[selected]));
});
};
List.prototype.enterSelected = function(i) {
if (i != null) this.select(i);
this.emit('action', this.items[this.selected], this.selected);
this.emit('select', this.items[this.selected], this.selected);
};
List.prototype.cancelSelected = function(i) {
if (i != null) this.select(i);
this.emit('action');
this.emit('cancel');
};
/**
* Expose
*/
module.exports = List;
};
BundleModuleCode['term/widgets/form']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.1
**
** $INFO:
**
** form.js - form element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* Form
*/
function Form(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Form(options);
}
options = options || {};
options.ignoreKeys = true;
Box.call(this, options);
if (options.keys) {
this.screen._listenKeys(this);
this.on('element keypress', function(el, ch, key) {
if ((key.name === 'tab' && !key.shift)
|| (el.type === 'textbox' && options.autoNext && key.name === 'enter')
|| key.name === 'down'
|| (options.vi && key.name === 'j')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'j') return;
if (key.name === 'tab') {
// Workaround, since we can't stop the tab from being added.
el.emit('keypress', null, { name: 'backspace' });
}
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusNext();
return;
}
if ((key.name === 'tab' && key.shift)
|| key.name === 'up'
|| (options.vi && key.name === 'k')) {
if (el.type === 'textbox' || el.type === 'textarea') {
if (key.name === 'k') return;
el.emit('keypress', '\x1b', { name: 'escape' });
}
self.focusPrevious();
return;
}
if (key.name === 'escape') {
self.focus();
return;
}
});
}
}
//Form.prototype.__proto__ = Box.prototype;
inheritPrototype(Form,Box),
Form.prototype.type = 'form';
Form.prototype._refresh = function() {
// XXX Possibly remove this if statement and refresh on every focus.
// Also potentially only include *visible* focusable elements.
// This would remove the need to check for _selected.visible in previous()
// and next().
if (!this._children) {
var out = [];
this.children.forEach(function fn(el) {
if (el.keyable) out.push(el);
el.children.forEach(fn);
});
this._children = out;
}
};
Form.prototype._visible = function() {
return !!this._children.filter(function(el) {
return el.visible;
}).length;
};
Form.prototype.next = function() {
this._refresh();
if (!this._visible()) return;
if (!this._selected) {
this._selected = this._children[0];
if (!this._selected.visible) return this.next();
if (this.screen.focused !== this._selected) return this._selected;
}
var i = this._children.indexOf(this._selected);
if (!~i || !this._children[i + 1]) {
this._selected = this._children[0];
if (!this._selected.visible) return this.next();
return this._selected;
}
this._selected = this._children[i + 1];
if (!this._selected.visible) return this.next();
return this._selected;
};
Form.prototype.previous = function() {
this._refresh();
if (!this._visible()) return;
if (!this._selected) {
this._selected = this._children[this._children.length - 1];
if (!this._selected.visible) return this.previous();
if (this.screen.focused !== this._selected) return this._selected;
}
var i = this._children.indexOf(this._selected);
if (!~i || !this._children[i - 1]) {
this._selected = this._children[this._children.length - 1];
if (!this._selected.visible) return this.previous();
return this._selected;
}
this._selected = this._children[i - 1];
if (!this._selected.visible) return this.previous();
return this._selected;
};
Form.prototype.focusNext = function() {
var next = this.next();
if (next) next.focus();
};
Form.prototype.focusPrevious = function() {
var previous = this.previous();
if (previous) previous.focus();
};
Form.prototype.resetSelected = function() {
this._selected = null;
};
Form.prototype.focusFirst = function() {
this.resetSelected();
this.focusNext();
};
Form.prototype.focusLast = function() {
this.resetSelected();
this.focusPrevious();
};
Form.prototype.submit = function() {
var out = {};
this.children.forEach(function fn(el) {
if (el.value != null) {
var name = el.name || el.type;
if (Array.isArray(out[name])) {
out[name].push(el.value);
} else if (out[name]) {
out[name] = [out[name], el.value];
} else {
out[name] = el.value;
}
}
el.children.forEach(fn);
});
this.emit('submit', out);
return this.submission = out;
};
Form.prototype.cancel = function() {
this.emit('cancel');
};
Form.prototype.reset = function() {
this.children.forEach(function fn(el) {
switch (el.type) {
case 'screen':
break;
case 'box':
break;
case 'text':
break;
case 'line':
break;
case 'scrollable-box':
break;
case 'list':
el.select(0);
return;
case 'form':
break;
case 'input':
break;
case 'textbox':
el.clearInput();
return;
case 'textarea':
el.clearInput();
return;
case 'button':
delete el.value;
break;
case 'progress-bar':
el.setProgress(0);
break;
case 'file-manager':
el.refresh(el.options.cwd);
return;
case 'checkbox':
el.uncheck();
return;
case 'radio-set':
break;
case 'radio-button':
el.uncheck();
return;
case 'prompt':
break;
case 'question':
break;
case 'message':
break;
case 'info':
break;
case 'loading':
break;
case 'list-bar':
//el.select(0);
break;
case 'dir-manager':
el.refresh(el.options.cwd);
return;
case 'terminal':
el.write('');
return;
case 'image':
//el.clearImage();
return;
}
el.children.forEach(fn);
});
this.emit('reset');
};
/**
* Expose
*/
module.exports = Form;
};
BundleModuleCode['term/widgets/textarea']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2018, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2017-2021)
** $REVESIO: 1.5.2
**
** $INFO:
**
** textarea.js - textarea element for blessed
**
** new: cursor control
**
** special options: {cursorControl}
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var unicode = Require('term/unicode');
var nextTick = global.setImmediate || process.nextTick.bind(process);
var Node = Require('term/widgets/node');
var Input = Require('term/widgets/input');
/**
* Textarea
*/
function Textarea(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Textarea(options);
}
options = options || {};
options.scrollable = options.scrollable !== false;
Input.call(this, options);
this.screen._listenKeys(this);
this.value = options.value || '';
// cursor position
this.cpos = {x:-1,y:-1};
this.cursorControl=true;
this.multiline=options.multiline;
this.__updateCursor = this._updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
if (options.inputOnFocus) {
this.on('focus', this.readInput.bind(this, null));
}
if (!options.inputOnFocus && options.keys) {
this.on('keypress', function(ch, key) {
if (self._reading) return;
if (key.name === 'enter' || (options.vi && key.name === 'i')) {
return self.readInput();
}
if (key.name === 'e') {
return self.readEditor();
}
});
}
if (options.mouse) {
this.on('click', function(data) {
if (self._reading) return;
if (data.button !== 'right') return;
self.readEditor();
});
}
}
//Textarea.prototype.__proto__ = Input.prototype;
inheritPrototype(Textarea,Input);
Textarea.prototype.type = 'textarea';
Textarea.prototype._updateCursor = function(get) {
if (this.screen.focused !== this) {
return;
}
var lpos = get ? this.lpos : this._getCoords();
if (!lpos) return;
var last = this._clines[this._clines.length - 1]
, program = this.screen.program
, line
, offsetY = this.childBase||0
, cx
, cy;
// Stop a situation where the textarea begins scrolling
// and the last cline appears to always be empty from the
// _typeScroll `+ '\n'` thing.
// Maybe not necessary anymore?
if (last === '' && this.value[this.value.length - 1] !== '\n') {
last = this._clines[this._clines.length - 2] || '';
}
line = Math.min(
this._clines.length - 1 - (this.childBase || 0),
(lpos.yl - lpos.yi) - this.iheight - 1);
// When calling clearValue() on a full textarea with a border, the first
// argument in the above Math.min call ends up being -2. Make sure we stay
// positive.
line = Math.max(0, line);
if (this.cpos.x==-1 || !this.cursorControl) this.cpos.x = this.strWidth(last);
if (this.cpos.y==-1 || !this.cursorControl) this.cpos.y = line;
this.cpos.y = Math.min(this.cpos.y,line);
this.cpos.x = Math.min(this.cpos.x,this.strWidth(this._clines[offsetY+this.cpos.y]));
cx = lpos.xi + this.ileft + this.cpos.x;
cy = lpos.yi + this.itop + this.cpos.y;
// XXX Not sure, but this may still sometimes
// cause problems when leaving editor.
if (cy === program.y && cx === program.x) {
return;
}
if (cy === program.y) {
if (cx > program.x) {
program.cuf(cx - program.x);
} else if (cx < program.x) {
program.cub(program.x - cx);
}
} else if (cx === program.x) {
if (cy > program.y) {
program.cud(cy - program.y);
} else if (cy < program.y) {
program.cuu(program.y - cy);
}
} else {
program.cup(cy, cx);
}
};
Textarea.prototype.input =
Textarea.prototype.setInput =
Textarea.prototype.readInput = function(callback) {
var self = this
, focused = this.screen.focused === this;
if (this._reading) return;
this._reading = true;
this._callback = callback;
if (!focused) {
this.screen.saveFocus();
this.focus();
}
this.screen.grabKeys = true;
this._updateCursor();
this.screen.program.showCursor();
//this.screen.program.sgr('normal');
this._done = function fn(err, value) {
if (!self._reading) return;
if (fn.done) return;
fn.done = true;
delete self._callback;
delete self._done;
self.removeListener('keypress', self.__listener);
delete self.__listener;
self.removeListener('blur', self.__done);
delete self.__done;
self.screen.program.hideCursor();
self.screen.grabKeys = false;
if (!focused) {
self.screen.restoreFocus();
}
if (self.options.inputOnFocus) {
self.screen.rewindFocus();
}
self._reading = false;
// Ugly
if (err === 'stop') return;
if (err) {
self.emit('error', err);
} else if (value != null) {
self.emit('submit', value);
} else {
self.emit('cancel', value);
}
self.emit('action', value);
if (!callback) return;
return err
? callback(err)
: callback(null, value);
};
// Put this in a nextTick so the current
// key event doesn't trigger any keys input.
nextTick(function() {
if (self.__listener) {
// double fired?
return;
}
self.__listener = self._listener.bind(self);
self.on('keypress', self.__listener);
});
this.__done = this._done.bind(this, null, null);
this.on('blur', this.__done);
};
Textarea.prototype._listener = function(ch, key) {
// Cursor position must be synced with scrollablebox and vice versa (if scrollable)! A real mess.
var done = this._done
, self = this
, value = this.value
, clinesLength=this._clines.length
, offsetY = this.childBase||0 // scrollable line offset if any
, newline = false
, backspace = false
, lastline = (this.cpos.y+offsetY+1) == clinesLength;
if (key.name == 'linefeed' ||
(!this.multiline && key.name=='enter')) return this.emit('enter',this.value);
if (key.name === 'return') return;
if (key.name === 'enter') {
ch = '\n';
// this.cpos.x=1;
// this.cpos.y++;
newline=true;
}
// Handle cursor positiong by keys.
if (this.cursorControl) switch (key.name) {
case 'left':
if (this.cpos.x>0) this.cpos.x--;
else {
if (this.cpos.y>0) {
this.cpos.y--;
this.cpos.x=this._clines[offsetY+this.cpos.y].length;
} else if (offsetY>0) {
if (this.scrollable) this.scroll(-1);
self.screen.render();
this.cpos.x=this._clines[offsetY+this.cpos.y-1].length;
}
}
this._updateCursor(true);
break;
case 'right':
var next=++this.cpos.x;
this._updateCursor(true);
if (this.cpos.x!=next && (offsetY+this.cpos.y+1)<this._clines.length) {
next=++this.cpos.y;
this.cpos.x=0;
this._updateCursor(true);
if (this.scrollable && this.cpos.y!=next) this.scroll(1);
}
break;
case 'up':
if (this.cpos.y>0) {
this.cpos.y--;
//this.cpos.x=this.strWidth(this._clines[this.cpos.y]);
}
this._updateCursor(true);
break;
case 'down':
this.cpos.y++;
this._updateCursor(true);
break;
}
if (this.options.keys && key.ctrl && key.name === 'e') {
return this.readEditor();
}
// Challenge: sync with line wrapping and adjust cursor and scrolling (done in element._wrapContent)
// TODO: Optimize typing by writing directly
// to the screen and screen buffer here.
if (key.name === 'escape') {
done(null, null);
} else if (key.name === 'backspace') {
backspace=true;
if (this.value.length) {
if (this.screen.fullUnicode) {
if (unicode.isSurrogate(this.value, this.value.length - 2)) {
// || unicode.isCombining(this.value, this.value.length - 1)) {
this.value = this.value.slice(0, -2);
} else {
this.value = this.value.slice(0, -1);
}
} else {
if (!this.cursorControl ||
this.cpos.x==-1 ||
(this.cpos.x==this._clines[offsetY+this.cpos.y].length &&
this.cpos.y==this._clines.length-1-offsetY)) {
// Delete last char of last line
this.value = this.value.slice(0, -1);
} else {
// Delete char at current cursor position
vpos=this.getLinearPos(this.value,offsetY+this.cpos.y, this.cpos.x);
// vpos+= this.cpos.x;
this.value = this.value.substr(0,vpos-1)+
this.value.substr(vpos,1000000);
}
}
if (this.cpos.x>0) this.cpos.x--;
else {this.cpos.x=-1; if (offsetY==0 && this.cpos.y>0 && lastline) this.cpos.y--; };
}
} else if (ch) {
if (!/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(ch)) {
if (!this.cursorControl ||
this.cpos.x==-1 ||
(this.cpos.x==this._clines[offsetY+this.cpos.y].length &&
this.cpos.y==this._clines.length-1-offsetY))
// Append new char at end of (last) line
this.value += ch;
else {
// Insert new char into line at current cursor position
vpos=this.getLinearPos(this.value,offsetY+this.cpos.y, this.cpos.x);
// vpos+= this.cpos.x;
this.value = this.value.substr(0,vpos)+ch+
this.value.substr(vpos,1000000);
}
if (newline) {
this.cpos.x=0; // first left position is zero!
this.cpos.y++;
} else
this.cpos.x++;
}
}
var rmline=this.cpos.x==-1;
// if (this.childOffset!=undefined) this.childOffset=this.cpos.y;
// TODO: clean up this mess; use rtof and ftor attributes of _clines
// to determine where we are (react correctly on line wrap extension and reduction)
if (this.value !== value) {
var cn0=clinesLength,
cn1=this._clines.length,
y0=this.cpos.y,
linelength=this._clines[offsetY+this.cpos.y] && this._clines[offsetY+this.cpos.y].length,
endofline=this.cpos.x==linelength+1;
// Log(this.cpos,this.childBase);
this.screen.render();
var cn2=this._clines.length;
// Log(this.cpos,lastline,endofline,rmline,newline,backspace,cn0,cn2,this.childBase);
if (!newline && endofline && cn2>cn0) {
// wrap expansion
if (this.scrollable && lastline) this.scrollBottom();
this.cpos.y++;
this._updateCursor(true);
if (this._clines[offsetY+this.cpos.y]) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
this._updateCursor(true);
} else if (cn2<cn0 && !rmline) {
// wrap reduction
if (this.cpos.y>0 && !lastline && endofline) this.cpos.y--;
this._updateCursor(true);
offsetY=this.childBase||0;
if (this._clines[offsetY+this.cpos.y]) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
this._updateCursor(true);
} else if (cn2<cn0 && !rmline && this.cpos.x==0) {
// wrap reduction
if (this._clines[offsetY+this.cpos.y]) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
this._updateCursor(true);
};
if (offsetY>0 && backspace) {
// @fix line deleted; refresh again due to miscalculation of height in scrollablebox!
if (this.scrollable) this.scroll(0);
this.screen.render();
if (rmline && cn0!=cn2) {
if (this._clines[offsetY+this.cpos.y]) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
else if (this._clines[offsetY+this.cpos.y-1]) this.cpos.x=this._clines[offsetY+this.cpos.y-1].length;
this._updateCursor(true);
}
}
}
};
// Return start position of nth (c)line in linear value string
Textarea.prototype.getLinearPos = function(v,clineIndex,cposx) {
// clineIndex is the index in the _clines array, cposx the cursor position in this line!
var vpos=0,len=v.length,cline,clinePos=0,clineNum=0;
cline=this._clines[clineNum];
// To support auto line wrapping the clines have to be parsed, too!
while (vpos < len && clineIndex) {
if (v.charAt(vpos)=='\n') {
clinePos=-1;
clineIndex--;
clineNum++;
cline=this._clines[clineNum];
} else {
if (v.charAt(vpos) != cline.charAt(clinePos)) {
//
clinePos=0;
clineIndex--;
clineNum++;
cline=this._clines[clineNum];
continue;
}
}
vpos++; clinePos++;
}
if (clineIndex==0) return vpos+cposx;
else 0
}
Textarea.prototype._typeScroll = function() {
// XXX Workaround
var height = this.height - this.iheight;
// Scroll down?
// if (typeof Log != 'undefined') Log(this.childBase,this.childOffset,this.cpos.y,height);
//if (this._clines.length - this.childBase > height) {
if (this.cpos.y == height) {
if (this.scrollable) this.scroll(this._clines.length);
}
};
Textarea.prototype.getValue = function() {
return this.value;
};
Textarea.prototype.setValue = function(value) {
if (value == null) {
value = this.value;
}
if (this._value !== value) {
this.value = value;
this._value = value;
this.setContent(this.value);
this._typeScroll();
this._updateCursor();
}
};
Textarea.prototype.clearInput =
Textarea.prototype.clearValue = function() {
return this.setValue('');
};
Textarea.prototype.submit = function() {
if (!this.__listener) return;
return this.__listener('\x1b', { name: 'escape' });
};
Textarea.prototype.cancel = function() {
if (!this.__listener) return;
return this.__listener('\x1b', { name: 'escape' });
};
Textarea.prototype.render = function() {
this.setValue();
return this._render();
};
Textarea.prototype.editor =
Textarea.prototype.setEditor =
Textarea.prototype.readEditor = function(callback) {
var self = this;
if (this._reading) {
var _cb = this._callback
, cb = callback;
this._done('stop');
callback = function(err, value) {
if (_cb) _cb(err, value);
if (cb) cb(err, value);
};
}
if (!callback) {
callback = function() {};
}
return this.screen.readEditor({ value: this.value }, function(err, value) {
if (err) {
if (err.message === 'Unsuccessful.') {
self.screen.render();
return self.readInput(callback);
}
self.screen.render();
self.readInput(callback);
return callback(err);
}
self.setValue(value);
self.screen.render();
return self.readInput(callback);
});
};
/**
* Expose
*/
module.exports = Textarea;
};
BundleModuleCode['term/widgets/textbox']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2018, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.5.2
**
** $INFO:
**
** textbox.js - textbox element for blessed
**
** Special options: {wrap,multiline,screct,censor}
**
** Usage:
1. Editable
var obj = blessed.textbox({
label: options.label||'My Input',
value: options.value||'default value',
fg: 'blue',
bg: 'default',
barBg: 'default',
barFg: 'blue',
width: options.width||(self.screen.width-(options.left*2||2)),
height: 3,
left: options.left,
right : options.right,
top: options.top||0,
keys: true,
vi: true,
mouse: true,
inputOnFocus: true,
focus:true,
//draggable:true,
border: {
type: 'line'
},
style: {
fg:'blue',
focus : {
border: {
fg: 'red'
}
}
}
});
2. Non editable
var obj = blessed.textbox({
top: options.top||1,
left: options.left||1,
width: options.width,
height: options.height||3,
label: options.label,
focus:false,
//draggable:true,
border: {
type: 'line'
},
style: {
fg:'blue'
}
});
screen.append(obj);
obj.setValue('Some Text')
**
**
** New options: wrap:boolean
**
** $ENDOFINFO
*/
var options = {
version:'1.5.2'
}
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Textarea = Require('term/widgets/textarea');
/**
* Textbox
*/
function Textbox(options) {
if (!instanceOf(this,Node)) {
return new Textbox(options);
}
options = options || {};
options.scrollable = options.scrollable||false;
Textarea.call(this, options);
// Special options
this.secret = options.secret;
this.censor = options.censor;
this.wrap = options.wrap;
this.multiline = options.multiline;
}
//Textbox.prototype.__proto__ = Textarea.prototype;
inheritPrototype(Textbox,Textarea);
Textbox.prototype.type = 'textbox';
Textbox.prototype.__olistener = Textbox.prototype._listener;
Textbox.prototype._listener = function(ch, key) {
if (!this.multiline && key.name === 'enter') {
this.emit('enter',this.value);
this._done(null, this.value);
return;
}
return this.__olistener(ch, key);
};
Textbox.prototype.setValue = function(value) {
var visible, val, i, line, sep;
if (value == null) {
value = this.value;
}
if (this._value !== value) {
if (!this.multiline) {
value = value.replace(/\n/g, '');
}
this.value = value;
this._value = value;
if (this.secret) {
this.setContent('');
} else if (this.censor) {
this.setContent(Array(this.value.length + 1).join('*'));
} else if (this.wrap && this.value.length > (this.width - this.iwidth - 1)) {
line='';i=0, sep='';
visible=this.width - this.iwidth - 1;
val=this.value;
while (val.length>0) {
line = line + val.substr(0,visible);
val = val.substr(visible,val.length-visible);
sep='\n';
}
this.setContent(line);
} else if (this.multiline) {
val = this.value.replace(/\t/g, this.screen.tabc);
this.setContent(val);
} else {
visible = -(this.width - this.iwidth - 1);
val = this.value.replace(/\t/g, this.screen.tabc);
this.setContent(val.slice(visible));
}
this._updateCursor();
}
};
// setValue + update screen IM
Textbox.prototype.update = function(value) {
this.setValue(value);
this.screen.render();
}
Textbox.prototype.submit = function() {
if (!this.__listener) return;
return this.__listener('\r', { name: 'enter' });
};
/**
* Expose
*/
module.exports = Textbox;
};
BundleModuleCode['term/widgets/progressbar']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.1
**
** $INFO:
**
** progressbar.js - progress bar element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Input = Require('term/widgets/input');
/**
* ProgressBar
*/
function ProgressBar(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new ProgressBar(options);
}
options = options || {};
Input.call(this, options);
this.filled = options.filled || 0;
if (typeof this.filled === 'string') {
this.filled = +this.filled.slice(0, -1);
}
this.value = this.filled;
this.pch = options.pch || ' ';
// XXX Workaround that predates the usage of `el.ch`.
if (options.ch) {
this.pch = options.ch;
this.ch = ' ';
}
if (options.bch) {
this.ch = options.bch;
}
if (!this.style.bar) {
this.style.bar = {};
this.style.bar.fg = options.barFg;
this.style.bar.bg = options.barBg;
}
this.orientation = options.orientation || 'horizontal';
if (options.keys) {
this.on('keypress', function(ch, key) {
var back, forward;
if (self.orientation === 'horizontal') {
back = ['left', 'h'];
forward = ['right', 'l'];
} else if (self.orientation === 'vertical') {
back = ['down', 'j'];
forward = ['up', 'k'];
}
if (key.name === back[0] || (options.vi && key.name === back[1])) {
self.progress(-5);
self.screen.render();
return;
}
if (key.name === forward[0] || (options.vi && key.name === forward[1])) {
self.progress(5);
self.screen.render();
return;
}
});
}
if (options.mouse) {
this.on('click', function(data) {
var x, y, m, p;
if (!self.lpos) return;
if (self.orientation === 'horizontal') {
x = data.x - self.lpos.xi;
m = (self.lpos.xl - self.lpos.xi) - self.iwidth;
p = x / m * 100 | 0;
} else if (self.orientation === 'vertical') {
y = data.y - self.lpos.yi;
m = (self.lpos.yl - self.lpos.yi) - self.iheight;
p = y / m * 100 | 0;
}
self.setProgress(p);
});
}
}
//ProgressBar.prototype.__proto__ = Input.prototype;
inheritPrototype(ProgressBar,Input);
ProgressBar.prototype.type = 'progress-bar';
ProgressBar.prototype.render = function() {
var ret = this._render();
if (!ret) return;
var xi = ret.xi
, xl = ret.xl
, yi = ret.yi
, yl = ret.yl
, dattr;
if (this.border) xi++, yi++, xl--, yl--;
if (this.orientation === 'horizontal') {
xl = xi + ((xl - xi) * (this.filled / 100)) | 0;
} else if (this.orientation === 'vertical') {
yi = yi + ((yl - yi) - (((yl - yi) * (this.filled / 100)) | 0));
}
dattr = this.sattr(this.style.bar);
this.screen.fillRegion(dattr, this.pch, xi, xl, yi, yl);
if (this.content) {
var line = this.screen.lines[yi];
for (var i = 0; i < this.content.length; i++) {
line[xi + i][1] = this.content[i];
}
line.dirty = true;
}
return ret;
};
ProgressBar.prototype.progress = function(filled) {
this.filled += filled;
if (this.filled < 0) this.filled = 0;
else if (this.filled > 100) this.filled = 100;
if (this.filled === 100) {
this.emit('complete');
}
this.value = this.filled;
};
ProgressBar.prototype.setProgress = function(filled) {
this.filled = 0;
this.progress(filled);
};
ProgressBar.prototype.reset = function() {
this.emit('reset');
this.filled = 0;
this.value = this.filled;
};
/**
* Expose
*/
module.exports = ProgressBar;
};
BundleModuleCode['term/widgets/filemanager']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2018, Christopher Jeffrey and contributors
** $VERSION: 1.4.2
**
** $INFO:
*
* filemanager.js - file manager element for blessed
*
* Events: 'ioerror','cd','file'
*
* New options: okayButton, cancelButton, autohide, select (select emits file event),
* noshow, arrows: {up:'[-]',down:'[+]',width:3,height:1,fg:'red',bg:'default'}},
* box:{bg,border}, input:{fg,bg,border}
*
** $ENDOFINFO
*/
var options = {
version:'1.4.2'
}
/**
* Modules
*/
var Comp = Require('com/compat');
var path = Require('path')
, fs = Require('fs');
var Node = Require('term/widgets/node');
var List = Require('term/widgets/list');
var Button = Require('term/widgets/button');
var Helpers = Require('term/helpers');
var Box = Require('term/widgets/box');
var TextBox = Require('term/widgets/textbox');
var Arrows = Require('term/widgets/arrows');
var Screen = Require('term/widgets/screen');
/**
* FileManager
*/
function FileManager(options) {
var self = this,
bbox,
off1=0,
off2=0,
arrows=options.arrows;
if (!instanceOf(this,Node)) {
return new FileManager(options);
}
options = options || {};
options.parseTags = true;
options.mouse = true;
options.arrows=_; // optional arrows are handled here, not in list
if (options.parent == Screen.global) {
// Screen overlay; adjust top/height settings
bbox=Helpers.bbox(Screen.global,options);
if (options.box) bbox.top += 1,bbox.height -= 2,bbox.width -= 4,bbox.left += 2;
if (options.input) bbox.height -= 3;
if (options.cancelButton||options.okayButton) bbox.height -= 1;
if (arrows) bbox.width -= 4,bbox.left += 2;
options.top=bbox.top;
options.left=bbox.left;
options.height=bbox.height;
options.width=bbox.width;
}
// options.label = ' {blue-fg}%path{/blue-fg} ';
List.call(this, options);
options.arrows=arrows;
this.cwd = options.cwd || process.cwd();
this.file = this.cwd;
this.value = this.cwd;
this.noshow = options.noshow;
if (options.parent == this.screen) {
// Collect clickable elements of this widget
this._clickable=this.screen.clickable;
this.screen.clickable=[];
// compute for button positions
bbox=Helpers.bbox(this.screen,options);
if (options.cancelButton||options.okayButton) off1=2;
if (options.input) off2=3;
if (options.box)
this._.box = new Box({
top:bbox.top-2,
width:bbox.width+8,
left:bbox.left-4,
height:bbox.height+3+off1+off2,
hidden:options.hidden,
border:options.box.border,
style:{
label:options.label,
fg:options.box.fg,
bg:options.box.bg||'white'
}
});
if (this._.box) this.screen.append(this._.box);
if (options.input) {
this._.input = new TextBox({
screen: this.screen,
top: bbox.top+bbox.height+(options.box?1:0)-1,
height: options.input.border&&options.input.border.type=='line'?3:1,
width: bbox.width,
left: bbox.left,
keys: options.input.mutable?true:undefined,
vi: options.input.mutable?true:undefined,
mouse: options.input.mutable?true:undefined,
inputOnFocus: options.input.mutable?true:undefined,
value: options.input.value||'<input>',
hidden:options.hidden,
border: options.input.border,
style: {
fg:options.input.fg||'black',
bg:options.input.bg||'white',
bold:true
}
});
this.screen.append(this._.input);
}
if (options.okayButton) {
this._.okay = new Button({
screen: this.screen,
top: bbox.top+bbox.height+(options.box?1:0)+off2,
height: 1,
left: bbox.left+1,
width: 10,
content: options.okayButton,
align: 'center',
style: {
fg:'white',
bg: 'blue',
bold:true,
},
autoFocus: false,
hidden:options.hidden,
mouse: true
});
this._.okay.on('press',function () {
var item=self.items[self.selected],
value = self._.input?
self._.input.getValue():
item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, ''),
file=path.resolve(self.cwd, value);
self.emit('file', file);
self.hide();
});
this.screen.append(this._.okay);
}
if (options.cancelButton) {
this._.cancel = new Button({
screen: this.screen,
top: bbox.top+bbox.height+(options.box?1:0)+off2,
height: 1,
left: bbox.left+bbox.width-10-1,
width: 10,
content: options.cancelButton,
align: 'center',
style: {
fg:'white',
bg: 'red',
bold:true,
},
autoFocus: false,
hidden:options.hidden,
mouse: true
});
this._.cancel.on('press',function () { self.hide(); });
this.screen.append(this._.cancel);
}
if (options.arrows)
Arrows(
self,
options,
function () {self.emit('element wheelup')},
function () {self.emit('element wheeldown')},
true
);
this._hide=this.hide;
this.hide = function() {
self._hide();
if (self._.box) self._.box.hide();
if (self._.input) self._.input.hide();
if (self._.okay) self._.okay.hide();
if (self._.cancel) self._.cancel.hide();
if (self._.up) self._.up.hide();
if (self._.down) self._.down.hide();
self.screen.render();
// restore all clickable elements
self.screen.clickable=self._clickable;
}
this._show = this.show;
this.show = function() {
// save all screen clickable elements; enable only this clickables
self._clickable=self.screen.clickable;
self.screen.clickable=self.clickable;
self._show();
if (self._.box) self._.box.show();
if (self._.input) self._.input.show();
if (self._.okay) self._.okay.show();
if (self._.cancel) self._.cancel.show();
if (self._.up) self._.up.show();
if (self._.down) self._.down.show();
self.screen.render();
}
// Save clickable elements of this widget; restore screen
this.clickable=this.screen.clickable;
this.screen.clickable=this._clickable;
}
if (options.label && ~options.label.indexOf('%path')) {
this._label.setContent(options.label.replace('%path', this.cwd));
}
if (this._.input)
this.on('selected', function(item) {
var value = item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, '');
if (value.indexOf('/') != -1) value='';
self._.input.setValue(value);
self._.input.update();
});
this.on('select', function(item) {
var value = item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, '')
, file = path.resolve(self.cwd, value);
return fs.stat(file, function(err, stat) {
var _cwd=self.cwd;
if (err) {
return self.emit('ioerror', err, file);
}
self.file = file;
self.value = file;
if (stat.isDirectory()) {
self.cwd = file;
self.refresh(undefined,function (err) {
if (err) self.cwd=_cwd;
else if (options.label && ~options.label.indexOf('%path')) {
self._label.setContent(options.label.replace('%path', self.cwd));
self.emit('cd', file, self.cwd);
self.screen.render();
}
});
} else {
if (self.options.select) self.emit('file', file);
if (self.options.select && self.options.autohide) self.hide();
}
});
});
}
//FileManager.prototype.__proto__ = List.prototype;
inheritPrototype(FileManager,List);
FileManager.prototype.type = 'file-manager';
FileManager.prototype.refresh = function(cwd, callback) {
var self = this;
if (cwd) this.cwd = cwd;
else cwd = this.cwd;
return fs.readdir(cwd, function(err, list) {
if (err && err.code === 'ENOENT') {
self.cwd = cwd !== process.env.HOME
? process.env.HOME
: '/';
return self.refresh(undefined,callback);
}
if (err) {
if (callback) return callback(err);
return self.emit('ioerror', err, cwd);
}
var dirs = []
, files = [];
list.unshift('..');
list.forEach(function(name) {
var f = path.resolve(cwd, name)
, stat;
try {
stat = fs.lstatSync(f);
} catch (e) {
;
}
if ((stat && stat.isDirectory()) || name === '..') {
dirs.push({
name: name,
text: '{light-blue-fg}' + name + '{/light-blue-fg}/',
dir: true
});
} else if (stat && stat.isSymbolicLink()) {
files.push({
name: name,
text: '{light-cyan-fg}' + name + '{/light-cyan-fg}@',
dir: false
});
} else {
files.push({
name: name,
text: name,
dir: false
});
}
});
dirs = Helpers.asort(dirs);
files = Helpers.asort(files);
list = dirs.concat(files).map(function(data) {
return data.text;
});
self.setItems(list);
self.select(0);
self.screen.render();
self.emit('refresh');
if (callback) callback();
});
};
FileManager.prototype.pick = function(cwd, callback) {
if (!callback) {
callback = cwd;
cwd = null;
}
var self = this
, focused = this.screen.focused === this
, hidden = this.hidden
, onfile
, oncancel;
function resume() {
self.removeListener('file', onfile);
self.removeListener('cancel', oncancel);
if (hidden) {
self.hide();
}
if (!focused) {
self.screen.restoreFocus();
}
self.screen.render();
}
this.on('file', onfile = function(file) {
resume();
return callback(null, file);
});
this.on('cancel', oncancel = function() {
resume();
return callback();
});
this.refresh(cwd, function(err) {
if (err) return callback(err);
if (hidden) {
self.show();
}
if (!focused) {
self.screen.saveFocus();
self.focus();
}
self.screen.render();
});
};
FileManager.prototype.reset = function(cwd, callback) {
if (!callback) {
callback = cwd;
cwd = null;
}
this.cwd = cwd || this.options.cwd;
this.refresh(callback);
};
/**
* Expose
*/
module.exports = FileManager;
};
BundleModuleCode['term/widgets/checkbox']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2017-2021)
** $REVESIO: 1.4.1
**
** $INFO:
**
** checkbox.js - checkbox element for blessed
**
** Usage:
**
** var obj = blessed.checkbox({
** checked: options.value||false,
** left: options.left,
** right : options.right,
** top: options.top||0,
** mouse: true,
** inputOnFocus: true,
** height: 1,
** text:options.text||'empty'
** });
** screen.append(obj);
** obj.on('check',function () {});
**
** Events:
** 'check' 'uncheck' 'select'
**
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Input = Require('term/widgets/input');
/**
* Checkbox
*/
function Checkbox(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Checkbox(options);
}
options = options || {};
Input.call(this, options);
this.text = options.content || options.text || '';
this.checked = this.value = options.checked || false;
this.on('keypress', function(ch, key) {
if (key.name === 'enter' || key.name === 'space') {
self.toggle();
self.screen.render();
}
});
if (options.mouse) {
this.on('click', function() {
self.toggle();
self.screen.render();
});
}
this.on('focus', function() {
var lpos = self.lpos;
if (!lpos) return;
self.screen.program.lsaveCursor('checkbox');
self.screen.program.cup(lpos.yi, lpos.xi + 1);
self.screen.program.showCursor();
});
this.on('blur', function() {
self.screen.program.lrestoreCursor('checkbox', true);
});
}
//Checkbox.prototype.__proto__ = Input.prototype;
inheritPrototype(Checkbox,Input);
Checkbox.prototype.type = 'checkbox';
Checkbox.prototype.render = function() {
this.clearPos(true);
this.setContent('[' + (this.checked ? 'x' : ' ') + '] ' + this.text, true);
return this._render();
};
Checkbox.prototype.check = function() {
if (this.checked) return;
this.checked = this.value = true;
this.emit('check',this);
this.emit('select',true);
};
Checkbox.prototype.uncheck = function() {
if (!this.checked) return;
this.checked = this.value = false;
this.emit('uncheck',this);
this.emit('select',false);
};
Checkbox.prototype.toggle = function() {
return this.checked
? this.uncheck()
: this.check();
};
/**
* Expose
*/
module.exports = Checkbox;
};
BundleModuleCode['term/widgets/radioset']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.1
**
** $INFO:
**
** radioset.js - radio set element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* RadioSet
*/
function RadioSet(options) {
if (!instanceOf(this,Node)) {
return new RadioSet(options);
}
options = options || {};
// Possibly inherit parent's style.
// options.style = this.parent.style;
Box.call(this, options);
}
//RadioSet.prototype.__proto__ = Box.prototype;
inheritPrototype(RadioSet,Box);
RadioSet.prototype.type = 'radio-set';
/**
* Expose
*/
module.exports = RadioSet;
};
BundleModuleCode['term/widgets/radiobutton']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2017-2021)
** $REVESIO: 1.3.1
**
** $INFO:
**
** radiobutton.js - radio button element for blessed
**
** Added:
** - Simplified group management (using options.group identifier instead radioset parent)
**
** Usage:
**
** var obj = blessed.radiobutton({
** checked: options.value||false,
** left: options.left,
** right : options.right,
** top: options.top||0,
** group:options.group,
** mouse: true,
** inputOnFocus: true,
** height: 1,
** text:options.text||'empty'
** });
** screen.append(obj);
** obj.on('select',function);
**
** Events:
** 'check' 'uncheck' 'select'
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Checkbox = Require('term/widgets/checkbox');
/**
* RadioButton
*/
function RadioButton(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new RadioButton(options);
}
options = options || {};
this.group=options.group;
Checkbox.call(this, options);
this.on('check', function() {
var el = self,
group=self.group;
while (el = el.parent) {
if (el.type === 'radio-set'
|| el.type === 'form') break;
}
el = el || self.parent;
var index=0;
el.forDescendants(function(el) {
if (el.type !== 'radio-button' || el === self || el.group!=group) {
return;
}
index++;
el.uncheck();
});
});
}
//RadioButton.prototype.__proto__ = Checkbox.prototype;
inheritPrototype(RadioButton,Checkbox);
RadioButton.prototype.type = 'radio-button';
RadioButton.prototype.render = function() {
this.clearPos(true);
this.setContent('(' + (this.checked ? '*' : ' ') + ') ' + this.text, true);
return this._render();
};
RadioButton.prototype.toggle = RadioButton.prototype.check;
/**
* Expose
*/
module.exports = RadioButton;
};
BundleModuleCode['term/widgets/prompt']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2016-2017)
** $REVESIO: 1.2.1
**
** $INFO:
**
** prompt.js - prompt element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var Button = Require('term/widgets/button');
var Textbox = Require('term/widgets/textbox');
/**
* Prompt
*/
function Prompt(options) {
if (!instanceOf(this, Node)) {
return new Prompt(options);
}
options = options || {};
options.hidden = true;
Box.call(this, options);
this._.input = new Textbox({
parent: this,
top: 3,
height: 1,
left: 2,
right: 2,
bg: 'black'
});
this._.okay = new Button({
parent: this,
top: 5,
height: 1,
left: 2,
width: 6,
content: 'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
this._.cancel = new Button({
parent: this,
top: 5,
height: 1,
shrink: true,
left: 10,
width: 8,
content: 'Cancel',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
}
//Prompt.prototype.__proto__ = Box.prototype;
inheritPrototype(Prompt,Box);
Prompt.prototype.type = 'prompt';
Prompt.prototype.input =
Prompt.prototype.setInput =
Prompt.prototype.readInput = function(text, value, callback) {
var self = this;
var okay, cancel;
if (!callback) {
callback = value;
value = '';
}
// Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
this.show();
this.setContent(' ' + text);
this._.input.value = value;
this.screen.saveFocus();
this._.okay.on('press', okay = function() {
self._.input.submit();
});
this._.cancel.on('press', cancel = function() {
self._.input.cancel();
});
this._.input.readInput(function(err, data) {
self.hide();
self.screen.restoreFocus();
self._.okay.removeListener('press', okay);
self._.cancel.removeListener('press', cancel);
return callback(err, data);
});
this.screen.render();
};
/**
* Expose
*/
module.exports = Prompt;
};
BundleModuleCode['term/widgets/question']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (C) 2006-2017
** $REVESIO: 1.3.5
**
** $INFO:
**
** question.js - question element for blessed (overlay)
**
** Usage:
var width=options.width,height=options.height;
if (Comp.obj.isString(options.width)) {
// relative value in %!
width=Comp.pervasives.int_of_string(options.width);
width=int(self.screen.width*width/100);
}
if (Comp.obj.isString(options.height)) {
// relative value in %!
height=Comp.pervasives.int_of_string(options.height);
height=int(self.screen.height*height/100);
}
var obj = blessed.Question({
width: width,
left: (options.center?int(self.screen.width/2-width/2):options.left),
right : options.right,
top: options.top||(options.center?int(self.screen.height/2-height/2):0),
height: height,
okButton : options.okButton||'Okay',
cancelButton : options.cancelButton||'Cancel',
style: {
bg:'red',
fg:'white',
bold:true
}
});
screen.append(obj);
...
var dia = dialog({width:'50%',height:6,center:true,
okButton : 'Okay',
cancelButton : 'Cancel'
});
dia.ask('You need to start the network service first!',function (answer) {});
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var Button = Require('term/widgets/button');
/**
* Question
*/
function Question(options) {
if (!instanceOf(this,Node)) {
return new Question(options);
}
options = options || {};
options.hidden = true;
if (!options.height || options.height<5) options.height=5;
Box.call(this, options);
// Collect clickable elements of this widget
this._clickable=this.screen.clickable;
this.screen.clickable=[];
this._.okay = new Button({
screen: this.screen,
parent: this,
top: options.height?(options.height-2):3,
height: 1,
left: 2,
width: 10,
content: options.okButton||'Okay',
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
if (options.cancelButton)
this._.cancel = new Button({
screen: this.screen,
parent: this,
top: options.height?(options.height-2):3,
height: 1,
left: options.width?(options.width-12):10,
width: 10,
content: options.cancelButton,
align: 'center',
bg: 'black',
hoverBg: 'blue',
autoFocus: false,
mouse: true
});
// Save clickable elements of this widget; restore screen
this.clickable=this.screen.clickable;
this.screen.clickable=this._clickable;
}
//Question.prototype.__proto__ = Box.prototype;
inheritPrototype(Question,Box);
Question.prototype.type = 'question';
Question.prototype.ask = function(text, callback) {
var self = this,
press, okay, cancel,
off,room;
if (!callback) callback=function () {};
// Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
// save all clickable elements; enable only this clickables
this._clickable=this.screen.clickable;
this.screen.clickable=this.clickable;
this.show();
if (text.length > this.options.width-4) {
var tokens=text.split(' '),
curlen=0,temp='';
for(var t in tokens) {
var token=tokens[t];
if (curlen+token.length+1 < this.options.width-4) {
temp = temp+token+' ';
curlen = curlen + token.length + 1;
} else {
if (token.length < this.options.width-4) {
temp = temp + '\n' + token+ ' ';
curlen = token.length + 1;
} else {
off=0,room=this.options.width-4-curlen;
temp = temp+token.substr(0,room);
off=room;
room=this.options.width-4;
while (off < token.length) {
frag = token.substr(off,room);
temp = temp + '\n' + frag;
off += room;
curlen = frag;
}
temp = temp + ' ';
curlen++;
}
}
}
text=temp;
}
this.setContent('\n ' + text.replace(/\n/g,'\n '));
this.onScreenEvent('keypress', press = function(ch, key) {
if (key.name === 'mouse') return;
if (key.name !== 'enter'
&& key.name !== 'escape'
&& key.name !== 'q'
&& key.name !== 'y'
&& key.name !== 'n') {
return;
}
done(null, key.name === 'enter' || key.name === 'y');
});
this._.okay.on('press', okay = function() {
done(null, true);
});
if (this._.cancel) this._.cancel.on('press', cancel = function() {
done(null, false);
});
this.screen.saveFocus();
this.focus();
function done(err, data) {
self.hide();
// restore all clickable elements
self.screen.clickable=self._clickable;
self.screen.restoreFocus();
self.removeScreenEvent('keypress', press);
self._.okay.removeListener('press', okay);
if (self._.cancel) self._.cancel.removeListener('press', cancel);
return callback(err, data);
}
this.screen.render();
};
/**
* Expose
*/
module.exports = Question;
};
BundleModuleCode['term/widgets/message']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2016-2017)
** $REVESIO: 1.2.1
**
** $INFO:
**
** message.js - message element for blessed
**
** $ENDOFINFO
*/
var Comp = Require('com/compat');
/**
* Modules
*/
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* Message / Error
*/
function Message(options) {
if (!instanceOf(this,Node)) {
return new Message(options);
}
options = options || {};
options.tags = true;
Box.call(this, options);
}
//Message.prototype.__proto__ = Box.prototype;
inheritPrototype(Message,Box);
Message.prototype.type = 'message';
Message.prototype.log =
Message.prototype.display = function(text, time, callback) {
var self = this;
if (typeof time === 'function') {
callback = time;
time = null;
}
if (time == null) time = 3;
// Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
if (this.scrollable) {
this.screen.saveFocus();
this.focus();
this.scrollTo(0);
}
this.show();
this.setContent(text);
this.screen.render();
if (time === Infinity || time === -1 || time === 0) {
var end = function() {
if (end.done) return;
end.done = true;
if (self.scrollable) {
try {
self.screen.restoreFocus();
} catch (e) {
;
}
}
self.hide();
self.screen.render();
if (callback) callback();
};
setTimeout(function() {
self.onScreenEvent('keypress', function fn(ch, key) {
if (key.name === 'mouse') return;
if (self.scrollable) {
if ((key.name === 'up' || (self.options.vi && key.name === 'k'))
|| (key.name === 'down' || (self.options.vi && key.name === 'j'))
|| (self.options.vi && key.name === 'u' && key.ctrl)
|| (self.options.vi && key.name === 'd' && key.ctrl)
|| (self.options.vi && key.name === 'b' && key.ctrl)
|| (self.options.vi && key.name === 'f' && key.ctrl)
|| (self.options.vi && key.name === 'g' && !key.shift)
|| (self.options.vi && key.name === 'g' && key.shift)) {
return;
}
}
if (self.options.ignoreKeys && ~self.options.ignoreKeys.indexOf(key.name)) {
return;
}
self.removeScreenEvent('keypress', fn);
end();
});
// XXX May be affected by new element.options.mouse option.
if (!self.options.mouse) return;
self.onScreenEvent('mouse', function fn(data) {
if (data.action === 'mousemove') return;
self.removeScreenEvent('mouse', fn);
end();
});
}, 10);
return;
}
setTimeout(function() {
self.hide();
self.screen.render();
if (callback) callback();
}, time * 1000);
};
Message.prototype.error = function(text, time, callback) {
return this.display('{red-fg}Error: ' + text + '{/red-fg}', time, callback);
};
/**
* Expose
*/
module.exports = Message;
};
BundleModuleCode['term/widgets/keyboard']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: sbosse (C) 2006-2018
** $REVESIO: 1.2.3
**
** $INFO:
**
** keyboard.js - software keyboard (overlay)
**
** Options:
** typeof options = {
** top,left,width,height,
** button?={width,height} is button size,
** margin?={x,y} is button margin,
** compact?:boolean,
** delButton?:string,
** nlButton?:string,
** okayButton?:string,
** cancelButton?:string,
** }
**
** $ENDOFINFO
*/
var options = {
version:'1.2.3'
}
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var Button = Require('term/widgets/button');
var TextBox = Require('term/widgets/textbox');
var Helpers = Require('term/helpers');
/**
* Keyboard
*/
function Keyboard(options) {
var self=this,
x,y,key,i=0,bbox;
if (!instanceOf(this,Node)) {
return new Keyboard(options);
}
options = options || {};
options.hidden = true;
if (!options.height || options.height<10) options.height=10;
Box.call(this, options);
// Collect clickable elements of this widget
this._clickable=this.screen.clickable;
this.screen.clickable=[];
if (!options.button) options.button={width:3,height:2};
if (!options.margin) options.margin={x:2,y:1};
this.shift=false;
this.group=0;
this._.buttons=[];
var Keys = [
[
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
'p','q','r','s','t','u','v','w','x','y','z'
],
[
'0','1','2','3','4','5','6','7','8','9','.','+','-','*',':',';'
],
[
'"','!','=','_','<','>','(',')','{','}','[',']','?','#','~',' '
]
];
var keys = Comp.array.flatten(Keys);
var complen=Keys[0].length+Keys[1].length;
// compute for button positions
bbox=Helpers.bbox(this.screen,options);
if (options.okayButton) {
this._.okay = new Button({
screen: this.screen,
parent: this,
top: bbox.height-3,
height: 1,
left: 1,
width: Math.max(6,options.okayButton.length+2),
content: options.okayButton,
align: 'center',
autoFocus: false,
mouse: true,
style: {
bold:true,
bg:'green',
fg:'white'
}
});
this._.okay.on('press',function () { self.hide(); if (self._.callback) self._.callback(self._.input.getValue())});
}
if (options.cancelButton) {
this._.cancel = new Button({
screen: this.screen,
parent: this,
top: bbox.height-3,
height: 1,
right: 1,
width: options.cancelButton.length+2,
content: options.cancelButton,
align: 'center',
autoFocus: false,
mouse: true,
style: {
bold:true,
bg:'red',
fg:'white'
}
});
this._.cancel.on('press',function () { self.hide(); });
}
this._.shift = new Button({
screen: this.screen,
parent: this,
top: bbox.height-3,
height: 1,
right: int(bbox.width/2)+(options.compact?1:int(options.margin.x)),
width: (options.shiftButton && options.shiftButton.length+2)||6,
content: options.shiftButton||'Shft',
align: 'center',
autoFocus: false,
mouse: true
});
this._.shift.on('press',function () {
self.shift=~self.shift;
for(var i=0;i<26;i++) {
self._.buttons[i].setContent(self.shift?Keys[0][i].toUpperCase():Keys[0][i]);
}
if (options.compact && self.shift) for(i in Keys[1]) self._.buttons[26+Number(i)].setContent(Keys[2][i]);
if (options.compact && !self.shift) for(i in Keys[1]) self._.buttons[26+Number(i)].setContent(Keys[1][i]);
if (self.shift && options.nlButton) self._.delete.setContent(options.nlButton);
else if (!self.shift && options.nlButton) self._.delete.setContent(options.delButton||'DEL');
self.screen.render();
});
this._.delete = new Button({
screen: this.screen,
parent: this,
top: bbox.height-3,
height: 1,
left: int(bbox.width/2)+(options.compact?0:int(options.margin.x)),
width: (options.delButton && options.delButton.length+2)||6,
content: options.delButton||'DEL',
align: 'center',
autoFocus: false,
mouse: true
});
this._.delete.on('press',function () {
var line=self._.input.getValue();
if (!self.shift || !options.nlButton) {
// Delete last character
self._.input.setValue(line.substring(0,line.length-1));
} else if (self.shift && options.nlButton) {
// Insert newline
self._.input.setValue(line+'\n');
}
//self.screen.render();
self._.input.update();
});
this._.input = new TextBox({
parent: this,
value: options.value||'content',
width: bbox.width-4,
height: 1,
left: 1,
top: 0,
style: {
fg:(options.style.input&&options.style.input.fg)||'black',
bg:(options.style.input&&options.style.input.bg)||'white',
bold:true
}
});
y=1+options.margin.y;
i=0;
while ((options.compact?i<complen:true) && keys[i] && y < (bbox.height-options.button.height-options.margin.y)-1) {
x=options.margin.x;
while ((options.compact?i<complen:true) && keys[i] && x < (bbox.width-options.button.width-options.margin.x)) {
function make(i) {
key = new Button ({
screen: self.screen,
parent: self,
top: y,
height: options.button.height,
left: x,
width: options.button.width,
content: keys[i],
align: 'center',
autoFocus: false,
mouse: true
});
self._.buttons.push(key);
key.on('press',function () {self.emit('key',i)});
}
make(i);
i++,x += (options.button.width+options.margin.x);
}
y += (options.button.height+options.margin.y);
}
// Save clickable elements of this widget; restore screen
this.clickable=this.screen.clickable;
this.screen.clickable=this._clickable;
this._hide=this.hide;
this.hide = function() {
self._hide();
self.screen.render();
// restore all clickable elements
self.screen.clickable=self._clickable;
}
this._show = this.show;
this.show = function() {
// save all screen clickable elements; enable only this clickables
self._clickable=self.screen.clickable;
self.screen.clickable=self.clickable;
self._show();
self.screen.render();
}
this.on('key',function (index) {
var line=self._.input.getValue(),ch;
if (options.compact) {
if (index<26) {
ch=self.shift?Keys[0][index].toUpperCase():Keys[0][index];
} else {
ch=self.shift?Keys[2][index-26]:Keys[1][index-26];
}
} else {
if (!self.shift || index>26) ch = keys[index];
else ch = keys[index].toUpperCase();
}
line += ch;
self._.input.setValue(line);
//self.screen.render();
self._.input.update();
});
}
//Question.prototype.__proto__ = Box.prototype;
inheritPrototype(Keyboard,Box);
Keyboard.prototype.setCallback = function (cb) {
this._.callback=cb
}
Keyboard.prototype.setValue = function (line) {
this._.input.setValue(line);
this._.input.update();
}
Keyboard.prototype.type = 'keyboard';
/**
* Expose
*/
module.exports = Keyboard;
};
BundleModuleCode['term/widgets/loading']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.1
**
** $INFO:
**
** loading.js - loading element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var Text = Require('term/widgets/text');
/**
* Loading
*/
function Loading(options) {
if (!instanceOf(this,Node)) {
return new Loading(options);
}
options = options || {};
Box.call(this, options);
this._.icon = new Text({
parent: this,
align: 'center',
top: 2,
left: 1,
right: 1,
height: 1,
content: '|'
});
}
//Loading.prototype.__proto__ = Box.prototype;
inheritPrototype(Loading,Box);
Loading.prototype.type = 'loading';
Loading.prototype.load = function(text) {
var self = this;
// XXX Keep above:
// var parent = this.parent;
// this.detach();
// parent.append(this);
this.show();
this.setContent(text);
if (this._.timer) {
this.stop();
}
this.screen.lockKeys = true;
this._.timer = setInterval(function() {
if (self._.icon.content === '|') {
self._.icon.setContent('/');
} else if (self._.icon.content === '/') {
self._.icon.setContent('-');
} else if (self._.icon.content === '-') {
self._.icon.setContent('\\');
} else if (self._.icon.content === '\\') {
self._.icon.setContent('|');
}
self.screen.render();
}, 200);
};
Loading.prototype.stop = function() {
this.screen.lockKeys = false;
this.hide();
if (this._.timer) {
clearInterval(this._.timer);
delete this._.timer;
}
this.screen.render();
};
/**
* Expose
*/
module.exports = Loading;
};
BundleModuleCode['term/widgets/listbar']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2016, Christopher Jeffrey and contributors
** $CREATED: sbosse on 28-3-15.
** $VERSION: 1.2.2
**
** $INFO:
*
* listbar.js - listbar element for blessed
*
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var helpers = Require('term/helpers');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* Listbar / HorizontalList
*/
function Listbar(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Listbar(options);
}
options = options || {};
this.items = [];
this.ritems = [];
this.commands = [];
this.leftBase = 0;
this.leftOffset = 0;
this.mouse = options.mouse || false;
Box.call(this, options);
if (!this.style.selected) {
this.style.selected = {};
}
if (!this.style.item) {
this.style.item = {};
}
if (options.commands || options.items) {
this.setItems(options.commands || options.items);
}
if (options.keys) {
this.on('keypress', function(ch, key) {
if (key.name === 'left'
|| (options.vi && key.name === 'h')
|| (key.shift && key.name === 'tab')) {
self.moveLeft();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'right'
|| (options.vi && key.name === 'l')
|| key.name === 'tab') {
self.moveRight();
self.screen.render();
// Stop propagation if we're in a form.
if (key.name === 'tab') return false;
return;
}
if (key.name === 'enter'
|| (options.vi && key.name === 'k' && !key.shift)) {
self.emit('action', self.items[self.selected], self.selected);
self.emit('select', self.items[self.selected], self.selected);
var item = self.items[self.selected];
if (item._.cmd.callback) {
item._.cmd.callback();
}
self.screen.render();
return;
}
if (key.name === 'escape' || (options.vi && key.name === 'q')) {
self.emit('action');
self.emit('cancel');
return;
}
});
}
if (options.autoCommandKeys) {
this.onScreenEvent('keypress', function(ch) {
if (/^[0-9]$/.test(ch)) {
var i = +ch - 1;
if (!~i) i = 9;
return self.selectTab(i);
}
});
}
this.on('focus', function() {
self.select(self.selected);
});
}
//Listbar.prototype.__proto__ = Box.prototype;
inheritPrototype(Listbar,Box);
Listbar.prototype.type = 'listbar';
Object.defineProperty(Listbar.prototype,'selected',{
get: function () {return this.leftBase + this.leftOffset;}
});
/* Depricated
Listbar.prototype.__defineGetter__('selected', function() {
return this.leftBase + this.leftOffset;
});
*/
Listbar.prototype.setItems = function(commands) {
var self = this;
if (!Array.isArray(commands)) {
commands = Object.keys(commands).reduce(function(obj, key, i) {
var cmd = commands[key]
, cb;
if (typeof cmd === 'function') {
cb = cmd;
cmd = { callback: cb };
}
if (cmd.text == null) cmd.text = key;
if (cmd.prefix == null) cmd.prefix = ++i + '';
if (cmd.text == null && cmd.callback) {
cmd.text = cmd.callback.name;
}
obj.push(cmd);
return obj;
}, []);
}
this.items.forEach(function(el) {
el.detach();
});
this.items = [];
this.ritems = [];
this.commands = [];
commands.forEach(function(cmd) {
self.add(cmd);
});
this.emit('set items');
};
Listbar.prototype.add =
Listbar.prototype.addItem =
Listbar.prototype.appendItem = function(item, callback) {
var self = this
, prev = this.items[this.items.length - 1]
, drawn
, cmd
, title
, len;
if (!this.parent) {
drawn = 0;
} else {
drawn = prev ? prev.aleft + prev.width : 0;
if (!this.screen.autoPadding) {
drawn += this.ileft;
}
}
if (typeof item === 'object') {
cmd = item;
if (cmd.prefix == null) cmd.prefix = (this.items.length + 1) + '';
}
if (typeof item === 'string') {
cmd = {
prefix: (this.items.length + 1) + '',
text: item,
callback: callback
};
}
if (typeof item === 'function') {
cmd = {
prefix: (this.items.length + 1) + '',
text: item.name,
callback: item
};
}
if (cmd.keys && cmd.keys[0]) {
cmd.prefix = cmd.keys[0];
}
var t = helpers.generateTags(this.style.prefix || { fg: 'lightblack' });
title = (cmd.prefix != null ? t.open + cmd.prefix + t.close + ':' : '') + cmd.text;
len = ((cmd.prefix != null ? cmd.prefix + ':' : '') + cmd.text).length;
var options = {
screen: this.screen,
top: 0,
left: drawn + 1,
height: 1,
content: title,
width: len + 2,
align: 'center',
autoFocus: false,
tags: true,
mouse: true,
style: helpers.merge({}, this.style.item),
noOverflow: true
};
if (!this.screen.autoPadding) {
options.top += this.itop;
options.left += this.ileft;
}
['bg', 'fg', 'bold', 'underline',
'blink', 'inverse', 'invisible'].forEach(function(name) {
options.style[name] = function() {
var attr = self.items[self.selected] === el
? self.style.selected[name]
: self.style.item[name];
if (typeof attr === 'function') attr = attr(el);
return attr;
};
});
var el = new Box(options);
this._[cmd.text] = el;
cmd.element = el;
el._.cmd = cmd;
this.ritems.push(cmd.text);
this.items.push(el);
this.commands.push(cmd);
this.append(el);
if (cmd.callback) {
if (cmd.keys) {
this.screen.key(cmd.keys, function() {
self.emit('action', el, self.selected);
self.emit('select', el, self.selected);
if (el._.cmd.callback) {
el._.cmd.callback();
}
self.select(el);
self.screen.render();
});
}
}
if (this.items.length === 1) {
this.select(0);
}
// XXX May be affected by new element.options.mouse option.
if (this.mouse) {
el.on('click', function() {
self.emit('action', el, self.selected);
self.emit('select', el, self.selected);
if (el._.cmd.callback) {
el._.cmd.callback();
}
self.select(el);
self.screen.render();
});
}
this.emit('add item');
};
Listbar.prototype.render = function() {
var self = this
, drawn = 0;
if (!this.screen.autoPadding) {
drawn += this.ileft;
}
this.items.forEach(function(el, i) {
if (i < self.leftBase) {
el.hide();
} else {
el.rleft = drawn + 1;
drawn += el.width + 2;
el.show();
}
});
return this._render();
};
Listbar.prototype.select = function(offset) {
if (typeof offset !== 'number') {
offset = this.items.indexOf(offset);
}
if (offset < 0) {
offset = 0;
} else if (offset >= this.items.length) {
offset = this.items.length - 1;
}
if (!this.parent) {
this.emit('select item', this.items[offset], offset);
return;
}
var lpos = this._getCoords();
if (!lpos) return;
var self = this
, width = (lpos.xl - lpos.xi) - this.iwidth
, drawn = 0
, visible = 0
, el;
el = this.items[offset];
if (!el) return;
this.items.forEach(function(el, i) {
if (i < self.leftBase) return;
var lpos = el._getCoords();
if (!lpos) return;
if (lpos.xl - lpos.xi <= 0) return;
drawn += (lpos.xl - lpos.xi) + 2;
if (drawn <= width) visible++;
});
var diff = offset - (this.leftBase + this.leftOffset);
if (offset > this.leftBase + this.leftOffset) {
if (offset > this.leftBase + visible - 1) {
this.leftOffset = 0;
this.leftBase = offset;
} else {
this.leftOffset += diff;
}
} else if (offset < this.leftBase + this.leftOffset) {
diff = -diff;
if (offset < this.leftBase) {
this.leftOffset = 0;
this.leftBase = offset;
} else {
this.leftOffset -= diff;
}
}
// XXX Move `action` and `select` events here.
this.emit('select item', el, offset);
};
Listbar.prototype.removeItem = function(child) {
var i = typeof child !== 'number'
? this.items.indexOf(child)
: child;
if (~i && this.items[i]) {
child = this.items.splice(i, 1)[0];
this.ritems.splice(i, 1);
this.commands.splice(i, 1);
this.remove(child);
if (i === this.selected) {
this.select(i - 1);
}
}
this.emit('remove item');
};
Listbar.prototype.move = function(offset) {
this.select(this.selected + offset);
};
Listbar.prototype.moveLeft = function(offset) {
this.move(-(offset || 1));
};
Listbar.prototype.moveRight = function(offset) {
this.move(offset || 1);
};
Listbar.prototype.selectTab = function(index) {
var item = this.items[index];
if (item) {
if (item._.cmd.callback) {
item._.cmd.callback();
}
this.select(index);
this.screen.render();
}
this.emit('select tab', item, index);
};
/**
* Expose
*/
module.exports = Listbar;
};
BundleModuleCode['term/widgets/table']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2017-2021)
** $REVESIO: 1.2.2
**
** $INFO:
**
** table.js - table element for blessed
**
** typoef options = {
** data: string [][],
** }
**
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* Table
*/
function Table(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Table(options);
}
options = options || {};
options.shrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
options.align = options.align || 'center';
// Regular tables do not get custom height (this would
// require extra padding). Maybe add in the future.
delete options.height;
Box.call(this, options);
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setContent('');
self.setData(self.rows);
});
this.on('resize', function() {
self.setContent('');
self.setData(self.rows);
self.screen.render();
});
}
//Table.prototype.__proto__ = Box.prototype;
inheritPrototype(Table,Box);
Table.prototype.type = 'table';
Table.prototype._calculateMaxes = function() {
var self = this;
var maxes = [];
if (this.detached) return;
this.rows = this.rows || [];
this.rows.forEach(function(row) {
row.forEach(function(cell, i) {
var clen = self.strWidth(cell);
if (!maxes[i] || maxes[i] < clen) {
maxes[i] = clen;
}
});
});
var total = maxes.reduce(function(total, max) {
return total + max;
}, 0);
total += maxes.length + 1;
// XXX There might be an issue with resizing where on the first resize event
// width appears to be less than total if it's a percentage or left/right
// combination.
if (this.width < total) {
delete this.position.width;
}
if (this.position.width != null) {
var missing = this.width - total;
var w = missing / maxes.length | 0;
var wr = missing % maxes.length;
maxes = maxes.map(function(max, i) {
if (i === maxes.length - 1) {
return max + w + wr;
}
return max + w;
});
} else {
maxes = maxes.map(function(max) {
return max + self.pad;
});
}
return this._maxes = maxes;
};
Table.prototype.setRows =
Table.prototype.setData = function(rows) {
var self = this
, text = ''
, align = this.align;
this.rows = rows || [];
this._calculateMaxes();
if (!this._maxes) return;
this.rows.forEach(function(row, i) {
var isFooter = i === self.rows.length - 1;
row.forEach(function(cell, i) {
var width = self._maxes[i];
var clen = self.strWidth(cell);
if (i !== 0) {
text += ' ';
}
while (clen < width) {
if (align === 'center') {
cell = ' ' + cell + ' ';
clen += 2;
} else if (align === 'left') {
cell = cell + ' ';
clen += 1;
} else if (align === 'right') {
cell = ' ' + cell;
clen += 1;
}
}
if (clen > width) {
if (align === 'center') {
cell = cell.substring(1);
clen--;
} else if (align === 'left') {
cell = cell.slice(0, -1);
clen--;
} else if (align === 'right') {
cell = cell.substring(1);
clen--;
}
}
text += cell;
});
if (!isFooter) {
text += '\n\n';
}
});
delete this.align;
this.setContent(text);
this.align = align;
};
Table.prototype.render = function() {
var self = this;
var coords = this._render();
if (!coords) return;
this._calculateMaxes();
if (!this._maxes) return coords;
var lines = this.screen.lines
, xi = coords.xi
, yi = coords.yi
, rx
, ry
, i;
var dattr = this.sattr(this.style)
, hattr = this.sattr(this.style.header)
, cattr = this.sattr(this.style.cell)
, battr = this.sattr(this.style.border);
var width = coords.xl - coords.xi - this.iright
, height = coords.yl - coords.yi - this.ibottom;
// Apply attributes to header cells and cells.
for (var y = this.itop; y < height; y++) {
if (!lines[yi + y]) break;
for (var x = this.ileft; x < width; x++) {
if (!lines[yi + y][xi + x]) break;
// Check to see if it's not the default attr. Allows for tags:
if (lines[yi + y][xi + x][0] !== dattr) continue;
if (y === this.itop) {
lines[yi + y][xi + x][0] = hattr;
} else {
lines[yi + y][xi + x][0] = cattr;
}
lines[yi + y].dirty = true;
}
}
if (!this.border || this.options.noCellBorders) return coords;
// Draw border with correct angles.
ry = 0;
for (i = 0; i < self.rows.length + 1; i++) {
if (!lines[yi + ry]) break;
rx = 0;
self._maxes.forEach(function(max, i) {
rx += max;
if (i === 0) {
if (!lines[yi + ry][xi + 0]) return;
// left side
if (ry === 0) {
// top
lines[yi + ry][xi + 0][0] = battr;
// lines[yi + ry][xi + 0][1] = '\u250c'; // '┌'
} else if (ry / 2 === self.rows.length) {
// bottom
lines[yi + ry][xi + 0][0] = battr;
// lines[yi + ry][xi + 0][1] = '\u2514'; // '└'
} else {
// middle
lines[yi + ry][xi + 0][0] = battr;
lines[yi + ry][xi + 0][1] = '\u251c'; // '├'
// XXX If we alter iwidth and ileft for no borders - nothing should be written here
if (!self.border.left) {
lines[yi + ry][xi + 0][1] = '\u2500'; // '─'
}
}
lines[yi + ry].dirty = true;
} else if (i === self._maxes.length - 1) {
if (!lines[yi + ry][xi + rx + 1]) return;
// right side
if (ry === 0) {
// top
rx++;
lines[yi + ry][xi + rx][0] = battr;
// lines[yi + ry][xi + rx][1] = '\u2510'; // '┐'
} else if (ry / 2 === self.rows.length) {
// bottom
rx++;
lines[yi + ry][xi + rx][0] = battr;
// lines[yi + ry][xi + rx][1] = '\u2518'; // '┘'
} else {
// middle
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u2524'; // '┤'
// XXX If we alter iwidth and iright for no borders - nothing should be written here
if (!self.border.right) {
lines[yi + ry][xi + rx][1] = '\u2500'; // '─'
}
}
lines[yi + ry].dirty = true;
return;
}
if (!lines[yi + ry][xi + rx + 1]) return;
// center
if (ry === 0) {
// top
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u252c'; // '┬'
// XXX If we alter iheight and itop for no borders - nothing should be written here
if (!self.border.top) {
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
}
} else if (ry / 2 === self.rows.length) {
// bottom
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u2534'; // '┴'
// XXX If we alter iheight and ibottom for no borders - nothing should be written here
if (!self.border.bottom) {
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
}
} else {
// middle
if (self.options.fillCellBorders) {
var lbg = (ry <= 2 ? hattr : cattr) & 0x1ff;
rx++;
lines[yi + ry][xi + rx][0] = (battr & ~0x1ff) | lbg;
} else {
rx++;
lines[yi + ry][xi + rx][0] = battr;
}
lines[yi + ry][xi + rx][1] = '\u253c'; // '┼'
// rx++;
}
lines[yi + ry].dirty = true;
});
ry += 2;
}
// Draw internal borders.
for (ry = 1; ry < self.rows.length * 2; ry++) {
if (!lines[yi + ry]) break;
rx = 0;
self._maxes.slice(0, -1).forEach(function(max) {
rx += max;
if (!lines[yi + ry][xi + rx + 1]) return;
if (ry % 2 !== 0) {
if (self.options.fillCellBorders) {
var lbg = (ry <= 2 ? hattr : cattr) & 0x1ff;
rx++;
lines[yi + ry][xi + rx][0] = (battr & ~0x1ff) | lbg;
} else {
rx++;
lines[yi + ry][xi + rx][0] = battr;
}
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
lines[yi + ry].dirty = true;
} else {
rx++;
}
});
rx = 1;
self._maxes.forEach(function(max) {
while (max--) {
if (ry % 2 === 0) {
if (!lines[yi + ry]) break;
if (!lines[yi + ry][xi + rx + 1]) break;
if (self.options.fillCellBorders) {
var lbg = (ry <= 2 ? hattr : cattr) & 0x1ff;
lines[yi + ry][xi + rx][0] = (battr & ~0x1ff) | lbg;
} else {
lines[yi + ry][xi + rx][0] = battr;
}
lines[yi + ry][xi + rx][1] = '\u2500'; // '─'
lines[yi + ry].dirty = true;
}
rx++;
}
rx++;
});
}
return coords;
};
/**
* Expose
*/
module.exports = Table;
};
BundleModuleCode['term/widgets/listtable']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $CREATED: sbosse on 28-3-15.
** $VERSION: 1.2.1
**
** $INFO:
*
* listtable.js - list table element for blessed
*
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var List = Require('term/widgets/list');
var Table = Require('term/widgets/table');
/**
* ListTable
*/
function ListTable(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new ListTable(options);
}
options = options || {};
options.shrink = true;
options.normalShrink = true;
options.style = options.style || {};
options.style.border = options.style.border || {};
options.style.header = options.style.header || {};
options.style.cell = options.style.cell || {};
this.__align = options.align || 'center';
delete options.align;
options.style.selected = options.style.cell.selected;
options.style.item = options.style.cell;
List.call(this, options);
this._header = new Box({
parent: this,
left: this.screen.autoPadding ? 0 : this.ileft,
top: 0,
width: 'shrink',
height: 1,
style: options.style.header,
tags: options.parseTags || options.tags
});
this.on('scroll', function() {
self._header.setFront();
self._header.rtop = self.childBase;
if (!self.screen.autoPadding) {
self._header.rtop = self.childBase + (self.border ? 1 : 0);
}
});
this.pad = options.pad != null
? options.pad
: 2;
this.setData(options.rows || options.data);
this.on('attach', function() {
self.setData(self.rows);
});
this.on('resize', function() {
var selected = self.selected;
self.setData(self.rows);
self.select(selected);
self.screen.render();
});
}
//ListTable.prototype.__proto__ = List.prototype;
inheritPrototype(ListTable,List);
ListTable.prototype.type = 'list-table';
ListTable.prototype._calculateMaxes = Table.prototype._calculateMaxes;
ListTable.prototype.setRows =
ListTable.prototype.setData = function(rows) {
var self = this
, align = this.__align;
if (this.visible && this.lpos) {
this.clearPos();
}
this.clearItems();
this.rows = rows || [];
this._calculateMaxes();
if (!this._maxes) return;
this.addItem('');
this.rows.forEach(function(row, i) {
var isHeader = i === 0;
var text = '';
row.forEach(function(cell, i) {
var width = self._maxes[i];
var clen = self.strWidth(cell);
if (i !== 0) {
text += ' ';
}
while (clen < width) {
if (align === 'center') {
cell = ' ' + cell + ' ';
clen += 2;
} else if (align === 'left') {
cell = cell + ' ';
clen += 1;
} else if (align === 'right') {
cell = ' ' + cell;
clen += 1;
}
}
if (clen > width) {
if (align === 'center') {
cell = cell.substring(1);
clen--;
} else if (align === 'left') {
cell = cell.slice(0, -1);
clen--;
} else if (align === 'right') {
cell = cell.substring(1);
clen--;
}
}
text += cell;
});
if (isHeader) {
self._header.setContent(text);
} else {
self.addItem(text);
}
});
this._header.setFront();
this.select(0);
};
ListTable.prototype._select = ListTable.prototype.select;
ListTable.prototype.select = function(i) {
if (i === 0) {
i = 1;
}
if (i <= this.childBase) {
this.setScroll(this.childBase - 1);
}
return this._select(i);
};
ListTable.prototype.render = function() {
var self = this;
var coords = this._render();
if (!coords) return;
this._calculateMaxes();
if (!this._maxes) return coords;
var lines = this.screen.lines
, xi = coords.xi
, yi = coords.yi
, rx
, ry
, i;
var battr = this.sattr(this.style.border);
var height = coords.yl - coords.yi - this.ibottom;
if (!this.border || this.options.noCellBorders) return coords;
// Draw border with correct angles.
ry = 0;
for (i = 0; i < height + 1; i++) {
if (!lines[yi + ry]) break;
rx = 0;
self._maxes.slice(0, -1).forEach(function(max) {
rx += max;
if (!lines[yi + ry][xi + rx + 1]) return;
// center
if (ry === 0) {
// top
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u252c'; // '┬'
// XXX If we alter iheight and itop for no borders - nothing should be written here
if (!self.border.top) {
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
}
lines[yi + ry].dirty = true;
} else if (ry === height) {
// bottom
rx++;
lines[yi + ry][xi + rx][0] = battr;
lines[yi + ry][xi + rx][1] = '\u2534'; // '┴'
// XXX If we alter iheight and ibottom for no borders - nothing should be written here
if (!self.border.bottom) {
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
}
lines[yi + ry].dirty = true;
} else {
// middle
rx++;
}
});
ry += 1;
}
// Draw internal borders.
for (ry = 1; ry < height; ry++) {
if (!lines[yi + ry]) break;
rx = 0;
self._maxes.slice(0, -1).forEach(function(max) {
rx += max;
if (!lines[yi + ry][xi + rx + 1]) return;
if (self.options.fillCellBorders !== false) {
var lbg = lines[yi + ry][xi + rx][0] & 0x1ff;
rx++;
lines[yi + ry][xi + rx][0] = (battr & ~0x1ff) | lbg;
} else {
rx++;
lines[yi + ry][xi + rx][0] = battr;
}
lines[yi + ry][xi + rx][1] = '\u2502'; // '│'
lines[yi + ry].dirty = true;
});
}
return coords;
};
/**
* Expose
*/
module.exports = ListTable;
};
BundleModuleCode['term/widgets/terminal']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2018, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2017-2021)
** $REVESIO: 1.5.2
**
** $INFO:
**
** terminal.js - interactive terminal shell based on textarea element for blessed
**
** events: 'eval' (high level passing command line after enter key was hit)
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var unicode = Require('term/unicode');
var nextTick = global.setImmediate || process.nextTick.bind(process);
var Node = Require('term/widgets/node');
var Input = Require('term/widgets/input');
/**
* Terminal
*/
function Terminal(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new Terminal(options);
}
options = options || {};
options.scrollable = options.scrollable !== false;
Input.call(this, options);
this.screen._listenKeys(this);
this.value = options.value || '';
// cursor position
this.cpos = {x:-1,y:-1};
this.cursorControl=true;
this.multiline=options.multiline;
this.history=[];
this.historyTop=0;
this.break='all';
this.__updateCursor = this._updateCursor.bind(this);
this.on('resize', this.__updateCursor);
this.on('move', this.__updateCursor);
if (options.inputOnFocus) {
this.on('focus', this.readInput.bind(this, null));
}
if (!options.inputOnFocus && options.keys) {
this.on('keypress', function(ch, key) {
if (self._reading) return;
if (key.name === 'enter' || (options.vi && key.name === 'i')) {
return self.readInput();
}
if (key.name === 'e') {
return self.readEditor();
}
});
}
if (options.mouse) {
this.on('click', function(data) {
if (self._reading) return;
if (data.button !== 'right') return;
self.readEditor();
});
}
var offsetY = 0;
if (this._clines) offsetY=this._clines.length-(this.childBase||0);
if (options.prompt) {
this.value=options.prompt;
this.prompt=options.prompt;
this.inputRange={x0:options.prompt.length,y0:offsetY,y1:offsetY,last:0,line:0};
} else {
this.inputRange={x0:0,y0:offsetY,y1:offsetY,last:0,line:0};
this.prompt='';
}
}
//Terminal.prototype.__proto__ = Input.prototype;
inheritPrototype(Terminal,Input);
Terminal.prototype.type = 'terminal';
Terminal.prototype._updateCursor = function(get) {
if (this.screen.focused !== this) {
return;
}
var lpos = get ? this.lpos : this._getCoords();
if (!lpos) return;
var last = this._clines[this._clines.length - 1]
, program = this.screen.program
, line
, offsetY = this.childBase||0
, cx
, cy;
// Stop a situation where the textarea begins scrolling
// and the last cline appears to always be empty from the
// _typeScroll `+ '\n'` thing.
// Maybe not necessary anymore?
if (last === '' && this.value[this.value.length - 1] !== '\n') {
last = this._clines[this._clines.length - 2] || '';
}
line = Math.min(
this._clines.length - 1 - (this.childBase || 0),
(lpos.yl - lpos.yi) - this.iheight - 1);
// When calling clearValue() on a full textarea with a border, the first
// argument in the above Math.min call ends up being -2. Make sure we stay
// positive.
line = Math.max(0, line);
if (this.cpos.x==-1 || !this.cursorControl) this.cpos.x = this.strWidth(last);
if (this.cpos.y==-1 || !this.cursorControl) this.cpos.y = line;
this.cpos.y = Math.min(this.cpos.y,line);
this.cpos.x = Math.min(this.cpos.x,this.strWidth(this._clines[offsetY+this.cpos.y]));
cx = lpos.xi + this.ileft + this.cpos.x;
cy = lpos.yi + this.itop + this.cpos.y;
// XXX Not sure, but this may still sometimes
// cause problems when leaving editor.
if (cy === program.y && cx === program.x) {
return;
}
if (cy === program.y) {
if (cx > program.x) {
program.cuf(cx - program.x);
} else if (cx < program.x) {
program.cub(program.x - cx);
}
} else if (cx === program.x) {
if (cy > program.y) {
program.cud(cy - program.y);
} else if (cy < program.y) {
program.cuu(program.y - cy);
}
} else {
program.cup(cy, cx);
}
};
Terminal.prototype.input =
Terminal.prototype.setInput =
Terminal.prototype.readInput = function(callback) {
var self = this
, focused = this.screen.focused === this;
if (this._reading) return;
this._reading = true;
this._callback = callback;
if (!focused) {
this.screen.saveFocus();
this.focus();
}
this.screen.grabKeys = true;
this._updateCursor();
this.screen.program.showCursor();
//this.screen.program.sgr('normal');
this._done = function fn(err, value) {
if (!self._reading) return;
if (fn.done) return;
fn.done = true;
self._reading = false;
delete self._callback;
delete self._done;
self.removeListener('keypress', self.__listener);
delete self.__listener;
self.removeListener('blur', self.__done);
delete self.__done;
self.screen.program.hideCursor();
self.screen.grabKeys = false;
if (!focused) {
self.screen.restoreFocus();
}
if (self.options.inputOnFocus) {
self.screen.rewindFocus();
}
// Ugly
if (err === 'stop') return;
if (err) {
self.emit('error', err);
} else if (value != null) {
self.emit('submit', value);
} else {
self.emit('cancel', value);
}
self.emit('action', value);
if (!callback) return;
return err
? callback(err)
: callback(null, value);
};
// Put this in a nextTick so the current
// key event doesn't trigger any keys input.
nextTick(function() {
if (self.__listener) {
// double fired?
return;
}
self.__listener = self._listener.bind(self);
self.on('keypress', self.__listener);
});
this.__done = this._done.bind(this, null, null);
this.on('blur', this.__done);
};
// Print ONE line (call mutiple times for multi-line text). Auto wrapping is supprted, though.
Terminal.prototype.print = function (line) {
// Log(this.inputRange,this._clines.length);
var offsetY = this.childBase||0,
cn1 = this._clines.length, y0=this.inputRange.y0,
start = this.getLinearPos(this.value,offsetY+this.inputRange.y0,0),
end = this.getLinearPos(this.value,offsetY+this.inputRange.y1,
this._clines[offsetY+this.inputRange.y1].length);
var command = this.value.slice(start,end);
this.value=this.value.slice(0,start)+line+'\n'+command+this.value.slice(end);
this.screen.render();
// Update inputRange
var cn2= this._clines.length;
this.scrollBottom();
this.cpos.y += 10;
this.cpos.x=this.inputRange.x0=this.prompt.length;
this._updateCursor(true);
this.inputRange.y0=Math.min(this._clines.length-1,this.cpos.y-this.inputRange.last);
this.inputRange.y1=Math.min(this._clines.length-1,this.cpos.y);
}
Terminal.prototype._listener = function(ch, key) {
// Cursor position must be synced with scrollablebox and vice versa (if scrollable)! A real mess.
var done = this._done
, self = this
, value = this.value
, clinesLength=this._clines.length
, offsetY = this.childBase||0 // scrollable line offset if any
, newline = false
, lastchar = false
, backspace = false
, controlkey = false
, lastline = (this.cpos.y+offsetY+1) == clinesLength;
if (key.name === 'return') return;
if (key.name === 'enter') {
// Log(this._clines)
// clear input line; execute command; create new input line
var start = this.getLinearPos(this.value,offsetY+this.inputRange.y0,0),
end = this.getLinearPos(this.value,offsetY+this.inputRange.y1,
this._clines[offsetY+this.inputRange.y1].length);
var command = this.value.slice(start+this.prompt.length,end);
this.value=this.value.slice(0,start)+this.prompt+this.value.slice(end);
if (command && command != this.history[this.historyTop-1]) {
this.history.push(command);
this.historyTop=this.history.length;
}
this.screen.render();
offsetY = this.childBase||0;
self.cpos.y += 10; // bottom
this._updateCursor(true);
this.cpos.x=self._clines[offsetY+self.cpos.y].length;
this.inputRange.y0=this.inputRange.y1=this.cpos.y; this.inputRange.last=0;
this.emit('eval',command);
return;
}
function history(delta) {
if (self.historyTop+delta<0 ) return self.scrollBottom();
self.historyTop += delta;
var start = self.getLinearPos(self.value,offsetY+self.inputRange.y0,0),
end = self.getLinearPos(self.value,offsetY+self.inputRange.y1,
self._clines[offsetY+self.inputRange.y1].length);
var command = self.history[self.historyTop]||'';
self.historyTop = Math.min(Math.max(0,self.historyTop),self.history.length);
self.value=self.value.slice(0,start)+self.prompt+command+self.value.slice(end);
self.screen.render();
self.scrollBottom();
self.cpos.x=self._clines[self._clines.length-1].length;
self.cpos.y += 10; // bottom
self._updateCursor(true);
self.inputRange.y1=self.cpos.y;
offsetY = self.childBase||0;
// find start y
var y0=self.cpos.y; while (self._clines[offsetY+y0].indexOf(self.prompt)!=0) y0--;
self.inputRange.y0=y0;
self.inputRange.last=self.inputRange.y1-self.inputRange.y0;
}
// Handle cursor positiong by keys.
if (this.cursorControl) switch (key.name) {
case 'left':
controlkey=true;
if (this.cpos.y==this.inputRange.y0 && this.cpos.x>this.inputRange.x0) this.cpos.x--;
else if (this.cpos.y!=this.inputRange.y0 && this.cpos.x>0) this.cpos.x--;
else if (this.cpos.y!=this.inputRange.y0 && this.cpos.x==0) {
this.cpos.y--;
this.cpos.x=this._clines[offsetY+this.cpos.y].length;
}
this._updateCursor(true);
break;
case 'right':
controlkey=true;
if (this.cpos.y>=this.inputRange.y0 && this.cpos.y<=this.inputRange.y1 &&
this.cpos.x<this._clines[offsetY+this.cpos.y].length-1) {
this.cpos.x++;
} else if (this.cpos.y>=this.inputRange.y0 && this.cpos.y<this.inputRange.y1 &&
this.cpos.x==this._clines[offsetY+this.cpos.y].length-1) {
this.cpos.x=0;
this.cpos.y++;
} else if (this.cpos.y>=this.inputRange.y0 && this.cpos.y==this.inputRange.y1 &&
this.cpos.x<this._clines[offsetY+this.cpos.y].length) {
this.cpos.x++;
}
this._updateCursor(true);
break;
case 'up':
controlkey=true;
history(-1);
return;
break;
case 'down':
controlkey=true;
history(1);
return;
break;
}
if (this.options.keys && key.ctrl && key.name === 'e') {
return this.readEditor();
}
// Challenge: sync with line wrapping and adjust cursor and scrolling (done in element._wrapContent)
// TODO: Optimize typing by writing directly
// to the screen and screen buffer here.
if (key.name === 'escape') {
done(null, null);
} else if (key.name === 'backspace') {
backspace=true;
if (this.cpos.y==this.inputRange.y0 && this.cpos.x<=this.inputRange.x0) return;
if (this.value.length) {
if (this.screen.fullUnicode) {
if (unicode.isSurrogate(this.value, this.value.length - 2)) {
// || unicode.isCombining(this.value, this.value.length - 1)) {
this.value = this.value.slice(0, -2);
} else {
this.value = this.value.slice(0, -1);
}
} else {
if (!this.cursorControl ||
this.cpos.x==-1 ||
(this.cpos.x==this._clines[offsetY+this.cpos.y].length &&
this.cpos.y==this._clines.length-1-offsetY)) {
// Delete last char of last line
this.value = this.value.slice(0, -1);
} else {
// Delete char at current cursor position
vpos=this.getLinearPos(this.value,offsetY+this.cpos.y, this.cpos.x);
// vpos+= this.cpos.x;
this.value = this.value.substr(0,vpos-1)+
this.value.substr(vpos,1000000);
}
}
if (this.cpos.x>0) this.cpos.x--;
else {this.cpos.x=-1; if (offsetY==0 && this.cpos.y>0 && lastline) this.cpos.y--; };
}
} else if (!controlkey && ch) {
if (!/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(ch)) {
if (!this.cursorControl ||
this.cpos.x==-1 ||
(this.cpos.x==this._clines[offsetY+this.cpos.y].length &&
this.cpos.y==this._clines.length-1-offsetY)) {
// Append new char at end of (last) line
lastchar=true;
this.value += ch;
} else {
// Insert new char into line at current cursor position
vpos=this.getLinearPos(this.value,offsetY+this.cpos.y, this.cpos.x);
// vpos+= this.cpos.x;
this.value = this.value.substr(0,vpos)+ch+
this.value.substr(vpos,1000000);
}
if (newline) {
this.cpos.x=0; // first left position is zero!
this.cpos.y++;
} else
this.cpos.x++;
}
}
var rmline=this.cpos.x==-1;
this.inputRange.line = this._clines.rtof[offsetY+this.cpos.y];
// Log(this.cpos, this.inputRange);
// TODO: clean up this mess; use rtof and ftor attributes of _clines
// to determine where we are (react correctly on line wrap extension and reduction)
if (this.value !== value) {
var cn0=clinesLength,
endofline=this.cpos.x==this._clines[offsetY+this.cpos.y].length+1,
cn1=this._clines.length;
var linelength1=this._clines[offsetY+this.cpos.y] && this._clines[offsetY+this.cpos.y].length;
this.screen.render();
var linelength2=this._clines[offsetY+this.cpos.y] && this._clines[offsetY+this.cpos.y].length;
var cn2=this._clines.length;
// Log(this.cpos,this.inputRange,linelength1,linelength2,cn0,cn2,this.inputRange,lastline,lastchar,endofline);
// Log('L',this.cpos,lastline,lastchar,endofline,linelength1,linelength2);
if (cn2>cn0 && endofline) {
this.scrollBottom();
// wrap expansion
this.cpos.y++;
this.inputRange.last++;
if (this._clines[offsetY+this.cpos.y] && lastchar) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
else this.cpos.x=linelength1-linelength2-1;
this._updateCursor(true);
this.inputRange.y0=this.cpos.y-this.inputRange.last;
this.inputRange.y1=this.cpos.y;
} else if (cn2<cn0 && !rmline) {
// wrap reduction
if (this.cpos.y>0 && !lastline && lastchar) this.cpos.y--;
this.inputRange.last--;
if (this._clines[offsetY+this.cpos.y]) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
this._updateCursor(true);
this.inputRange.y0=this.cpos.y-this.inputRange.last;
this.inputRange.y1=this.cpos.y;
offsetY = this.childBase||0;
this.cpos.x=this._clines[offsetY+this.cpos.y].length;
this._updateCursor(true);
} else if (linelength2<linelength1 && !backspace) {
// wrap shift
this.cpos.y++;
this.cpos.x=linelength1-linelength2;
this._updateCursor(true);
}
if (offsetY>0 && backspace) {
// @fix line deleted; refresh again due to miscalculation of height in scrollablebox!
this.scroll(0);
this.screen.render();
if (rmline) {
if (this._clines[offsetY+this.cpos.y]) this.cpos.x=this._clines[offsetY+this.cpos.y].length;
else if (this._clines[offsetY+this.cpos.y-1]) this.cpos.x=this._clines[offsetY+this.cpos.y-1].length;
this._updateCursor(true);
}
}
}
};
// Return start position of nth (c)line in linear value string
Terminal.prototype.getLinearPos = function(v,clineIndex,cposx) {
var lines=v.split('\n'),
line=this._clines[clineIndex], // assuming search in plain text line (no ctrl/spaces aligns)
flinenum=this._clines.rtof[clineIndex],
vpos=flinenum>0?
lines.slice(0,flinenum)
.map(function (line) { return line.length+1 })
.reduce(function (a,b) { return a+b }):0;
// console.log(clineIndex,lines.length,flinenum,lines[flinenum],line);
// TODO: search from a start position in line estimated by _clines.ftor array
function search(part,line) {
// assuming search offset in plain text line (no ctrl/spaces aligns)
var i=line.indexOf(part);
if (i==-1) return 0;
else return i;
}
if (lines[flinenum]) {
return vpos+search(line,lines[flinenum])+cposx;
} else
return vpos+cposx;
// clineIndex is the index in the _clines array, cposx the cursor position in this line!
var vpos=0,len=v.length,cline,clinePos=0,clineNum=0;
cline=this._clines[clineNum];
// To support auto line wrapping the clines have to be parsed, too!
while (vpos < len && clineIndex) {
if (v.charAt(vpos)=='\n') {
clinePos=-1;
clineIndex--;
clineNum++;
cline=this._clines[clineNum];
} else {
if (v.charAt(vpos) != cline.charAt(clinePos)) {
//
clinePos=0;
clineIndex--;
clineNum++;
cline=this._clines[clineNum];
continue;
}
}
vpos++; clinePos++;
}
if (clineIndex==0) return vpos+cposx;
else 0
}
Terminal.prototype._typeScroll = function() {
// XXX Workaround
var height = this.height - this.iheight;
// Scroll down?
// if (typeof Log != 'undefined') Log(this.childBase,this.childOffset,this.cpos.y,height);
//if (this._clines.length - this.childBase > height) {
if (this.cpos.y == height) {
this.scroll(this._clines.length);
}
};
Terminal.prototype.getValue = function() {
return this.value;
};
Terminal.prototype.setValue = function(value) {
if (value == null) {
value = this.value;
}
if (this._value !== value) {
this.value = value;
this._value = value;
this.setContent(this.value);
this._typeScroll();
this._updateCursor();
}
};
Terminal.prototype.clearInput =
Terminal.prototype.clearValue = function() {
return this.setValue('');
};
Terminal.prototype.submit = function() {
if (!this.__listener) return;
return this.__listener('\x1b', { name: 'escape' });
};
Terminal.prototype.cancel = function() {
if (!this.__listener) return;
return this.__listener('\x1b', { name: 'escape' });
};
Terminal.prototype.render = function() {
this.setValue();
return this._render();
};
Terminal.prototype.editor =
Terminal.prototype.setEditor =
Terminal.prototype.readEditor = function(callback) {
var self = this;
if (this._reading) {
var _cb = this._callback
, cb = callback;
this._done('stop');
callback = function(err, value) {
if (_cb) _cb(err, value);
if (cb) cb(err, value);
};
}
if (!callback) {
callback = function() {};
}
return this.screen.readEditor({ value: this.value }, function(err, value) {
if (err) {
if (err.message === 'Unsuccessful.') {
self.screen.render();
return self.readInput(callback);
}
self.screen.render();
self.readInput(callback);
return callback(err);
}
self.setValue(value);
self.screen.render();
return self.readInput(callback);
});
};
/**
* Expose
*/
module.exports = Terminal;
};
BundleModuleCode['term/widgets/image']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2016-2017)
** $REVESIO: 1.2.1
**
** $INFO:
**
** image.js - image element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
/**
* Modules
*/
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* Image
*/
function Image(options) {
if (!instanceOf(this,Node)) {
return new Image(options);
}
options = options || {};
options.type = options.itype || options.type || 'ansi';
Box.call(this, options);
if (options.type === 'ansi' && this.type !== 'ansiimage') {
var ANSIImage = require('./ansiimage');
Object.getOwnPropertyNames(ANSIImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(ANSIImage.prototype, key));
}, this);
ANSIImage.call(this, options);
return this;
}
if (options.type === 'overlay' && this.type !== 'overlayimage') {
var OverlayImage = require('./overlayimage');
Object.getOwnPropertyNames(OverlayImage.prototype).forEach(function(key) {
if (key === 'type') return;
Object.defineProperty(this, key,
Object.getOwnPropertyDescriptor(OverlayImage.prototype, key));
}, this);
OverlayImage.call(this, options);
return this;
}
throw new Error('`type` must either be `ansi` or `overlay`.');
}
//Image.prototype.__proto__ = Box.prototype;
inheritPrototype(Image,Box);
Image.prototype.type = 'image';
/**
* Expose
*/
module.exports = Image;
};
BundleModuleCode['term/widgets/ansiimage']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: sbosse (2017).
** $VERSION: 1.2.2
**
** $INFO:
*
* ansiimage.js - render PNGS/GIFS as ANSI
*
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var cp = Require('child_process');
var colors = Require('term/colors');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var tng = Require('term/tng');
/**
* ANSIImage
*/
function ANSIImage(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new ANSIImage(options);
}
options = options || {};
options.shrink = true;
Box.call(this, options);
this.scale = this.options.scale || 1.0;
this.options.animate = this.options.animate !== false;
this._noFill = true;
if (this.options.file) {
this.setImage(this.options.file);
}
this.screen.on('prerender', function() {
var lpos = self.lpos;
if (!lpos) return;
// prevent image from blending with itself if there are alpha channels
self.screen.clearRegion(lpos.xi, lpos.xl, lpos.yi, lpos.yl);
});
this.on('destroy', function() {
self.stop();
});
}
//ANSIImage.prototype.__proto__ = Box.prototype;
inheritPrototype(ANSIImage,Box);
ANSIImage.prototype.type = 'ansiimage';
ANSIImage.curl = function(url) {
try {
return cp.execFileSync('curl',
['-s', '-A', '', url],
{ stdio: ['ignore', 'pipe', 'ignore'] });
} catch (e) {
;
}
try {
return cp.execFileSync('wget',
['-U', '', '-O', '-', url],
{ stdio: ['ignore', 'pipe', 'ignore'] });
} catch (e) {
;
}
throw new Error('curl or wget failed.');
};
ANSIImage.prototype.setImage = function(file) {
this.file = typeof file === 'string' ? file : null;
if (/^https?:/.test(file)) {
file = ANSIImage.curl(file);
}
var width = this.position.width;
var height = this.position.height;
if (width != null) {
width = this.width;
}
if (height != null) {
height = this.height;
}
try {
this.setContent('');
this.img = tng(file, {
colors: colors,
width: width,
height: height,
scale: this.scale,
ascii: this.options.ascii,
speed: this.options.speed,
filename: this.file
});
if (width == null || height == null) {
this.width = this.img.cellmap[0].length;
this.height = this.img.cellmap.length;
}
if (this.img.frames && this.options.animate) {
this.play();
} else {
this.cellmap = this.img.cellmap;
}
} catch (e) {
this.setContent('Image Error: ' + e.message);
this.img = null;
this.cellmap = null;
}
};
ANSIImage.prototype.play = function() {
var self = this;
if (!this.img) return;
return this.img.play(function(bmp, cellmap) {
self.cellmap = cellmap;
self.screen.render();
});
};
ANSIImage.prototype.pause = function() {
if (!this.img) return;
return this.img.pause();
};
ANSIImage.prototype.stop = function() {
if (!this.img) return;
return this.img.stop();
};
ANSIImage.prototype.clearImage = function() {
this.stop();
this.setContent('');
this.img = null;
this.cellmap = null;
};
ANSIImage.prototype.render = function() {
var coords = this._render();
if (!coords) return;
if (this.img && this.cellmap) {
this.img.renderElement(this.cellmap, this);
}
return coords;
};
/**
* Expose
*/
module.exports = ANSIImage;
};
BundleModuleCode['term/tng']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors (MIT License)
** $REVESIO: 1.1.6
**
** $INFO:
*
* tng.js - png reader
*
* $ENDINFO
*/
var fs = Require('fs')
, util = Require('util')
, path = Require('path')
, zlib = Require('zlib')
, assert = Require('assert')
, cp = Require('child_process')
, exec = cp?cp.execFileSync:_;
/**
* PNG
*/
function PNG(file, options) {
var buf
, chunks
, idat
, pixels;
if (!(this instanceof PNG)) {
return new PNG(file, options);
}
if (!file) throw new Error('no file');
this.options = options || {};
this.colors = options.colors || Require('term/colors');
this.optimization = this.options.optimization || 'mem';
this.speed = this.options.speed || 1;
if (Buffer.isBuffer(file)) {
this.file = this.options.filename || null;
buf = file;
} else {
this.options.filename = file;
this.file = path.resolve(process.cwd(), file);
buf = fs.readFileSync(this.file);
}
this.format = buf.readUInt32BE(0) === 0x89504e47 ? 'png'
: buf.slice(0, 3).toString('ascii') === 'GIF' ? 'gif'
: buf.readUInt16BE(0) === 0xffd8 ? 'jpg'
: path.extname(this.file).slice(1).toLowerCase() || 'png';
if (this.format !== 'png') {
try {
return this.toPNG(buf);
} catch (e) {
throw e;
}
}
chunks = this.parseRaw(buf);
idat = this.parseChunks(chunks);
pixels = this.parseLines(idat);
this.bmp = this.createBitmap(pixels);
this.cellmap = this.createCellmap(this.bmp);
this.frames = this.compileFrames(this.frames);
}
PNG.prototype.parseRaw = function(buf) {
var chunks = []
, index = 0
, i = 0
, buf
, len
, type
, name
, data
, crc
, check
, critical
, public_
, conforming
, copysafe
, pos;
this._debug(this.file);
if (buf.readUInt32BE(0) !== 0x89504e47
|| buf.readUInt32BE(4) !== 0x0d0a1a0a) {
throw new Error('bad header');
}
i += 8;
while (i < buf.length) {
try {
len = buf.readUInt32BE(i);
i += 4;
pos = i;
type = buf.slice(i, i + 4);
name = type.toString('ascii');
i += 4;
data = buf.slice(i, i + len);
i += len;
check = this.crc32(buf.slice(pos, i));
crc = buf.readInt32BE(i);
i += 4;
critical = !!(~type[0] & 32);
public_ = !!(~type[1] & 32);
conforming = !!(~type[2] & 32);
copysafe = !!(~type[3] & 32);
if (crc !== check) {
throw new Error(name + ': bad crc');
}
} catch (e) {
if (this.options.debug) throw e;
break;
}
chunks.push({
index: index++,
id: name.toLowerCase(),
len: len,
pos: pos,
end: i,
type: type,
name: name,
data: data,
crc: crc,
check: check,
raw: buf.slice(pos, i),
flags: {
critical: critical,
public_: public_,
conforming: conforming,
copysafe: copysafe
}
});
}
return chunks;
};
PNG.prototype.parseChunks = function(chunks) {
var i
, chunk
, name
, data
, p
, idat
, info;
for (i = 0; i < chunks.length; i++) {
chunk = chunks[i];
name = chunk.id;
data = chunk.data;
info = {};
switch (name) {
case 'ihdr': {
this.width = info.width = data.readUInt32BE(0);
this.height = info.height = data.readUInt32BE(4);
this.bitDepth = info.bitDepth = data.readUInt8(8);
this.colorType = info.colorType = data.readUInt8(9);
this.compression = info.compression = data.readUInt8(10);
this.filter = info.filter = data.readUInt8(11);
this.interlace = info.interlace = data.readUInt8(12);
switch (this.bitDepth) {
case 1: case 2: case 4: case 8: case 16: case 24: case 32: break;
default: throw new Error('bad bit depth: ' + this.bitDepth);
}
switch (this.colorType) {
case 0: case 2: case 3: case 4: case 6: break;
default: throw new Error('bad color: ' + this.colorType);
}
switch (this.compression) {
case 0: break;
default: throw new Error('bad compression: ' + this.compression);
}
switch (this.filter) {
case 0: case 1: case 2: case 3: case 4: break;
default: throw new Error('bad filter: ' + this.filter);
}
switch (this.interlace) {
case 0: case 1: break;
default: throw new Error('bad interlace: ' + this.interlace);
}
break;
}
case 'plte': {
this.palette = info.palette = [];
for (p = 0; p < data.length; p += 3) {
this.palette.push({
r: data[p + 0],
g: data[p + 1],
b: data[p + 2],
a: 255
});
}
break;
}
case 'idat': {
this.size = this.size || 0;
this.size += data.length;
this.idat = this.idat || [];
this.idat.push(data);
info.size = data.length;
break;
}
case 'iend': {
this.end = true;
break;
}
case 'trns': {
this.alpha = info.alpha = Array.prototype.slice.call(data);
if (this.palette) {
for (p = 0; p < data.length; p++) {
if (!this.palette[p]) break;
this.palette[p].a = data[p];
}
}
break;
}
// https://wiki.mozilla.org/APNG_Specification
case 'actl': {
this.actl = info = {};
this.frames = [];
this.actl.numFrames = data.readUInt32BE(0);
this.actl.numPlays = data.readUInt32BE(4);
break;
}
case 'fctl': {
// IDAT is the first frame depending on the order:
// IDAT is a frame: acTL->fcTL->IDAT->[fcTL]->fdAT
// IDAT is not a frame: acTL->IDAT->[fcTL]->fdAT
if (!this.idat) {
this.idat = [];
this.frames.push({
idat: true,
fctl: info,
fdat: this.idat
});
} else {
this.frames.push({
fctl: info,
fdat: []
});
}
info.sequenceNumber = data.readUInt32BE(0);
info.width = data.readUInt32BE(4);
info.height = data.readUInt32BE(8);
info.xOffset = data.readUInt32BE(12);
info.yOffset = data.readUInt32BE(16);
info.delayNum = data.readUInt16BE(20);
info.delayDen = data.readUInt16BE(22);
info.disposeOp = data.readUInt8(24);
info.blendOp = data.readUInt8(25);
break;
}
case 'fdat': {
info.sequenceNumber = data.readUInt32BE(0);
info.data = data.slice(4);
this.frames[this.frames.length - 1].fdat.push(info.data);
break;
}
}
chunk.info = info;
}
this._debug(chunks);
if (this.frames) {
this.frames = this.frames.map(function(frame, i) {
frame.fdat = this.decompress(frame.fdat);
if (!frame.fdat.length) throw new Error('no data');
return frame;
}, this);
}
idat = this.decompress(this.idat);
if (!idat.length) throw new Error('no data');
return idat;
};
PNG.prototype.parseLines = function(data) {
var pixels = []
, x
, p
, prior
, line
, filter
, samples
, pendingSamples
, ch
, shiftStart
, i
, toShift
, sample;
this.sampleDepth =
this.colorType === 0 ? 1
: this.colorType === 2 ? 3
: this.colorType === 3 ? 1
: this.colorType === 4 ? 2
: this.colorType === 6 ? 4
: 1;
this.bitsPerPixel = this.bitDepth * this.sampleDepth;
this.bytesPerPixel = Math.ceil(this.bitsPerPixel / 8);
this.wastedBits = ((this.width * this.bitsPerPixel) / 8) - ((this.width * this.bitsPerPixel / 8) | 0);
this.byteWidth = Math.ceil(this.width * (this.bitsPerPixel / 8));
this.shiftStart = ((this.bitDepth + (8 / this.bitDepth - this.bitDepth)) - 1) | 0;
this.shiftMult = this.bitDepth >= 8 ? 0 : this.bitDepth;
this.mask = this.bitDepth === 32 ? 0xffffffff : (1 << this.bitDepth) - 1;
if (this.interlace === 1) {
samples = this.sampleInterlacedLines(data);
for (i = 0; i < samples.length; i += this.sampleDepth) {
pixels.push(samples.slice(i, i + this.sampleDepth));
}
return pixels;
}
for (p = 0; p < data.length; p += this.byteWidth) {
prior = line || [];
filter = data[p++];
line = data.slice(p, p + this.byteWidth);
line = this.unfilterLine(filter, line, prior);
samples = this.sampleLine(line);
for (i = 0; i < samples.length; i += this.sampleDepth) {
pixels.push(samples.slice(i, i + this.sampleDepth));
}
}
return pixels;
};
PNG.prototype.unfilterLine = function(filter, line, prior) {
for (var x = 0; x < line.length; x++) {
if (filter === 0) {
break;
} else if (filter === 1) {
line[x] = this.filters.sub(x, line, prior, this.bytesPerPixel);
} else if (filter === 2) {
line[x] = this.filters.up(x, line, prior, this.bytesPerPixel);
} else if (filter === 3) {
line[x] = this.filters.average(x, line, prior, this.bytesPerPixel);
} else if (filter === 4) {
line[x] = this.filters.paeth(x, line, prior, this.bytesPerPixel);
}
}
return line;
};
PNG.prototype.sampleLine = function(line, width) {
var samples = []
, x = 0
, pendingSamples
, ch
, i
, sample
, shiftStart
, toShift;
while (x < line.length) {
pendingSamples = this.sampleDepth;
while (pendingSamples--) {
ch = line[x];
if (this.bitDepth === 16) {
ch = (ch << 8) | line[++x];
} else if (this.bitDepth === 24) {
ch = (ch << 16) | (line[++x] << 8) | line[++x];
} else if (this.bitDepth === 32) {
ch = (ch << 24) | (line[++x] << 16) | (line[++x] << 8) | line[++x];
} else if (this.bitDepth > 32) {
throw new Error('bitDepth ' + this.bitDepth + ' unsupported.');
}
shiftStart = this.shiftStart;
toShift = shiftStart - (x === line.length - 1 ? this.wastedBits : 0);
for (i = 0; i <= toShift; i++) {
sample = (ch >> (this.shiftMult * shiftStart)) & this.mask;
if (this.colorType !== 3) {
if (this.bitDepth < 8) { // <= 8 would work too, doesn't matter
// sample = sample * (0xff / this.mask) | 0; // would work too
sample *= 0xff / this.mask;
sample |= 0;
} else if (this.bitDepth > 8) {
sample = (sample / this.mask) * 255 | 0;
}
}
samples.push(sample);
shiftStart--;
}
x++;
}
}
// Needed for deinterlacing?
if (width != null) {
samples = samples.slice(0, width * this.sampleDepth);
}
return samples;
};
// http://www.w3.org/TR/PNG-Filters.html
PNG.prototype.filters = {
sub: function Sub(x, line, prior, bpp) {
if (x < bpp) return line[x];
return (line[x] + line[x - bpp]) % 256;
},
up: function Up(x, line, prior, bpp) {
return (line[x] + (prior[x] || 0)) % 256;
},
average: function Average(x, line, prior, bpp) {
if (x < bpp) return Math.floor((prior[x] || 0) / 2);
// if (x < bpp) return (prior[x] || 0) >> 1;
return (line[x]
+ Math.floor((line[x - bpp] + prior[x]) / 2)
// + ((line[x - bpp] + prior[x]) >> 1)
) % 256;
},
paeth: function Paeth(x, line, prior, bpp) {
if (x < bpp) return prior[x] || 0;
return (line[x] + this._predictor(
line[x - bpp], prior[x] || 0, prior[x - bpp] || 0
)) % 256;
},
_predictor: function PaethPredictor(a, b, c) {
// a = left, b = above, c = upper left
var p = a + b - c
, pa = Math.abs(p - a)
, pb = Math.abs(p - b)
, pc = Math.abs(p - c);
if (pa <= pb && pa <= pc) return a;
if (pb <= pc) return b;
return c;
}
};
/**
* Adam7 deinterlacing ported to javascript from PyPNG:
* pypng - Pure Python library for PNG image encoding/decoding
* Copyright (c) 2009-2015, David Jones (MIT License).
* https://github.com/drj11/pypng
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
PNG.prototype.sampleInterlacedLines = function(raw) {
var psize
, vpr
, samples
, source_offset
, i
, pass
, xstart
, ystart
, xstep
, ystep
, recon
, ppr
, row_size
, y
, filter_type
, scanline
, flat
, offset
, k
, end_offset
, skip
, j
, k
, f;
var adam7 = [
[0, 0, 8, 8],
[4, 0, 8, 8],
[0, 4, 4, 8],
[2, 0, 4, 4],
[0, 2, 2, 4],
[1, 0, 2, 2],
[0, 1, 1, 2]
];
// Fractional bytes per pixel
psize = (this.bitDepth / 8) * this.sampleDepth;
// Values per row (of the target image)
vpr = this.width * this.sampleDepth;
// Make a result array, and make it big enough. Interleaving
// writes to the output array randomly (well, not quite), so the
// entire output array must be in memory.
samples = new Buffer(vpr * this.height);
samples.fill(0);
source_offset = 0;
for (i = 0; i < adam7.length; i++) {
pass = adam7[i];
xstart = pass[0];
ystart = pass[1];
xstep = pass[2];
ystep = pass[3];
if (xstart >= this.width) continue;
// The previous (reconstructed) scanline. Empty array at the
// beginning of a pass to indicate that there is no previous
// line.
recon = [];
// Pixels per row (reduced pass image)
ppr = Math.ceil((this.width - xstart) / xstep);
// Row size in bytes for this pass.
row_size = Math.ceil(psize * ppr);
for (y = ystart; y < this.height; y += ystep) {
filter_type = raw[source_offset];
source_offset += 1;
scanline = raw.slice(source_offset, source_offset + row_size);
source_offset += row_size;
recon = this.unfilterLine(filter_type, scanline, recon);
// Convert so that there is one element per pixel value
flat = this.sampleLine(recon, ppr);
if (xstep === 1) {
assert.equal(xstart, 0);
offset = y * vpr;
for (k = offset, f = 0; k < offset + vpr; k++, f++) {
samples[k] = flat[f];
}
} else {
offset = y * vpr + xstart * this.sampleDepth;
end_offset = (y + 1) * vpr;
skip = this.sampleDepth * xstep;
for (j = 0; j < this.sampleDepth; j++) {
for (k = offset + j, f = j; k < end_offset; k += skip, f += this.sampleDepth) {
samples[k] = flat[f];
}
}
}
}
}
return samples;
};
PNG.prototype.createBitmap = function(pixels) {
var bmp = []
, i;
if (this.colorType === 0) {
pixels = pixels.map(function(sample) {
return { r: sample[0], g: sample[0], b: sample[0], a: 255 };
});
} else if (this.colorType === 2) {
pixels = pixels.map(function(sample) {
return { r: sample[0], g: sample[1], b: sample[2], a: 255 };
});
} else if (this.colorType === 3) {
pixels = pixels.map(function(sample) {
if (!this.palette[sample[0]]) throw new Error('bad palette index');
return this.palette[sample[0]];
}, this);
} else if (this.colorType === 4) {
pixels = pixels.map(function(sample) {
return { r: sample[0], g: sample[0], b: sample[0], a: sample[1] };
});
} else if (this.colorType === 6) {
pixels = pixels.map(function(sample) {
return { r: sample[0], g: sample[1], b: sample[2], a: sample[3] };
});
}
for (i = 0; i < pixels.length; i += this.width) {
bmp.push(pixels.slice(i, i + this.width));
}
return bmp;
};
PNG.prototype.createCellmap = function(bmp, options) {
var bmp = bmp || this.bmp
, options = options || this.options
, cellmap = []
, scale = options.scale || 0.20
, height = bmp.length
, width = bmp[0].length
, cmwidth = options.width
, cmheight = options.height
, line
, x
, y
, xx
, yy
, scale
, xs
, ys;
if (cmwidth) {
scale = cmwidth / width;
} else if (cmheight) {
scale = cmheight / height;
}
if (!cmheight) {
cmheight = Math.round(height * scale);
}
if (!cmwidth) {
cmwidth = Math.round(width * scale);
}
ys = height / cmheight;
xs = width / cmwidth;
for (y = 0; y < bmp.length; y += ys) {
line = [];
yy = Math.round(y);
if (!bmp[yy]) break;
for (x = 0; x < bmp[yy].length; x += xs) {
xx = Math.round(x);
if (!bmp[yy][xx]) break;
line.push(bmp[yy][xx]);
}
cellmap.push(line);
}
return cellmap;
};
PNG.prototype.renderANSI = function(bmp) {
var self = this
, out = '';
bmp.forEach(function(line, y) {
line.forEach(function(pixel, x) {
var outch = self.getOutch(x, y, line, pixel);
out += self.pixelToSGR(pixel, outch);
});
out += '\n';
});
return out;
};
PNG.prototype.renderContent = function(bmp, el) {
var self = this
, out = '';
bmp.forEach(function(line, y) {
line.forEach(function(pixel, x) {
var outch = self.getOutch(x, y, line, pixel);
out += self.pixelToTags(pixel, outch);
});
out += '\n';
});
el.setContent(out);
return out;
};
PNG.prototype.renderScreen = function(bmp, screen, xi, xl, yi, yl) {
var self = this
, lines = screen.lines
, cellLines
, y
, yy
, x
, xx
, alpha
, attr
, ch;
cellLines = bmp.reduce(function(cellLines, line, y) {
var cellLine = [];
line.forEach(function(pixel, x) {
var outch = self.getOutch(x, y, line, pixel)
, cell = self.pixelToCell(pixel, outch);
cellLine.push(cell);
});
cellLines.push(cellLine);
return cellLines;
}, []);
for (y = yi; y < yl; y++) {
yy = y - yi;
for (x = xi; x < xl; x++) {
xx = x - xi;
if (lines[y] && lines[y][x] && cellLines[yy] && cellLines[yy][xx]) {
alpha = cellLines[yy][xx].pop();
// completely transparent
if (alpha === 0.0) {
continue;
}
// translucency / blending
if (alpha < 1.0) {
attr = cellLines[yy][xx][0];
ch = cellLines[yy][xx][1];
lines[y][x][0] = this.colors.blend(lines[y][x][0], attr, alpha);
if (ch !== ' ') lines[y][x][1] = ch;
lines[y].dirty = true;
continue;
}
// completely opaque
lines[y][x] = cellLines[yy][xx];
lines[y].dirty = true;
}
}
}
};
PNG.prototype.renderElement = function(bmp, el) {
var xi = el.aleft + el.ileft
, xl = el.aleft + el.width - el.iright
, yi = el.atop + el.itop
, yl = el.atop + el.height - el.ibottom;
return this.renderScreen(bmp, el.screen, xi, xl, yi, yl);
};
PNG.prototype.pixelToSGR = function(pixel, ch) {
var bga = 1.0
, fga = 0.5
, a = pixel.a / 255
, bg
, fg;
bg = this.colors.match(
pixel.r * a * bga | 0,
pixel.g * a * bga | 0,
pixel.b * a * bga | 0);
if (ch && this.options.ascii) {
fg = this.colors.match(
pixel.r * a * fga | 0,
pixel.g * a * fga | 0,
pixel.b * a * fga | 0);
if (a === 0) {
return '\x1b[38;5;' + fg + 'm' + ch + '\x1b[m';
}
return '\x1b[38;5;' + fg + 'm\x1b[48;5;' + bg + 'm' + ch + '\x1b[m';
}
if (a === 0) return ' ';
return '\x1b[48;5;' + bg + 'm \x1b[m';
};
PNG.prototype.pixelToTags = function(pixel, ch) {
var bga = 1.0
, fga = 0.5
, a = pixel.a / 255
, bg
, fg;
bg = this.colors.RGBtoHex(
pixel.r * a * bga | 0,
pixel.g * a * bga | 0,
pixel.b * a * bga | 0);
if (ch && this.options.ascii) {
fg = this.colors.RGBtoHex(
pixel.r * a * fga | 0,
pixel.g * a * fga | 0,
pixel.b * a * fga | 0);
if (a === 0) {
return '{' + fg + '-fg}' + ch + '{/}';
}
return '{' + fg + '-fg}{' + bg + '-bg}' + ch + '{/}';
}
if (a === 0) return ' ';
return '{' + bg + '-bg} {/' + bg + '-bg}';
};
PNG.prototype.pixelToCell = function(pixel, ch) {
var bga = 1.0
, fga = 0.5
, a = pixel.a / 255
, bg
, fg;
bg = this.colors.match(
pixel.r * bga | 0,
pixel.g * bga | 0,
pixel.b * bga | 0);
if (ch && this.options.ascii) {
fg = this.colors.match(
pixel.r * fga | 0,
pixel.g * fga | 0,
pixel.b * fga | 0);
} else {
fg = 0x1ff;
ch = null;
}
// if (a === 0) bg = 0x1ff;
return [(0 << 18) | (fg << 9) | (bg << 0), ch || ' ', a];
};
// Taken from libcaca:
PNG.prototype.getOutch = (function() {
var dchars = '????8@8@#8@8##8#MKXWwz$&%x><\\/xo;+=|^-:i\'.`, `. ';
var luminance = function(pixel) {
var a = pixel.a / 255
, r = pixel.r * a
, g = pixel.g * a
, b = pixel.b * a
, l = 0.2126 * r + 0.7152 * g + 0.0722 * b;
return l / 255;
};
return function(x, y, line, pixel) {
var lumi = luminance(pixel)
, outch = dchars[lumi * (dchars.length - 1) | 0];
return outch;
};
})();
PNG.prototype.compileFrames = function(frames) {
return this.optimization === 'mem'
? this.compileFrames_lomem(frames)
: this.compileFrames_locpu(frames);
};
PNG.prototype.compileFrames_lomem = function(frames) {
if (!this.actl) return;
return frames.map(function(frame, i) {
this.width = frame.fctl.width;
this.height = frame.fctl.height;
var pixels = frame._pixels || this.parseLines(frame.fdat)
, bmp = frame._bmp || this.createBitmap(pixels)
, fc = frame.fctl;
return {
actl: this.actl,
fctl: frame.fctl,
delay: (fc.delayNum / (fc.delayDen || 100)) * 1000 | 0,
bmp: bmp
};
}, this);
};
PNG.prototype.compileFrames_locpu = function(frames) {
if (!this.actl) return;
this._curBmp = null;
this._lastBmp = null;
return frames.map(function(frame, i) {
this.width = frame.fctl.width;
this.height = frame.fctl.height;
var pixels = frame._pixels || this.parseLines(frame.fdat)
, bmp = frame._bmp || this.createBitmap(pixels)
, renderBmp = this.renderFrame(bmp, frame, i)
, cellmap = this.createCellmap(renderBmp)
, fc = frame.fctl;
return {
actl: this.actl,
fctl: frame.fctl,
delay: (fc.delayNum / (fc.delayDen || 100)) * 1000 | 0,
bmp: renderBmp,
cellmap: cellmap
};
}, this);
};
PNG.prototype.renderFrame = function(bmp, frame, i) {
var first = this.frames[0]
, last = this.frames[i - 1]
, fc = frame.fctl
, xo = fc.xOffset
, yo = fc.yOffset
, lxo
, lyo
, x
, y
, line
, p;
if (!this._curBmp) {
this._curBmp = [];
for (y = 0; y < first.fctl.height; y++) {
line = [];
for (x = 0; x < first.fctl.width; x++) {
p = bmp[y][x];
line.push({ r: p.r, g: p.g, b: p.b, a: p.a });
}
this._curBmp.push(line);
}
}
if (last && last.fctl.disposeOp !== 0) {
lxo = last.fctl.xOffset;
lyo = last.fctl.yOffset;
for (y = 0; y < last.fctl.height; y++) {
for (x = 0; x < last.fctl.width; x++) {
if (last.fctl.disposeOp === 0) {
// none / keep
} else if (last.fctl.disposeOp === 1) {
// background / clear
this._curBmp[lyo + y][lxo + x] = { r: 0, g: 0, b: 0, a: 0 };
} else if (last.fctl.disposeOp === 2) {
// previous / restore
p = this._lastBmp[y][x];
this._curBmp[lyo + y][lxo + x] = { r: p.r, g: p.g, b: p.b, a: p.a };
}
}
}
}
if (frame.fctl.disposeOp === 2) {
this._lastBmp = [];
for (y = 0; y < frame.fctl.height; y++) {
line = [];
for (x = 0; x < frame.fctl.width; x++) {
p = this._curBmp[yo + y][xo + x];
line.push({ r: p.r, g: p.g, b: p.b, a: p.a });
}
this._lastBmp.push(line);
}
} else {
this._lastBmp = null;
}
for (y = 0; y < frame.fctl.height; y++) {
for (x = 0; x < frame.fctl.width; x++) {
p = bmp[y][x];
if (fc.blendOp === 0) {
// source
this._curBmp[yo + y][xo + x] = { r: p.r, g: p.g, b: p.b, a: p.a };
} else if (fc.blendOp === 1) {
// over
if (p.a !== 0) {
this._curBmp[yo + y][xo + x] = { r: p.r, g: p.g, b: p.b, a: p.a };
}
}
}
}
return this._curBmp;
};
PNG.prototype._animate = function(callback) {
if (!this.frames) {
return callback(this.bmp, this.cellmap);
}
var self = this
, numPlays = this.actl.numPlays || Infinity
, running = 0
, i = -1;
this._curBmp = null;
this._lastBmp = null;
var next_lomem = function() {
if (!running) return;
var frame = self.frames[++i];
if (!frame) {
if (!--numPlays) return callback();
i = -1;
// XXX may be able to optimize by only setting the self._curBmp once???
self._curBmp = null;
self._lastBmp = null;
return setImmediate(next);
}
var bmp = frame.bmp
, renderBmp = self.renderFrame(bmp, frame, i)
, cellmap = self.createCellmap(renderBmp);
callback(renderBmp, cellmap);
return setTimeout(next, frame.delay / self.speed | 0);
};
var next_locpu = function() {
if (!running) return;
var frame = self.frames[++i];
if (!frame) {
if (!--numPlays) return callback();
i = -1;
return setImmediate(next);
}
callback(frame.bmp, frame.cellmap);
return setTimeout(next, frame.delay / self.speed | 0);
};
var next = this.optimization === 'mem'
? next_lomem
: next_locpu;
this._control = function(state) {
if (state === -1) {
i = -1;
self._curBmp = null;
self._lastBmp = null;
running = 0;
callback(self.frames[0].bmp,
self.frames[0].cellmap || self.createCellmap(self.frames[0].bmp));
return;
}
if (state === running) return;
running = state;
return next();
};
this._control(1);
};
PNG.prototype.play = function(callback) {
if (!this._control || callback) {
this.stop();
return this._animate(callback);
}
this._control(1);
};
PNG.prototype.pause = function() {
if (!this._control) return;
this._control(0);
};
PNG.prototype.stop = function() {
if (!this._control) return;
this._control(-1);
};
PNG.prototype.toPNG = function(input) {
var options = this.options
, file = this.file
, format = this.format
, buf
, img
, gif
, i
, control
, disposeOp;
if (format !== 'gif') {
buf = exec('convert', [format + ':-', 'png:-'],
{ stdio: ['pipe', 'pipe', 'ignore'], input: input });
img = PNG(buf, options);
img.file = file;
return img;
}
gif = GIF(input, options);
this.width = gif.width;
this.height = gif.height;
this.frames = [];
for (i = 0; i < gif.images.length; i++) {
img = gif.images[i];
// Convert from gif disposal to png disposal. See:
// http://www.w3.org/Graphics/GIF/spec-gif89a.txt
control = img.control || gif;
disposeOp = Math.max(0, (control.disposeMethod || 0) - 1);
if (disposeOp > 2) disposeOp = 0;
this.frames.push({
fctl: {
sequenceNumber: i,
width: img.width,
height: img.height,
xOffset: img.left,
yOffset: img.top,
delayNum: control.delay,
delayDen: 100,
disposeOp: disposeOp,
blendOp: 1
},
fdat: [],
_pixels: [],
_bmp: img.bmp
});
}
this.bmp = this.frames[0]._bmp;
this.cellmap = this.createCellmap(this.bmp);
if (this.frames.length > 1) {
this.actl = { numFrames: gif.images.length, numPlays: gif.numPlays || 0 };
this.frames = this.compileFrames(this.frames);
} else {
this.frames = undefined;
}
return this;
};
// Convert a gif to an apng using imagemagick. Unfortunately imagemagick
// doesn't support apngs, so we coalesce the gif frames into one image and then
// slice them into frames.
PNG.prototype.gifMagick = function(input) {
var options = this.options
, file = this.file
, format = this.format
, buf
, fmt
, img
, frames
, frame
, width
, height
, iwidth
, twidth
, i
, lines
, line
, x
, y;
buf = exec('convert',
[format + ':-', '-coalesce', '+append', 'png:-'],
{ stdio: ['pipe', 'pipe', 'ignore'], input: input });
fmt = '{"W":%W,"H":%H,"w":%w,"h":%h,"d":%T,"x":"%X","y":"%Y"},'
frames = exec('identify', ['-format', fmt, format + ':-'],
{ encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'], input: input });
frames = JSON.parse('[' + frames.trim().slice(0, -1) + ']');
img = PNG(buf, options);
img.file = file;
Object.keys(img).forEach(function(key) {
this[key] = img[key];
}, this);
width = frames[0].W;
height = frames[0].H;
iwidth = 0;
twidth = 0;
this.width = width;
this.height = height;
this.frames = [];
for (i = 0; i < frames.length; i++) {
frame = frames[i];
frame.x = +frame.x;
frame.y = +frame.y;
iwidth = twidth;
twidth += width;
lines = [];
for (y = frame.y; y < height; y++) {
line = [];
for (x = iwidth + frame.x; x < twidth; x++) {
line.push(img.bmp[y][x]);
}
lines.push(line);
}
this.frames.push({
fctl: {
sequenceNumber: i,
width: frame.w,
height: frame.h,
xOffset: frame.x,
yOffset: frame.y,
delayNum: frame.d,
delayDen: 100,
disposeOp: 0,
blendOp: 0
},
fdat: [],
_pixels: [],
_bmp: lines
});
}
this.bmp = this.frames[0]._bmp;
this.cellmap = this.createCellmap(this.bmp);
if (this.frames.length > 1) {
this.actl = { numFrames: frames.length, numPlays: 0 };
this.frames = this.compileFrames(this.frames);
} else {
this.frames = undefined;
}
return this;
};
PNG.prototype.decompress = function(buffers) {
return zlib.inflateSync(new Buffer(buffers.reduce(function(out, data) {
return out.concat(Array.prototype.slice.call(data));
}, [])));
};
/**
* node-crc
* https://github.com/alexgorbatchev/node-crc
* https://github.com/alexgorbatchev/node-crc/blob/master/LICENSE
*
* The MIT License (MIT)
*
* Copyright 2014 Alex Gorbatchev
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
PNG.prototype.crc32 = (function() {
var crcTable = [
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
];
return function crc32(buf) {
//var crc = previous === 0 ? 0 : ~~previous ^ -1;
var crc = -1;
for (var i = 0, len = buf.length; i < len; i++) {
crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
}
return crc ^ -1;
};
})();
PNG.prototype._debug = function() {
if (!this.options.log) return;
return this.options.log.apply(null, arguments);
};
/**
* GIF
*/
function GIF(file, options) {
var self = this;
if (!(this instanceof GIF)) {
return new GIF(file, options);
}
var info = {}
, p = 0
, buf
, i
, total
, sig
, desc
, img
, ext
, label
, size;
if (!file) throw new Error('no file');
options = options || {};
this.options = options;
// XXX If the gif is not optimized enough
// it may OOM the process with too many frames.
// TODO: Implement in PNG reader.
this.pixelLimit = this.options.pixelLimit || 7622550;
this.totalPixels = 0;
if (Buffer.isBuffer(file)) {
buf = file;
file = null;
} else {
file = path.resolve(process.cwd(), file);
buf = fs.readFileSync(file);
}
sig = buf.slice(0, 6).toString('ascii');
if (sig !== 'GIF87a' && sig !== 'GIF89a') {
throw new Error('bad header: ' + sig);
}
this.width = buf.readUInt16LE(6);
this.height = buf.readUInt16LE(8);
this.flags = buf.readUInt8(10);
this.gct = !!(this.flags & 0x80);
this.gctsize = (this.flags & 0x07) + 1;
this.bgIndex = buf.readUInt8(11);
this.aspect = buf.readUInt8(12);
p += 13;
if (this.gct) {
this.colors = [];
total = 1 << this.gctsize;
for (i = 0; i < total; i++, p += 3) {
this.colors.push([buf[p], buf[p + 1], buf[p + 2], 255]);
}
}
this.images = [];
this.extensions = [];
try {
while (p < buf.length) {
desc = buf.readUInt8(p);
p += 1;
if (desc === 0x2c) {
img = {};
img.left = buf.readUInt16LE(p);
p += 2;
img.top = buf.readUInt16LE(p);
p += 2;
img.width = buf.readUInt16LE(p);
p += 2;
img.height = buf.readUInt16LE(p);
p += 2;
img.flags = buf.readUInt8(p);
p += 1;
img.lct = !!(img.flags & 0x80);
img.ilace = !!(img.flags & 0x40);
img.lctsize = (img.flags & 0x07) + 1;
if (img.lct) {
img.lcolors = [];
total = 1 << img.lctsize;
for (i = 0; i < total; i++, p += 3) {
img.lcolors.push([buf[p], buf[p + 1], buf[p + 2], 255]);
}
}
img.codeSize = buf.readUInt8(p);
p += 1;
img.size = buf.readUInt8(p);
p += 1;
img.lzw = [buf.slice(p, p + img.size)];
p += img.size;
while (buf[p] !== 0x00) {
// Some gifs screw up their size.
// XXX Same for all subblocks?
if (buf[p] === 0x3b && p === buf.length - 1) {
p--;
break;
}
size = buf.readUInt8(p);
p += 1;
img.lzw.push(buf.slice(p, p + size));
p += size;
}
assert.equal(buf.readUInt8(p), 0x00);
p += 1;
if (ext && ext.label === 0xf9) {
img.control = ext;
}
this.totalPixels += img.width * img.height;
this.images.push(img);
if (this.totalPixels >= this.pixelLimit) {
break;
}
} else if (desc === 0x21) {
// Extensions:
// http://www.w3.org/Graphics/GIF/spec-gif89a.txt
ext = {};
label = buf.readUInt8(p);
p += 1;
ext.label = label;
if (label === 0xf9) {
size = buf.readUInt8(p);
assert.equal(size, 0x04);
p += 1;
ext.fields = buf.readUInt8(p);
ext.disposeMethod = (ext.fields >> 2) & 0x07;
ext.useTransparent = !!(ext.fields & 0x01);
p += 1;
ext.delay = buf.readUInt16LE(p);
p += 2;
ext.transparentColor = buf.readUInt8(p);
p += 1;
while (buf[p] !== 0x00) {
size = buf.readUInt8(p);
p += 1;
p += size;
}
assert.equal(buf.readUInt8(p), 0x00);
p += 1;
this.delay = ext.delay;
this.transparentColor = ext.transparentColor;
this.disposeMethod = ext.disposeMethod;
this.useTransparent = ext.useTransparent;
} else if (label === 0xff) {
// https://wiki.whatwg.org/wiki/GIF#Specifications
size = buf.readUInt8(p);
p += 1;
ext.id = buf.slice(p, p + 8).toString('ascii');
p += 8;
ext.auth = buf.slice(p, p + 3).toString('ascii');
p += 3;
ext.data = [];
while (buf[p] !== 0x00) {
size = buf.readUInt8(p);
p += 1;
ext.data.push(buf.slice(p, p + size));
p += size;
}
ext.data = new Buffer(ext.data.reduce(function(out, data) {
return out.concat(Array.prototype.slice.call(data));
}, []));
// AnimExts looping extension (identical to netscape)
if (ext.id === 'ANIMEXTS' && ext.auth === '1.0') {
ext.id = 'NETSCAPE';
ext.auth = '2.0';
ext.animexts = true;
}
// Netscape extensions
if (ext.id === 'NETSCAPE' && ext.auth === '2.0') {
if (ext.data.readUInt8(0) === 0x01) {
// Netscape looping extension
// http://graphcomp.com/info/specs/ani_gif.html
ext.numPlays = ext.data.readUInt16LE(1);
this.numPlays = ext.numPlays;
} else if (ext.data.readUInt8(0) === 0x02) {
// Netscape buffering extension
this.minBuffer = ext.data;
}
}
// Adobe XMP extension
if (ext.id === 'XMP Data' && ext.auth === 'XMP') {
ext.xmp = ext.data.toString('utf8');
this.xmp = ext.xmp;
}
// ICC extension
if (ext.id === 'ICCRGBG1' && ext.auth === '012') {
// NOTE: Says size is 4 bytes, not 1? Maybe just buffer size?
this.icc = ext.data;
}
// fractint extension
if (ext.id === 'fractint' && /^00[1-7]$/.test(ext.auth)) {
// NOTE: Says size is 4 bytes, not 1? Maybe just buffer size?
// Size: '!\377\013' == [0x00, 0x15, 0xff, 0x0b]
this.fractint = ext.data;
}
assert.equal(buf.readUInt8(p), 0x00);
p += 1;
} else {
ext.data = [];
while (buf[p] !== 0x00) {
size = buf.readUInt8(p);
p += 1;
ext.data.push(buf.slice(p, p + size));
p += size;
}
assert.equal(buf.readUInt8(p), 0x00);
p += 1;
}
this.extensions.push(ext);
} else if (desc === 0x3b) {
break;
} else if (p === buf.length - 1) {
// } else if (desc === 0x00 && p === buf.length - 1) {
break;
} else {
throw new Error('unknown block');
}
}
} catch (e) {
if (options.debug) {
throw e;
}
}
this.images = this.images.map(function(img, imageIndex) {
var control = img.control || this;
img.lzw = new Buffer(img.lzw.reduce(function(out, data) {
return out.concat(Array.prototype.slice.call(data));
}, []));
try {
img.data = this.decompress(img.lzw, img.codeSize);
} catch (e) {
if (options.debug) throw e;
return;
}
var interlacing = [
[ 0, 8 ],
[ 4, 8 ],
[ 2, 4 ],
[ 1, 2 ],
[ 0, 0 ]
];
var table = img.lcolors || this.colors
, row = 0
, col = 0
, ilp = 0
, p = 0
, b
, idx
, i
, y
, x
, line
, pixel;
img.samples = [];
// Rewritten version of:
// https://github.com/lbv/ka-cs-programs/blob/master/lib/gif-reader.js
for (;;) {
b = img.data[p++];
if (b == null) break;
idx = (row * img.width + col) * 4;
if (!table[b]) {
if (options.debug) throw new Error('bad samples');
table[b] = [0, 0, 0, 0];
}
img.samples[idx] = table[b][0];
img.samples[idx + 1] = table[b][1];
img.samples[idx + 2] = table[b][2];
img.samples[idx + 3] = table[b][3];
if (control.useTransparent && b === control.transparentColor) {
img.samples[idx + 3] = 0;
}
if (++col >= img.width) {
col = 0;
if (img.ilace) {
row += interlacing[ilp][1];
if (row >= img.height) {
row = interlacing[++ilp][0];
}
} else {
row++;
}
}
}
img.pixels = [];
for (i = 0; i < img.samples.length; i += 4) {
img.pixels.push(img.samples.slice(i, i + 4));
}
img.bmp = [];
for (y = 0, p = 0; y < img.height; y++) {
line = [];
for (x = 0; x < img.width; x++) {
pixel = img.pixels[p++];
if (!pixel) {
if (options.debug) throw new Error('no pixel');
line.push({ r: 0, g: 0, b: 0, a: 0 });
continue;
}
line.push({ r: pixel[0], g: pixel[1], b: pixel[2], a: pixel[3] });
}
img.bmp.push(line);
}
return img;
}, this).filter(Boolean);
if (!this.images.length) {
throw new Error('no image data or bad decompress');
}
}
// Rewritten version of:
// https://github.com/lbv/ka-cs-programs/blob/master/lib/gif-reader.js
GIF.prototype.decompress = function(input, codeSize) {
var bitDepth = codeSize + 1
, CC = 1 << codeSize
, EOI = CC + 1
, stack = []
, table = []
, ntable = 0
, oldCode = null
, buffer = 0
, nbuffer = 0
, p = 0
, buf = []
, bits
, read
, ans
, n
, code
, i
, K
, b
, maxElem;
for (;;) {
if (stack.length === 0) {
bits = bitDepth;
read = 0;
ans = 0;
while (read < bits) {
if (nbuffer === 0) {
if (p >= input.length) return buf;
buffer = input[p++];
nbuffer = 8;
}
n = Math.min(bits - read, nbuffer);
ans |= (buffer & ((1 << n) - 1)) << read;
read += n;
nbuffer -= n;
buffer >>= n;
}
code = ans;
if (code === EOI) {
break;
}
if (code === CC) {
table = [];
for (i = 0; i < CC; ++i) {
table[i] = [i, -1, i];
}
bitDepth = codeSize + 1;
maxElem = 1 << bitDepth;
ntable = CC + 2;
oldCode = null;
continue;
}
if (oldCode === null) {
oldCode = code;
buf.push(table[code][0]);
continue;
}
if (code < ntable) {
for (i = code; i >= 0; i = table[i][1]) {
stack.push(table[i][0]);
}
table[ntable++] = [
table[code][2],
oldCode,
table[oldCode][2]
];
} else {
K = table[oldCode][2];
table[ntable++] = [K, oldCode, K];
for (i = code; i >= 0; i = table[i][1]) {
stack.push(table[i][0]);
}
}
oldCode = code;
if (ntable === maxElem) {
maxElem = 1 << (++bitDepth);
if (bitDepth > 12) bitDepth = 12;
}
}
b = stack.pop();
if (b == null) break;
buf.push(b);
}
return buf;
};
/**
* Expose
*/
exports = PNG;
exports.png = PNG;
exports.gif = GIF;
module.exports = exports;
};
BundleModuleCode['term/widgets/overlayimage']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: sbosse (2017).
** $VERSION: 1.2.2
**
** $INFO:
*
* overlayimage.js - w3m image element for blessed
*
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var fs = Require('fs')
, cp = Require('child_process');
var helpers = Require('term/helpers');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
/**
* OverlayImage
* Good example of w3mimgdisplay commands:
* https://github.com/hut/ranger/blob/master/ranger/ext/img_display.py
*/
function OverlayImage(options) {
var self = this;
if (!instanceOf(this,Node)) {
return new OverlayImage(options);
}
options = options || {};
Box.call(this, options);
if (options.w3m) {
OverlayImage.w3mdisplay = options.w3m;
}
if (OverlayImage.hasW3MDisplay == null) {
if (fs.existsSync(OverlayImage.w3mdisplay)) {
OverlayImage.hasW3MDisplay = true;
} else if (options.search !== false) {
var file = helpers.findFile('/usr', 'w3mimgdisplay')
|| helpers.findFile('/lib', 'w3mimgdisplay')
|| helpers.findFile('/bin', 'w3mimgdisplay');
if (file) {
OverlayImage.hasW3MDisplay = true;
OverlayImage.w3mdisplay = file;
} else {
OverlayImage.hasW3MDisplay = false;
}
}
}
this.on('hide', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('show', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.on('detach', function() {
self._lastFile = self.file;
self.clearImage();
});
this.on('attach', function() {
if (!self._lastFile) return;
self.setImage(self._lastFile);
});
this.onScreenEvent('resize', function() {
self._needsRatio = true;
});
// Get images to overlap properly. Maybe not worth it:
// this.onScreenEvent('render', function() {
// self.screen.program.flush();
// if (!self._noImage) return;
// function display(el, next) {
// if (el.type === 'w3mimage' && el.file) {
// el.setImage(el.file, next);
// } else {
// next();
// }
// }
// function done(el) {
// el.children.forEach(recurse);
// }
// function recurse(el) {
// display(el, function() {
// var pending = el.children.length;
// el.children.forEach(function(el) {
// display(el, function() {
// if (!--pending) done(el);
// });
// });
// });
// }
// recurse(self.screen);
// });
this.onScreenEvent('render', function() {
self.screen.program.flush();
if (!self._noImage) {
self.setImage(self.file);
}
});
if (this.options.file || this.options.img) {
this.setImage(this.options.file || this.options.img);
}
}
//OverlayImage.prototype.__proto__ = Box.prototype;
inheritPrototype(OverlayImage,Box);
OverlayImage.prototype.type = 'overlayimage';
OverlayImage.w3mdisplay = '/usr/lib/w3m/w3mimgdisplay';
OverlayImage.prototype.spawn = function(file, args, opt, callback) {
var spawn = require('child_process').spawn
, ps;
opt = opt || {};
ps = spawn(file, args, opt);
ps.on('error', function(err) {
if (!callback) return;
return callback(err);
});
ps.on('exit', function(code) {
if (!callback) return;
if (code !== 0) return callback(new Error('Exit Code: ' + code));
return callback(null, code === 0);
});
return ps;
};
OverlayImage.prototype.setImage = function(img, callback) {
var self = this;
if (this._settingImage) {
this._queue = this._queue || [];
this._queue.push([img, callback]);
return;
}
this._settingImage = true;
var reset = function() {
self._settingImage = false;
self._queue = self._queue || [];
var item = self._queue.shift();
if (item) {
self.setImage(item[0], item[1]);
}
};
if (OverlayImage.hasW3MDisplay === false) {
reset();
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
if (!img) {
reset();
if (!callback) return;
return callback(new Error('No image.'));
}
this.file = img;
return this.getPixelRatio(function(err, ratio) {
if (err) {
reset();
if (!callback) return;
return callback(err);
}
return self.renderImage(img, ratio, function(err, success) {
if (err) {
reset();
if (!callback) return;
return callback(err);
}
if (self.shrink || self.options.autofit) {
delete self.shrink;
delete self.options.shrink;
self.options.autofit = true;
return self.imageSize(function(err, size) {
if (err) {
reset();
if (!callback) return;
return callback(err);
}
if (self._lastSize
&& ratio.tw === self._lastSize.tw
&& ratio.th === self._lastSize.th
&& size.width === self._lastSize.width
&& size.height === self._lastSize.height
&& self.aleft === self._lastSize.aleft
&& self.atop === self._lastSize.atop) {
reset();
if (!callback) return;
return callback(null, success);
}
self._lastSize = {
tw: ratio.tw,
th: ratio.th,
width: size.width,
height: size.height,
aleft: self.aleft,
atop: self.atop
};
self.position.width = size.width / ratio.tw | 0;
self.position.height = size.height / ratio.th | 0;
self._noImage = true;
self.screen.render();
self._noImage = false;
reset();
return self.renderImage(img, ratio, callback);
});
}
reset();
if (!callback) return;
return callback(null, success);
});
});
};
OverlayImage.prototype.renderImage = function(img, ratio, callback) {
var self = this;
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.renderImageSync(img, ratio));
} catch (e) {
return callback(e);
}
}
if (OverlayImage.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
if (!ratio) {
if (!callback) return;
return callback(new Error('No ratio.'));
}
// clearImage unsets these:
var _file = self.file;
var _lastSize = self._lastSize;
return self.clearImage(function(err) {
if (err) return callback(err);
self.file = _file;
self._lastSize = _lastSize;
var opt = {
stdio: 'pipe',
env: process.env,
cwd: process.env.HOME
};
var ps = self.spawn(OverlayImage.w3mdisplay, [], opt, function(err, success) {
if (!callback) return;
return err
? callback(err)
: callback(null, success);
});
var width = self.width * ratio.tw | 0
, height = self.height * ratio.th | 0
, aleft = self.aleft * ratio.tw | 0
, atop = self.atop * ratio.th | 0;
var input = '0;1;'
+ aleft + ';'
+ atop + ';'
+ width + ';'
+ height + ';;;;;'
+ img
+ '\n4;\n3;\n';
self._props = {
aleft: aleft,
atop: atop,
width: width,
height: height
};
ps.stdin.write(input);
ps.stdin.end();
});
};
OverlayImage.prototype.clearImage = function(callback) {
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.clearImageSync());
} catch (e) {
return callback(e);
}
}
if (OverlayImage.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
if (!this._props) {
if (!callback) return;
return callback(null);
}
var opt = {
stdio: 'pipe',
env: process.env,
cwd: process.env.HOME
};
var ps = this.spawn(OverlayImage.w3mdisplay, [], opt, function(err, success) {
if (!callback) return;
return err
? callback(err)
: callback(null, success);
});
var width = this._props.width + 2
, height = this._props.height + 2
, aleft = this._props.aleft
, atop = this._props.atop;
if (this._drag) {
aleft -= 10;
atop -= 10;
width += 10;
height += 10;
}
var input = '6;'
+ aleft + ';'
+ atop + ';'
+ width + ';'
+ height
+ '\n4;\n3;\n';
delete this.file;
delete this._props;
delete this._lastSize;
ps.stdin.write(input);
ps.stdin.end();
};
OverlayImage.prototype.imageSize = function(callback) {
var img = this.file;
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.imageSizeSync());
} catch (e) {
return callback(e);
}
}
if (OverlayImage.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
if (!img) {
if (!callback) return;
return callback(new Error('No image.'));
}
var opt = {
stdio: 'pipe',
env: process.env,
cwd: process.env.HOME
};
var ps = this.spawn(OverlayImage.w3mdisplay, [], opt);
var buf = '';
ps.stdout.setEncoding('utf8');
ps.stdout.on('data', function(data) {
buf += data;
});
ps.on('error', function(err) {
if (!callback) return;
return callback(err);
});
ps.on('exit', function() {
if (!callback) return;
var size = buf.trim().split(/\s+/);
return callback(null, {
raw: buf.trim(),
width: +size[0],
height: +size[1]
});
});
var input = '5;' + img + '\n';
ps.stdin.write(input);
ps.stdin.end();
};
OverlayImage.prototype.termSize = function(callback) {
var self = this;
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.termSizeSync());
} catch (e) {
return callback(e);
}
}
if (OverlayImage.hasW3MDisplay === false) {
if (!callback) return;
return callback(new Error('W3M Image Display not available.'));
}
var opt = {
stdio: 'pipe',
env: process.env,
cwd: process.env.HOME
};
var ps = this.spawn(OverlayImage.w3mdisplay, ['-test'], opt);
var buf = '';
ps.stdout.setEncoding('utf8');
ps.stdout.on('data', function(data) {
buf += data;
});
ps.on('error', function(err) {
if (!callback) return;
return callback(err);
});
ps.on('exit', function() {
if (!callback) return;
if (!buf.trim()) {
// Bug: w3mimgdisplay will sometimes
// output nothing. Try again:
return self.termSize(callback);
}
var size = buf.trim().split(/\s+/);
return callback(null, {
raw: buf.trim(),
width: +size[0],
height: +size[1]
});
});
ps.stdin.end();
};
OverlayImage.prototype.getPixelRatio = function(callback) {
var self = this;
if (cp.execSync) {
callback = callback || function(err, result) { return result; };
try {
return callback(null, this.getPixelRatioSync());
} catch (e) {
return callback(e);
}
}
// XXX We could cache this, but sometimes it's better
// to recalculate to be pixel perfect.
if (this._ratio && !this._needsRatio) {
return callback(null, this._ratio);
}
return this.termSize(function(err, dimensions) {
if (err) return callback(err);
self._ratio = {
tw: dimensions.width / self.screen.width,
th: dimensions.height / self.screen.height
};
self._needsRatio = false;
return callback(null, self._ratio);
});
};
OverlayImage.prototype.renderImageSync = function(img, ratio) {
if (OverlayImage.hasW3MDisplay === false) {
throw new Error('W3M Image Display not available.');
}
if (!ratio) {
throw new Error('No ratio.');
}
// clearImage unsets these:
var _file = this.file;
var _lastSize = this._lastSize;
this.clearImageSync();
this.file = _file;
this._lastSize = _lastSize;
var width = this.width * ratio.tw | 0
, height = this.height * ratio.th | 0
, aleft = this.aleft * ratio.tw | 0
, atop = this.atop * ratio.th | 0;
var input = '0;1;'
+ aleft + ';'
+ atop + ';'
+ width + ';'
+ height + ';;;;;'
+ img
+ '\n4;\n3;\n';
this._props = {
aleft: aleft,
atop: atop,
width: width,
height: height
};
try {
cp.execFileSync(OverlayImage.w3mdisplay, [], {
env: process.env,
encoding: 'utf8',
input: input,
timeout: 1000
});
} catch (e) {
;
}
return true;
};
OverlayImage.prototype.clearImageSync = function() {
if (OverlayImage.hasW3MDisplay === false) {
throw new Error('W3M Image Display not available.');
}
if (!this._props) {
return false;
}
var width = this._props.width + 2
, height = this._props.height + 2
, aleft = this._props.aleft
, atop = this._props.atop;
if (this._drag) {
aleft -= 10;
atop -= 10;
width += 10;
height += 10;
}
var input = '6;'
+ aleft + ';'
+ atop + ';'
+ width + ';'
+ height
+ '\n4;\n3;\n';
delete this.file;
delete this._props;
delete this._lastSize;
try {
cp.execFileSync(OverlayImage.w3mdisplay, [], {
env: process.env,
encoding: 'utf8',
input: input,
timeout: 1000
});
} catch (e) {
;
}
return true;
};
OverlayImage.prototype.imageSizeSync = function() {
var img = this.file;
if (OverlayImage.hasW3MDisplay === false) {
throw new Error('W3M Image Display not available.');
}
if (!img) {
throw new Error('No image.');
}
var buf = '';
var input = '5;' + img + '\n';
try {
buf = cp.execFileSync(OverlayImage.w3mdisplay, [], {
env: process.env,
encoding: 'utf8',
input: input,
timeout: 1000
});
} catch (e) {
;
}
var size = buf.trim().split(/\s+/);
return {
raw: buf.trim(),
width: +size[0],
height: +size[1]
};
};
OverlayImage.prototype.termSizeSync = function(_, recurse) {
if (OverlayImage.hasW3MDisplay === false) {
throw new Error('W3M Image Display not available.');
}
var buf = '';
try {
buf = cp.execFileSync(OverlayImage.w3mdisplay, ['-test'], {
env: process.env,
encoding: 'utf8',
timeout: 1000
});
} catch (e) {
;
}
if (!buf.trim()) {
// Bug: w3mimgdisplay will sometimes
// output nothing. Try again:
recurse = recurse || 0;
if (++recurse === 5) {
throw new Error('Term size not determined.');
}
return this.termSizeSync(_, recurse);
}
var size = buf.trim().split(/\s+/);
return {
raw: buf.trim(),
width: +size[0],
height: +size[1]
};
};
OverlayImage.prototype.getPixelRatioSync = function() {
// XXX We could cache this, but sometimes it's better
// to recalculate to be pixel perfect.
if (this._ratio && !this._needsRatio) {
return this._ratio;
}
this._needsRatio = false;
var dimensions = this.termSizeSync();
this._ratio = {
tw: dimensions.width / this.screen.width,
th: dimensions.height / this.screen.height
};
return this._ratio;
};
OverlayImage.prototype.displayImage = function(callback) {
return this.screen.displayImage(this.file, callback);
};
/**
* Expose
*/
module.exports = OverlayImage;
};
BundleModuleCode['term/widgets/video']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey and contributors, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: sbosse (2017).
** $VERSION: 1.2.2
**
** $INFO:
*
* video.js - video element for blessed
*
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var cp = Require('child_process');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var Terminal = Require('term/widgets/terminal');
/**
* Video
*/
function Video(options) {
var self = this
, shell
, args;
if (!instanceOf(this,Node)) {
return new Video(options);
}
options = options || {};
Box.call(this, options);
if (this.exists('mplayer')) {
shell = 'mplayer';
args = ['-vo', 'caca', '-quiet', options.file];
} else if (this.exists('mpv')) {
shell = 'mpv';
args = ['--vo', 'caca', '--really-quiet', options.file];
} else {
this.parseTags = true;
this.setContent('{red-fg}{bold}Error:{/bold}'
+ ' mplayer or mpv not installed.{/red-fg}');
return this;
}
var opts = {
parent: this,
left: 0,
top: 0,
width: this.width - this.iwidth,
height: this.height - this.iheight,
shell: shell,
args: args.slice()
};
this.now = Date.now() / 1000 | 0;
this.start = opts.start || 0;
if (this.start) {
if (shell === 'mplayer') {
opts.args.unshift('-ss', this.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', this.start + '');
}
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
this.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
this.on('click', function() {
self.tty.pty.write('p');
});
// mplayer/mpv cannot resize itself in the terminal, so we have
// to restart it at the correct start time.
this.on('resize', function() {
self.tty.destroy();
var opts = {
parent: self,
left: 0,
top: 0,
width: self.width - self.iwidth,
height: self.height - self.iheight,
shell: shell,
args: args.slice()
};
var watched = (Date.now() / 1000 | 0) - self.now;
self.now = Date.now() / 1000 | 0;
self.start += watched;
if (shell === 'mplayer') {
opts.args.unshift('-ss', self.start + '');
} else if (shell === 'mpv') {
opts.args.unshift('--start', self.start + '');
}
var DISPLAY = process.env.DISPLAY;
delete process.env.DISPLAY;
self.tty = new Terminal(opts);
process.env.DISPLAY = DISPLAY;
self.screen.render();
});
}
//Video.prototype.__proto__ = Box.prototype;
inheritPrototype(Video,Box);
Video.prototype.type = 'video';
Video.prototype.exists = function(program) {
try {
return !!+cp.execSync('type '
+ program + ' > /dev/null 2> /dev/null'
+ ' && echo 1', { encoding: 'utf8' }).trim();
} catch (e) {
return false;
}
};
/**
* Expose
*/
module.exports = Video;
};
BundleModuleCode['term/widgets/layout']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Christopher Jeffrey, Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse
** $REVESIO: 1.2.1
**
** $INFO:
**
** layout.js - layout element for blessed
**
** $ENDOFINFO
*/
/**
* Modules
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Element = Require('term/widgets/element');
/**
* Layout
*/
function Layout(options) {
if (!instanceOf(this,Node)) {
return new Layout(options);
}
options = options || {};
if ((options.width == null
&& (options.left == null && options.right == null))
|| (options.height == null
&& (options.top == null && options.bottom == null))) {
throw new Error('`Layout` must have a width and height!');
}
options.layout = options.layout || 'inline';
Element.call(this, options);
if (options.renderer) {
this.renderer = options.renderer;
}
}
//Layout.prototype.__proto__ = Element.prototype;
inheritPrototype(Layout,Element);
Layout.prototype.type = 'layout';
Layout.prototype.isRendered = function(el) {
if (!el.lpos) return false;
return (el.lpos.xl - el.lpos.xi) > 0
&& (el.lpos.yl - el.lpos.yi) > 0;
};
Layout.prototype.getLast = function(i) {
while (this.children[--i]) {
var el = this.children[i];
if (this.isRendered(el)) return el;
}
};
Layout.prototype.getLastCoords = function(i) {
var last = this.getLast(i);
if (last) return last.lpos;
};
Layout.prototype._renderCoords = function() {
var coords = this._getCoords(true);
var children = this.children;
this.children = [];
this._render();
this.children = children;
return coords;
};
Layout.prototype.renderer = function(coords) {
var self = this;
// The coordinates of the layout element
var width = coords.xl - coords.xi
, height = coords.yl - coords.yi
, xi = coords.xi
, yi = coords.yi;
// The current row offset in cells (which row are we on?)
var rowOffset = 0;
// The index of the first child in the row
var rowIndex = 0;
var lastRowIndex = 0;
// Figure out the highest width child
if (this.options.layout === 'grid') {
var highWidth = this.children.reduce(function(out, el) {
out = Math.max(out, el.width);
return out;
}, 0);
}
return function iterator(el, i) {
// Make our children shrinkable. If they don't have a height, for
// example, calculate it for them.
el.shrink = true;
// Find the previous rendered child's coordinates
var last = self.getLast(i);
// If there is no previously rendered element, we are on the first child.
if (!last) {
el.position.left = 0;
el.position.top = 0;
} else {
// Otherwise, figure out where to place this child. We'll start by
// setting it's `left`/`x` coordinate to right after the previous
// rendered element. This child will end up directly to the right of it.
el.position.left = last.lpos.xl - xi;
// Make sure the position matches the highest width element
if (self.options.layout === 'grid') {
// Compensate with width:
// el.position.width = el.width + (highWidth - el.width);
// Compensate with position:
el.position.left += highWidth - (last.lpos.xl - last.lpos.xi);
}
// If our child does not overlap the right side of the Layout, set it's
// `top`/`y` to the current `rowOffset` (the coordinate for the current
// row).
if (el.position.left + el.width <= width) {
el.position.top = rowOffset;
} else {
// Otherwise we need to start a new row and calculate a new
// `rowOffset` and `rowIndex` (the index of the child on the current
// row).
rowOffset += self.children.slice(rowIndex, i).reduce(function(out, el) {
if (!self.isRendered(el)) return out;
out = Math.max(out, el.lpos.yl - el.lpos.yi);
return out;
}, 0);
lastRowIndex = rowIndex;
rowIndex = i;
el.position.left = 0;
el.position.top = rowOffset;
}
}
// Make sure the elements on lower rows graviatate up as much as possible
if (self.options.layout === 'inline') {
var above = null;
var abovea = Infinity;
for (var j = lastRowIndex; j < rowIndex; j++) {
var l = self.children[j];
if (!self.isRendered(l)) continue;
var abs = Math.abs(el.position.left - (l.lpos.xi - xi));
// if (abs < abovea && (l.lpos.xl - l.lpos.xi) <= el.width) {
if (abs < abovea) {
above = l;
abovea = abs;
}
}
if (above) {
el.position.top = above.lpos.yl - yi;
}
}
// If our child overflows the Layout, do not render it!
// Disable this feature for now.
if (el.position.top + el.height > height) {
// Returning false tells blessed to ignore this child.
// return false;
}
};
};
Layout.prototype.render = function() {
this._emit('prerender');
var coords = this._renderCoords();
if (!coords) {
delete this.lpos;
return;
}
if (coords.xl - coords.xi <= 0) {
coords.xl = Math.max(coords.xl, coords.xi);
return;
}
if (coords.yl - coords.yi <= 0) {
coords.yl = Math.max(coords.yl, coords.yi);
return;
}
this.lpos = coords;
if (this.border) coords.xi++, coords.xl--, coords.yi++, coords.yl--;
if (this.tpadding) {
coords.xi += this.padding.left, coords.xl -= this.padding.right;
coords.yi += this.padding.top, coords.yl -= this.padding.bottom;
}
var iterator = this.renderer(coords);
if (this.border) coords.xi--, coords.xl++, coords.yi--, coords.yl++;
if (this.tpadding) {
coords.xi -= this.padding.left, coords.xl += this.padding.right;
coords.yi -= this.padding.top, coords.yl += this.padding.bottom;
}
this.children.forEach(function(el, i) {
if (el.screen._ci !== -1) {
el.index = el.screen._ci++;
}
var rendered = iterator(el, i);
if (rendered === false) {
delete el.lpos;
return;
}
// if (el.screen._rendering) {
// el._rendering = true;
// }
el.render();
// if (el.screen._rendering) {
// el._rendering = false;
// }
});
this._emit('render', [coords]);
return coords;
};
/**
* Expose
*/
module.exports = Layout;
};
BundleModuleCode['term/widgets/tree']=function (module,exports){
/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR.
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2013-2015, Christopher Jeffrey and contributors
** $MODIFIED: by sbosse (2017-2018)
** $REVESIO: 1.3.1
**
** $INFO:
**
** Tree Widget
**
** Added:
** - 'preselect' event emission on node selection. The preselection
** event allows node children modificiation before screen rendering.
** - 'arrows', optional arrow buttons
**
** Events Out: preselect(node), select(node), selected(node)
**
** Node label text: node.name
** Node parent element: node.parent
** Get path:
** var path=node.name;
** node=node.parent;
** while(node)
** path=node.name+(node.name!='/'?'/':'')+path,
** node=node.parent;
**
**
** $ENDOFINFO
*/
var Comp = Require('com/compat');
var Node = Require('term/widgets/node');
var Box = Require('term/widgets/box');
var List = Require('term/widgets/list');
var Arrows = Require('term/widgets/arrows');
function Tree(options) {
var self=this,
height=0;
function strequal(str1,str2) {
var i;
var eq=true;
if (str1.length != str2.length) return false;
for(i=0;i<str1.length;i++) { if (str1.charAt(i)!=str2.charAt(i)) eq=false;}
return eq;
};
if (!instanceOf(this,Node)) {
return new Tree(options);
}
options = options || {};
if (!options.style) options.style = {
border: {
fg: 'black'
},
focus: {
border: {
fg: 'red'
}
}
};
if (!options.border) options.border = {
type:'line'
};
options.style.border._fg=options.style.border.fg; // save border style, can change
options.bold = true;
var self = this;
this.options = options;
this.data = {};
this.nodeLines = [];
this.lineNbr = 0;
this.init=true;
Box.call(this, options);
var boxfocus=this.focus;
if (options.height) {
if (typeof options.height == 'number') height=options.height;
else if (typeof options.height == 'string') {
var perc=0;
perc=parseInt(options.height);
height=this.screen.height*perc/100;
}
}
options.extended = options.extended || false;
options.keys = options.keys || ['space','enter'];
options.template = options.template || {};
options.template.extend = options.template.extend || ' [+]';
options.template.retract = options.template.retract || ' [-]';
options.template.lines = options.template.lines || true;
this.listOptions = {
height: 0,
top: 1,
width: 0,
left: 1,
selectedFg: 'white',
selectedBg: 'blue',
fg: "green",
keys: true ,
mouse:true ,
selectoffset:height>8?4:2,
};
if (!options.scrollbar) this.listOptions.scrollbar =
{
ch: ' ',
track: {
bg: 'yellow'
},
style: {
fg: 'cyan',
inverse: true
}
};
else if (options.scrollbar != null)
this.listOptions.scrollbar = options.scrollbar;
/*
** Tree content
*/
this.list = new List(this.listOptions);
this.list.key(options.keys,function(){
var ind = this.getItemIndex(this.selected);
var line = self.nodeLines[ind];
self.emit('preselect',line);
self.nodeLines[ind].extended = !self.nodeLines[ind].extended;
self.setData(self.data);
self.screen.render();
self.emit('select',line);
});
this.list.on('element click',function(w,ev){
var ind = this.getItemIndex(this.selected);
//console.log(ind)
var line = self.nodeLines[ind];
var pos = ev.x-w.aleft;
var item = this.ritems[ind];
if (item) {
self.emit('preselect',line);
var len1 = self.options.template.extend.length;
var len2 = self.options.template.retract.length;
var roi1 = item.indexOf(self.options.template.extend);
var roi2 = item.indexOf(self.options.template.retract);
if ((pos > roi1 && pos < roi1+len1) ||
(pos > roi2 && pos < roi2+len1)) {
self.nodeLines[ind].extended = !self.nodeLines[ind].extended;
self.setData(self.data);
self.screen.render();
self.emit('select',line);
}
}
});
if (options.arrows)
Arrows(
self,
options,
function () { self.list.select(self.list.selected - 2); self.screen.render()},
function () { self.list.select(self.list.selected + 2); self.screen.render()}
);
this.on('mousedown', function(data) {
self.focus();
Box.prototype.render.call(self);
self.screen.render();
});
// Propagate selection events of list items ...
this.list.on('selected', function() {
var ind = this.getItemIndex(this.selected);
var line = self.nodeLines[ind];
self.emit('selected',line);
});
this.append(this.list);
}
Tree.prototype.walk = function (node,treeDepth) {
var lines = [];
if (!node.parent)
node.parent = null;
if (treeDepth == '' && node.name) {
this.lineNbr = 0;
this.nodeLines[this.lineNbr++] = node;
lines.push(node.name);
treeDepth = ' ';
}
node.depth = treeDepth.length-1;
if (node.children && node.extended) {
var i = 0;
if (typeof node.children == 'function')
node.childrenContent = node.children(node);
if(!node.childrenContent)
node.childrenContent = node.children;
for (var child in node.childrenContent) {
if(!node.childrenContent[child].name)
node.childrenContent[child].name = child;
var childIndex = child;
child = node.childrenContent[child];
child.parent = node;
child.position = i++;
if(typeof child.extended == 'undefined')
child.extended = this.options.extended;
if (typeof child.children == 'function')
child.childrenContent = child.children(child);
else
child.childrenContent = child.children;
var isLastChild = child.position == Object.keys(child.parent.childrenContent).length - 1;
var tree;
var suffix = '';
if (isLastChild) {
tree = '└';
} else {
tree = '├';
}
if (!child.childrenContent || Object.keys(child.childrenContent).length == 0){
tree += '─';
} else if(child.extended) {
tree += '┬';
suffix = this.options.template.retract;
} else {
tree += '─';
suffix = this.options.template.extend;
}
if (!this.options.template.lines){
tree = '|-';
}
lines.push(treeDepth + tree + child.name + suffix);
this.nodeLines[this.lineNbr++] = child;
var parentTree;
if (isLastChild || !this.options.template.lines){
parentTree = treeDepth+" ";
} else {
parentTree = treeDepth+"│";
}
lines = lines.concat(this.walk(child, parentTree));
}
}
return lines;
}
Tree.prototype.focus = function(){
this.list.focus();
}
Tree.prototype.render = function() {
//console.log(this.style.border._fg)
if((this.screen.focused == this.list || this.screen.focused == this)) {
// List is focussed, propagate style changes to the Box element.
if (this.style.focus.border.fg) this.style.border.fg=this.style.focus.border.fg;
} else if ((this.screen.focused != this.list && this.screen.focused != this)) {
// List is not focussed, restore style changes of the Box element.
if (this.style.border._fg) this.style.border.fg=this.style.border._fg;
}
this.list.width = this.width-3;
this.list.height = this.height-3;
Box.prototype.render.call(this);
}
Tree.prototype.setData = function(data) {
var formatted = [];
formatted = this.walk(data,'');
this.data = data;
if (this.init) {
this.screen.render();
this.init=false;
};
this.list.setItems(formatted);
this.screen.render();
}
//Tree.prototype.__proto__ = Box.prototype;
inheritPrototype(Tree,Box);
Tree.prototype.type = 'tree';
module.exports = Tree
};
FilesEmbedded['term/def/xterm']=function (format){return Base64.decodeBuf('GgEcACYADwCdAWwFeHRlcm18WDExIHRlcm1pbmFsIGVtdWxhdG9yAAABAAABAAAAAQAAAAABAQAAAAAAAAABAAABAAABAAAAAAAAAAABUAAIABgA//////////////////////////8IAEAAAAAEAAYACAAZAB4AJgAqAC4A//85AEoATABQAFcA//9ZAGYA//9qAG4AeAB8AP////+AAIQAiQCOAP////+XAJwA//+hAKYAqwCwALkAvQDEAP//zQDSANgA3gD////////wAP///////wIB//8GAf///////wgB//8NAf//////////EQEVARsBHwEjAScBLQEzATkBPwFFAUkB//9OAf//UgFXAVwBYAFnAf//bgFyAXoB/////////////////////////////4IBiwH/////lAGdAaYBrwG4AcEBygHTAdwB5QH////////uAfIB9wH///wB/wH/////EQIUAh8CIgIkAicCeQL//3wC////////////////fgL//////////4IC//+3Av////+7AsEC/////////////////////////////8cCywL//////////////////////////////////////////////////////////////////88C/////9YC///////////dAuQC6wL/////8gL///kC////////AAP/////////////BwMNAxMDGgMhAygDLwM3Az8DRwNPA1cDXwNnA28DdgN9A4QDiwOTA5sDowOrA7MDuwPDA8sD0gPZA+AD5wPvA/cD/wMHBA8EFwQfBCcELgQ1BDwEQwRLBFMEWwRjBGsEcwR7BIMEigSRBJgE/////////////////////////////////////////////////////////////50EqAStBLUEuQT//////////8IECAX///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9OBf///////1IFXAX/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////ZgVpBRtbWgAHAA0AG1slaSVwMSVkOyVwMiVkcgAbWzNnABtbSBtbMkoAG1tLABtbSgAbWyVpJXAxJWRHABtbJWklcDElZDslcDIlZEgACgAbW0gAG1s/MjVsAAgAG1s/MTJsG1s/MjVoABtbQwAbW0EAG1s/MTI7MjVoABtbUAAbW00AGygwABtbNW0AG1sxbQAbWz8xMDQ5aAAbWzRoABtbOG0AG1s3bQAbWzdtABtbNG0AG1slcDElZFgAGyhCABsoQhtbbQAbWz8xMDQ5bAAbWzRsABtbMjdtABtbMjRtABtbPzVoJDwxMDAvPhtbPzVsABtbIXAbWz8zOzRsG1s0bBs+ABtbTAAIABtbM34AG09CABtPUAAbWzIxfgAbT1EAG09SABtPUwAbWzE1fgAbWzE3fgAbWzE4fgAbWzE5fgAbWzIwfgAbT0gAG1syfgAbT0QAG1s2fgAbWzV+ABtPQwAbWzE7MkIAG1sxOzJBABtPQQAbWz8xbBs+ABtbPzFoGz0AG1s/MTAzNGwAG1s/MTAzNGgAG1slcDElZFAAG1slcDElZE0AG1slcDElZEIAG1slcDElZEAAG1slcDElZFMAG1slcDElZEwAG1slcDElZEQAG1slcDElZEMAG1slcDElZFQAG1slcDElZEEAG1tpABtbNGkAG1s1aQAbYwAbWyFwG1s/Mzs0bBtbNGwbPgAbOAAbWyVpJXAxJWRkABs3AAoAG00AJT8lcDkldBsoMCVlGyhCJTsbWzAlPyVwNiV0OzElOyU/JXAyJXQ7NCU7JT8lcDElcDMlfCV0OzclOyU/JXA0JXQ7NSU7JT8lcDcldDs4JTttABtIAAkAG09FAGBgYWFmZmdnaWlqamtrbGxtbW5ub29wcHFxcnJzc3R0dXV2dnd3eHh5eXp6e3t8fH19fn4AG1taABtbPzdoABtbPzdsABtPRgAbT00AG1szOzJ+ABtbMTsyRgAbWzE7MkgAG1syOzJ+ABtbMTsyRAAbWzY7Mn4AG1s1OzJ+ABtbMTsyQwAbWzIzfgAbWzI0fgAbWzE7MlAAG1sxOzJRABtbMTsyUgAbWzE7MlMAG1sxNTsyfgAbWzE3OzJ+ABtbMTg7Mn4AG1sxOTsyfgAbWzIwOzJ+ABtbMjE7Mn4AG1syMzsyfgAbWzI0OzJ+ABtbMTs1UAAbWzE7NVEAG1sxOzVSABtbMTs1UwAbWzE1OzV+ABtbMTc7NX4AG1sxODs1fgAbWzE5OzV+ABtbMjA7NX4AG1syMTs1fgAbWzIzOzV+ABtbMjQ7NX4AG1sxOzZQABtbMTs2UQAbWzE7NlIAG1sxOzZTABtbMTU7Nn4AG1sxNzs2fgAbWzE4OzZ+ABtbMTk7Nn4AG1syMDs2fgAbWzIxOzZ+ABtbMjM7Nn4AG1syNDs2fgAbWzE7M1AAG1sxOzNRABtbMTszUgAbWzE7M1MAG1sxNTszfgAbWzE3OzN+ABtbMTg7M34AG1sxOTszfgAbWzIwOzN+ABtbMjE7M34AG1syMzszfgAbWzI0OzN+ABtbMTs0UAAbWzE7NFEAG1sxOzRSABtbMUsAG1slaSVkOyVkUgAbWzZuABtbPzE7MmMAG1tjABtbMzk7NDltABtbMyU/JXAxJXsxfSU9JXQ0JWUlcDElezN9JT0ldDYlZSVwMSV7NH0lPSV0MSVlJXAxJXs2fSU9JXQzJWUlcDElZCU7bQAbWzQlPyVwMSV7MX0lPSV0NCVlJXAxJXszfSU9JXQ2JWUlcDElezR9JT0ldDElZSVwMSV7Nn0lPSV0MyVlJXAxJWQlO20AG1tNABtbMyVwMSVkbQAbWzQlcDElZG0AG2wAG20AAgAAAD4AfgDvAgEBAAAHABMAGQArADEAOwBCAEkAUABXAF4AZQBsAHMAegCBAIgAjwCWAJ0ApACrALIAuQDAAMcAzgDVANwA4wDqAPEA+AD/AAYBDQEUARsBIgEpATABNwE+AUUBTAFTAVoBYQFoAW8BdgF9AYQBiwGSAZkBoAH//////////wAAAwAGAAkADAAPABIAFQAYAB0AIgAnACwAMQA1ADoAPwBEAEkATgBUAFoAYABmAGwAcgB4AH4AhACKAI8AlACZAJ4AowCpAK8AtQC7AMEAxwDNANMA2QDfAOUA6wDxAPcA/QADAQkBDwEVARsBHwEkASkBLgEzATgBPAFAAUQBG10xMTIHABtdMTI7JXAxJXMHABtbMztKABtdNTI7JXAxJXM7JXAyJXMHABtbMiBxABtbJXAxJWQgcQAbWzM7M34AG1szOzR+ABtbMzs1fgAbWzM7Nn4AG1szOzd+ABtbMTsyQgAbWzE7M0IAG1sxOzRCABtbMTs1QgAbWzE7NkIAG1sxOzdCABtbMTszRgAbWzE7NEYAG1sxOzVGABtbMTs2RgAbWzE7N0YAG1sxOzNIABtbMTs0SAAbWzE7NUgAG1sxOzZIABtbMTs3SAAbWzI7M34AG1syOzR+ABtbMjs1fgAbWzI7Nn4AG1syOzd+ABtbMTszRAAbWzE7NEQAG1sxOzVEABtbMTs2RAAbWzE7N0QAG1s2OzN+ABtbNjs0fgAbWzY7NX4AG1s2OzZ+ABtbNjs3fgAbWzU7M34AG1s1OzR+ABtbNTs1fgAbWzU7Nn4AG1s1Ozd+ABtbMTszQwAbWzE7NEMAG1sxOzVDABtbMTs2QwAbWzE7N0MAG1sxOzJBABtbMTszQQAbWzE7NEEAG1sxOzVBABtbMTs2QQAbWzE7N0EAQVgAWFQAQ3IAQ3MARTMATXMAU2UAU3MAa0RDMwBrREM0AGtEQzUAa0RDNgBrREM3AGtETgBrRE4zAGtETjQAa0RONQBrRE42AGtETjcAa0VORDMAa0VORDQAa0VORDUAa0VORDYAa0VORDcAa0hPTTMAa0hPTTQAa0hPTTUAa0hPTTYAa0hPTTcAa0lDMwBrSUM0AGtJQzUAa0lDNgBrSUM3AGtMRlQzAGtMRlQ0AGtMRlQ1AGtMRlQ2AGtMRlQ3AGtOWFQzAGtOWFQ0AGtOWFQ1AGtOWFQ2AGtOWFQ3AGtQUlYzAGtQUlY0AGtQUlY1AGtQUlY2AGtQUlY3AGtSSVQzAGtSSVQ0AGtSSVQ1AGtSSVQ2AGtSSVQ3AGtVUABrVVAzAGtVUDQAa1VQNQBrVVA2AGtVUDcAa2EyAGtiMQBrYjMAa2MyAA==')};
FilesEmbedded['term/def/windows-ansi']=function (format){return Base64.decodeBuf('GgEoACYAEAB9AUQCYW5zaXxhbnNpL3BjLXRlcm0gY29tcGF0aWJsZSB3aXRoIGNvbG9yAAABAAAAAAAAAAAAAAABAQAAAAAAAAABAAAAAAAAAAAAAAAAAAABUAAIABgA//////////////////////////8IAEAAAwAAAAQABgD//wgADQAUABgAHAD//ycAOAA8AP//QAD/////RAD//0gA//9MAFAA/////1QAWgBfAP//////////ZAD//2kAbgBzAHgAgQCHAP///////48AkwD/////////////////////lwD//5sA/////////////50A/////////////////////////////////////6EApQD//6kA////////rQD///////+xAP///////////////////////////////////////7UA//+6AMMAzADVAN4A5wDwAPkAAgELAf//////////FAEZAR4B/////////////zIB//89Af//PwGVAf//mAH/////////////////////////////nAH//9sB////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////3wH/////////////////////////////////////////////////////////////5AHvAfQBBwILAv//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////FAIeAv///////ygCLAIwAjQC/////////////////////////////zgCPgIbW1oABwANABtbMmcAG1tIG1tKABtbSwAbW0oAG1slaSVwMSVkRwAbWyVpJXAxJWQ7JXAyJWRIABtbQgAbW0gAG1tEABtbQwAbW0EAG1tQABtbTQAbWzExbQAbWzVtABtbMW0AG1s4bQAbWzdtABtbN20AG1s0bQAbWyVwMSVkWAAbWzEwbQAbWzA7MTBtABtbbQAbW20AG1tMAAgAG1tCABtbSAAbW0wAG1tEABtbQwAbW0EADRtbUwAbWyVwMSVkUAAbWyVwMSVkTQAbWyVwMSVkQgAbWyVwMSVkQAAbWyVwMSVkUwAbWyVwMSVkTAAbWyVwMSVkRAAbWyVwMSVkQwAbWyVwMSVkVAAbWyVwMSVkQQAbWzRpABtbNWkAJXAxJWMbWyVwMiV7MX0lLSVkYgAbWyVpJXAxJWRkAAoAG1swOzEwJT8lcDEldDs3JTslPyVwMiV0OzQlOyU/JXAzJXQ7NyU7JT8lcDQldDs1JTslPyVwNiV0OzElOyU/JXA3JXQ7OCU7JT8lcDkldDsxMSU7bQAbSAAbW0kAKxAsES0YLhkw22AEYbGm+C3xaLBq2Wu/bNptwG7Fb35wxHHEcsRzX3TDdbR2wXfCeLN583rye+N82H2cfv4AG1taABtbMUsAG1slaSVkOyVkUgAbWzZuABtbPyVbOzAxMjM0NTY3ODldYwAbW2MAG1szOTs0OW0AG1szJXAxJWRtABtbNCVwMSVkbQAbKEIAGylCABsqQgAbK0IAG1sxMW0AG1sxMG0AAQAAAAAAAQADAAEAAABBWAA=')};
var Base64=Require('os/base64');
module.exports=Require('/home/sbosse/proj/jam/js/top/jamsh.js');