diff --git a/components/component-args.js b/components/component-args.js new file mode 100644 index 0000000..2fd2e55 --- /dev/null +++ b/components/component-args.js @@ -0,0 +1,82 @@ +var parseARGS = (kvKeys) => { + return exports.parse($ARGS, kvKeys); +}; + +var parse = (args, kvKeys) => { + var newArgs = []; + var keyValueMap = {}; + var keyFlagMap = {}; + + keyValueMap.vals = () => { + var ret = []; + for (var i = 0; i < arguments.length; i++) { + var v = keyValueMap[arguments[i]]; + if (v) { v.forEach((x) => { ret.push(x) } ); } + } + return ret; + }; + keyValueMap.val = () => { + for (var i = 0; i < arguments.length; i++) { + var v = keyValueMap[arguments[i]] && keyValueMap[arguments[i]][0]; + if (v) { return v; } + } + return null; + }; + keyFlagMap.flg = () => { + for (var i = 0; i < arguments.length; i++) { + var v = keyFlagMap[arguments[i]]; + if (v) { return true; } + } + return false; + }; + + var kvKeyMap = {}; (kvKeys || []).forEach((k) => { kvKeyMap[k] = 1; }); + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + var key = null; + if (arg.startsWith('--')) { + key = arg.substring(2); + } else if (arg.startsWith('-')) { + key = arg.substring(1); + if (key.length > 1) { + for (var x = 0; x < key.length; x++) { + var k = key.substring(x, 1); + kvKeyMap[k] = 1; + keyFlagMap[k] = 1; + } + continue; + } + } + if ((key != null) && ((i + 1) < args.length) || kvKeyMap[key]) { + if (kvKeyMap[key]) { + keyFlagMap[key] = 1; + } else { + keyValueMap[key] = keyValueMap[key] || []; + keyValueMap[key].push(args[++i]); + } + } else { + newArgs.push(arg); + } + } + return { + args: newArgs, + flags: keyFlagMap, + values: keyValueMap + }; +}; + +var parseDefARGs = (kvKeys) => { + var args = parseARGS(kvKeys); + var newArgs = args.args; + newArgs.flg = args.flags.flg; + newArgs.val = args.values.val; + newArgs.vals = args.values.vals; + return newArgs; +}; + + +if (typeof exports == 'object') { + exports.parse = parse; + exports.parseARGS = parseARGS; + exports.parseDefARGs = parseDefARGs; +} \ No newline at end of file diff --git a/components/component-bytes.js b/components/component-bytes.js new file mode 100644 index 0000000..527bdb3 --- /dev/null +++ b/components/component-bytes.js @@ -0,0 +1,17 @@ + +var UNITS = ['bytes', 'KB', 'MB', 'GB', 'TB']; +var UNITS_BYTES = [0, Math.pow(1024, 1), Math.pow(1024, 2), Math.pow(1024, 3), Math.pow(1024, 4)]; + +exports.showBytes = (bytes) => { + for (var i = 0; i < (UNITS.length - 1); i++) { + if ((bytes >= UNITS_BYTES[i]) && bytes < UNITS_BYTES[i + 1]) { + if (i == 0) { + return bytes + ((bytes <= 1) ? ' byte' : ' bytes'); + } else { + return (parseInt(bytes * 100 / UNITS_BYTES[i]) / 100) + ' ' + UNITS[i]; + } + } + } + + return (parseInt(bytes * 100 / UNITS_BYTES[i]) / 100) + ' ' + UNITS[i]; // i == UNITS.length - 1 +}; diff --git a/components/component-colorprint-ex.js b/components/component-colorprint-ex.js new file mode 100644 index 0000000..475e893 --- /dev/null +++ b/components/component-colorprint-ex.js @@ -0,0 +1,41 @@ + +var HEADER = '\033[95m'; +var OKBLUE = '\033[94m'; +var OKGREEN = '\033[92m'; +var WARNING = '\033[93m'; +var FAIL = '\033[91m'; +var BOLD = '\033[1m'; +var UNDERLINE = '\033[4m'; +var ENDC = '\033[0m'; + +var colorMapping = { + "header": HEADER, + "okblue": OKBLUE, + "okgreen": OKGREEN, + "warning": WARNING, + "fail": FAIL, + "bold": BOLD, + "underline": UNDERLINE, + "none": null // nothing special +}; + +var colorPrint = exports; + +for (var k in colorMapping) { + colorPrint[k] = {}; + colorPrint[k].render = ((k) => { + return (x) => { + return colorMapping[k] ? (colorMapping[k] + x + ENDC) : x; + }; + })(k); + colorPrint[k].print = ((k) => { + return (x) => { + print(colorPrint[k].render(x)); + }; + })(k); + colorPrint[k].println = ((k) => { + return (x) => { + println(colorPrint[k].render(x)); + }; + })(k); +} diff --git a/components/component-colorprint.js b/components/component-colorprint.js new file mode 100644 index 0000000..b5b8b9e --- /dev/null +++ b/components/component-colorprint.js @@ -0,0 +1,3 @@ +(() => { + colorPrint = require('component-colorprint-ex.js'); +})(); \ No newline at end of file diff --git a/components/component-console.js b/components/component-console.js new file mode 100644 index 0000000..5218e49 --- /dev/null +++ b/components/component-console.js @@ -0,0 +1,61 @@ +var System = java.lang.System; + +var readLineEx = (prompt) => { + var ret = null; + while ((ret == null) || $STR(ret).trim() == '') { + ret = readLine(prompt); + } + return ret; +}; + +var readLine = (prompt) => { + print(prompt); + return System.console().readLine(); +}; + +var readYNEx = (prompt) => { + var ret = null; + while (ret == null) { + ret = readYN(prompt); + } + return ret; +}; + +var readYN = (prompt) => { + print(prompt); + var read = readLine(); + if ($STR(read).toLowerCase() == 'y') { + return true; + } + if ($STR(read).toLowerCase() == 'n') { + return false; + } + return null; +}; + +var readListToMark = (prompt, mark) => { + if (!(mark)) { throw 'Mark cannot be null or empty.'; } + print(prompt); + var list = []; + while (true) { + var ln = System.console().readLine(); + if ($STR(ln) == $STR(mark)) { + return list; + } + list.push(ln); + } +}; + +var readListToCustomMark = (prompt) => { + var mark = readLineEx(prompt); + return readListToMark('', mark); +}; + +if (typeof exports == 'object') { + exports.readYN = readYN; + exports.readYNEx = readYNEx; + exports.readLine = readLine; + exports.readLineEx = readLineEx; + exports.readListToMark = readListToMark; + exports.readListToCustomMark = readListToCustomMark; +} diff --git a/components/component-coordtransform.js b/components/component-coordtransform.js new file mode 100644 index 0000000..914ce1e --- /dev/null +++ b/components/component-coordtransform.js @@ -0,0 +1,137 @@ +// https://github.com/wandergis/coordtransform/blob/master/index.js +(() => { + /** + * Created by Wandergis on 2015/7/8. + * 提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换 + */ + //定义一些常量 + var x_PI = 3.14159265358979324 * 3000.0 / 180.0; + var PI = 3.1415926535897932384626; + var a = 6378245.0; + var ee = 0.00669342162296594323; + /** + * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换 + * 即 百度 转 谷歌、高德 + * @param bd_lon + * @param bd_lat + * @returns {*[]} + */ + var bd09togcj02 = function bd09togcj02(bd_lon, bd_lat) { + var bd_lon = +bd_lon; + var bd_lat = +bd_lat; + var x = bd_lon - 0.0065; + var y = bd_lat - 0.006; + var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI); + var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI); + var gg_lng = z * Math.cos(theta); + var gg_lat = z * Math.sin(theta); + return [gg_lng, gg_lat] + }; + + /** + * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换 + * 即谷歌、高德 转 百度 + * @param lng + * @param lat + * @returns {*[]} + */ + var gcj02tobd09 = function gcj02tobd09(lng, lat) { + var lat = +lat; + var lng = +lng; + var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI); + var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI); + var bd_lng = z * Math.cos(theta) + 0.0065; + var bd_lat = z * Math.sin(theta) + 0.006; + return [bd_lng, bd_lat] + }; + + /** + * WGS84转GCj02 + * @param lng + * @param lat + * @returns {*[]} + */ + var wgs84togcj02 = function wgs84togcj02(lng, lat) { + var lat = +lat; + var lng = +lng; + if (out_of_china(lng, lat)) { + return [lng, lat] + } else { + var dlat = transformlat(lng - 105.0, lat - 35.0); + var dlng = transformlng(lng - 105.0, lat - 35.0); + var radlat = lat / 180.0 * PI; + var magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + var sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + var mglat = lat + dlat; + var mglng = lng + dlng; + return [mglng, mglat] + } + }; + + /** + * GCJ02 转换为 WGS84 + * @param lng + * @param lat + * @returns {*[]} + */ + var gcj02towgs84 = function gcj02towgs84(lng, lat) { + var lat = +lat; + var lng = +lng; + if (out_of_china(lng, lat)) { + return [lng, lat] + } else { + var dlat = transformlat(lng - 105.0, lat - 35.0); + var dlng = transformlng(lng - 105.0, lat - 35.0); + var radlat = lat / 180.0 * PI; + var magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + var sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + var mglat = lat + dlat; + var mglng = lng + dlng; + return [lng * 2 - mglng, lat * 2 - mglat] + } + }; + + var transformlat = function transformlat(lng, lat) { + var lat = +lat; + var lng = +lng; + var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0; + return ret + }; + + var transformlng = function transformlng(lng, lat) { + var lat = +lat; + var lng = +lng; + var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0; + return ret + }; + + /** + * 判断是否在国内,不在国内则不做偏移 + * @param lng + * @param lat + * @returns {boolean} + */ + var out_of_china = function out_of_china(lng, lat) { + var lat = +lat; + var lng = +lng; + // 纬度3.86~53.55,经度73.66~135.05 + return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55); + }; + + exports.bd09togcj02 = bd09togcj02; + exports.gcj02tobd09 = gcj02tobd09; + exports.wgs84togcj02 = wgs84togcj02; + exports.gcj02towgs84 = gcj02towgs84; +})(); \ No newline at end of file diff --git a/components/component-cos.js b/components/component-cos.js new file mode 100644 index 0000000..cc3eb2d --- /dev/null +++ b/components/component-cos.js @@ -0,0 +1,68 @@ + +var counter = require('component-counter.js'); + +var requireJARs = () => { + $ONCE('component-cos-requireJARS').run(() => { + requireJAR('cos_api-5.2.2.jar'); + requireJAR('httpcore-4.4.6.jar'); + requireJAR('commons-codec-1.10.jar'); + requireJAR('commons-logging-1.2.jar'); + requireJAR('httpclient-4.5.3.jar'); + requireJAR('httpmime-4.5.2.jar'); + requireJAR('slf4j-api-1.7.21.jar'); + requireJAR('log4j-1.2.17.jar'); + requireJAR('slf4j-log4j12-1.7.21.jar'); + requireJAR('joda-time-2.9.6.jar'); + requireJAR('jackson-core-2.8.5.jar'); + requireJAR('jackson-annotations-2.8.0.jar'); + requireJAR('jackson-databind-2.8.5.jar'); + }); +}; + +var createCounterIS = (file) => { + var CounterInputStream = Packages.me.hatter.tools.commons.io.CounterInputStream; + var uploadFile = (file instanceof java.io.File) ? file : new java.io.File(file); + var uploadIS = new java.io.FileInputStream(uploadFile); + var counterIS = new CounterInputStream(uploadIS, counter.getCounterBar(uploadFile.length())); + return counterIS; +}; + +var putObject = (client, bucket, key, file) => { + var f = $$.file(file); + var objectMetadata = new Packages.com.qcloud.cos.model.ObjectMetadata(); + objectMetadata.setContentLength(f.length()); + var result = client.putObject(bucket, key, createCounterIS(f), objectMetadata); + return result; +}; + +// client -> https://cloud.tencent.com/document/product/436/10199 +// region -> https://cloud.tencent.com/document/product/436/6224 +var newClient = (region, appId, accessKey, secretKey) => { + requireJARs(); + + var COSClient = Packages.com.qcloud.cos.COSClient; + var Region = Packages.com.qcloud.cos.region.Region; + var ClientConfig = Packages.com.qcloud.cos.ClientConfig; + var BasicCOSCredentials = Packages.com.qcloud.cos.auth.BasicCOSCredentials; + var cred = new BasicCOSCredentials(appId, accessKey, secretKey); + var clientConfig = new ClientConfig(new Region(region)); + var client = new COSClient(cred, clientConfig); + return { + runWith: (runFunc) => { + try { + runFunc(client); + } catch (e) { + println('COS Error: ' + e); + } finally { + client.shutdown(); + } + } + }; +}; + +if (typeof exports == 'object') { + exports.requireJARs = requireJARs; + exports.createCounterIS = createCounterIS; + exports.putObject = putObject; + exports.newClient = newClient; +} diff --git a/components/component-counter.js b/components/component-counter.js new file mode 100644 index 0000000..7d1b12c --- /dev/null +++ b/components/component-counter.js @@ -0,0 +1,77 @@ +var bytesjs = require('component-bytes.js'); + +exports.getCounter = (fileLength, dotStep) => { + var total = 0; + var step = dotStep || (1024 * 64); + var percentPrintFlag = {}; + var percentN = (fileLength > (60 * 1024 * 1024)) ? 100 : 10; + return (len) => { + var t = total; + total += len; + var n = parseInt(total / step) - parseInt(t / step); + if (n > 0) { n.times(() => { print('.') }); } + if (fileLength) { + var updatePercentN = parseInt((total * percentN) / fileLength); + if (updatePercentN && (!(percentPrintFlag[updatePercentN]))) { + percentPrintFlag[updatePercentN] = 1; + print('[' + parseInt((total * 100) / fileLength) + '%]'); + } + } + }; +}; + +exports.getCounter2 = (dotStep) => { + var total = 0; + var step = dotStep || (1024 * 64); + var percentPrintFlag = {}; + return (len, fileLength) => { + var t = total; + total += len; + var n = parseInt(total / step) - parseInt(t / step); + if (n > 0) { n.times(() => { print('.') }); } + if (fileLength > 0) { + var updatePercent10 = parseInt((total * 10) / fileLength); + if (updatePercent10 && (!(percentPrintFlag[updatePercent10]))) { + percentPrintFlag[updatePercent10] = 1; + print('[' + parseInt((total * 100) / fileLength) + '%]'); + } + } + }; +}; + +exports.getCounter2Bar = (dotStep) => { + Packages.me.hatter.tools.jssp.main.StandaloneMainInteractive.initJLine(); // require jline + var total = 0; + var start = $$.date().millis(); + var step = dotStep || (1024 * 64); + var percentPrintFlag = {}; + return (len, fileLength) => { + var t = total; total += len; + var diff = $$.date().millis() - start; + var download = bytesjs.showBytes(total); + var speed = 'unknown'; + if (diff > 0) { + speed = bytesjs.showBytes(parseInt(total * 1000 / diff)) + '/s'; + } + if (fileLength > 0) { + var width = Packages.jline.TerminalFactory.get().getWidth(); + var per = ' ' + $$.num(total).mul(100).divr(fileLength, 2, null) + '% [' + download + ' - ' + speed + ']'; + var w = $$.num(total).divr((fileLength), 10, null).mul(width - 2 - per.length).floor().aint(); + reprint(repeat('>', w) + per); + } else { + reprint('Downloaded: ' + download + ', speed: ' + speed); + } + }; +}; + +exports.getCounterBar = (fileLength) => { + Packages.me.hatter.tools.jssp.main.StandaloneMainInteractive.initJLine(); // require jline + var total = 0; + return (len) => { + var width = Packages.jline.TerminalFactory.get().getWidth(); + total += len; + var per = ' ' + $$.num(total).mul(100).divr(fileLength, 2, null) + '%'; + var w = $$.num(total).divr((fileLength), 10, null).mul(width - 1 - per.length).floor().aint(); + print('\r' + repeat('>', w) + per + '\033[K'); + }; +}; diff --git a/components/component-dataaccessentity.js b/components/component-dataaccessentity.js new file mode 100644 index 0000000..ef90023 --- /dev/null +++ b/components/component-dataaccessentity.js @@ -0,0 +1,26 @@ +requireJAR('dataaccess-1.0.jar'); + +var InMemoryCompiler = Packages.me.hatter.tools.commons.classloader.compile.InMemoryCompiler; +var EntityJavaSourceCodeGenerateTool = Packages.me.hatter.tools.dataaccess.entity.EntityJavaSourceCodeGenerateTool; + +var systemInMemoryCompiler = InMemoryCompiler.systemInstance().updateClassPath(); +var allCompiledEntityClassMap = {}; + +var generateClass = (json) => { + var sourceCode = EntityJavaSourceCodeGenerateTool.instance().config(json).generate(); + var clazz = systemInMemoryCompiler.compile(sourceCode); + return clazz; +}; + +var getClass = (json) => { + var jsonSha256Hex = $$.digests().sha256().digest(json.getBytes('UTF-8')).asHex(); + if (allCompiledEntityClassMap[jsonSha256Hex]) { + return allCompiledEntityClassMap[jsonSha256Hex]; + } + var clazz = generateClass(json); + allCompiledEntityClassMap[jsonSha256Hex] = clazz; + return clazz; +}; + +exports.getClass = getClass; +exports.generateClass = generateClass; diff --git a/components/component-diffutil-ex.js b/components/component-diffutil-ex.js new file mode 100644 index 0000000..078a645 --- /dev/null +++ b/components/component-diffutil-ex.js @@ -0,0 +1,38 @@ +requireJAR('diffutils-1.3.0.jar'); + +var DiffUtils = Packages.difflib.DiffUtils; +var DeleteDelta = Packages.difflib.DeleteDelta; +var ChangeDelta = Packages.difflib.ChangeDelta; +var InsertDelta = Packages.difflib.InsertDelta; + +exports.diff2List = (left, right) => { + var patch = DiffUtils.diff(left, right); + var result = []; + $EACH(patch.getDeltas(), (delta) => { + if (delta instanceof DeleteDelta) { + result.push("@@deleted @" + delta.getOriginal().getPosition() + + " #" + delta.getOriginal().getLines().size()); + $EACH(delta.getOriginal().getLines(), (line) => { + result.push("-- " + line); + }) + } + if (delta instanceof ChangeDelta) { + result.push("@@modified @" + delta.getOriginal().getPosition() + + " #" + delta.getOriginal().getLines().size() + '-' + delta.getRevised().getLines().size()); + $EACH(delta.getOriginal().getLines(), (line) => { + result.push("-- " + line); + }); + $EACH(delta.getRevised().getLines(), (line) => { + result.push("++ " + line); + }); + } + if (delta instanceof InsertDelta) { + result.push("@@inserted @" + delta.getOriginal().getPosition() + + " #" + delta.getRevised().getLines().size()); + $EACH(delta.getRevised().getLines(), (line) => { + result.push("++ " + line); + }); + } + }); + return result; +}; diff --git a/components/component-diffutil.js b/components/component-diffutil.js new file mode 100644 index 0000000..57bf913 --- /dev/null +++ b/components/component-diffutil.js @@ -0,0 +1,9 @@ +requireJAR('diffutils-1.3.0.jar'); + +(() => { + var diff = require('component-diffutil-ex.js'); + + diff2List = (left, right) => { + return diff.diff2List(left, right); + }; +})(); diff --git a/components/component-dingtalkrobot-ex.js b/components/component-dingtalkrobot-ex.js new file mode 100644 index 0000000..b7a76b7 --- /dev/null +++ b/components/component-dingtalkrobot-ex.js @@ -0,0 +1,24 @@ + +exports.sendText = (token, message) => { + var dingTalkRobotURL = 'https://oapi.dingtalk.com/robot/send?access_token=' + token; + var postResult = $$.httpRequest() + .url(dingTalkRobotURL) + .addHeader('Content-Type', 'application/json') + .post(JSON.stringify({ + "msgtype": "text", + "text": { + "content": $STR(message) + } + })); + return postResult; +}; + +exports.sendTextDefault = (message) => { + var tokenFile = $$.file($$.prop('user.home'), '.component-dingtalkrobot.js.token'); + if (!tokenFile.exists()) { + println('DingTalk token file not exists: ' + tokenFile); + return; + } + var token = $$.rFile(tokenFile).string().trim(); + sendDingTalkRobotTextMessage(token, message); +}; diff --git a/components/component-dingtalkrobot.js b/components/component-dingtalkrobot.js new file mode 100644 index 0000000..1819f2b --- /dev/null +++ b/components/component-dingtalkrobot.js @@ -0,0 +1,11 @@ +(() => { + var dingtalkrobot = require('component-dingtalkrobot-ex.js'); + + sendDingTalkRobotTextMessage = (token, message) => { + return dingtalkrobot.sendText(token, message); + }; + + sendDingTalkRobotTextMessageDefault = (message) => { + return dingtalkrobot.sendTextDefault(message); + }; +})(); \ No newline at end of file diff --git a/components/component-filechooser-ex.js b/components/component-filechooser-ex.js new file mode 100644 index 0000000..57c0718 --- /dev/null +++ b/components/component-filechooser-ex.js @@ -0,0 +1,26 @@ + +var Proxy = java.lang.reflect.Proxy; +var Toolkit = java.awt.Toolkit; +var FileFilter = javax.swing.filechooser.FileFilter; +var JFileChooser = javax.swing.JFileChooser; + +exports.showDialog = (filter, filterName) => { + var fileFilter = new FileFilter({ + "getDescription": () => { + return filterName || 'Default Filter'; + }, + "accept": (f) => { + return filter(f); + } + }); + var chooser = new JFileChooser(); + if (filter != null) { + chooser.setFileFilter(fileFilter); + } + var ret = chooser.showOpenDialog(null); + + if (ret == JFileChooser.APPROVE_OPTION) { + return chooser.getSelectedFile(); + } + return null; +}; diff --git a/components/component-filechooser.js b/components/component-filechooser.js new file mode 100644 index 0000000..13f9619 --- /dev/null +++ b/components/component-filechooser.js @@ -0,0 +1,7 @@ +(() => { + var filechooser = require('component-filechooser-ex.js'); + + showFileOpenDialog = (filter, filterName) => { + return filechooser.showDialog(filter, filterName); + }; +})(); diff --git a/components/component-filesystem.js b/components/component-filesystem.js new file mode 100644 index 0000000..8c1cac1 --- /dev/null +++ b/components/component-filesystem.js @@ -0,0 +1,14 @@ +var Files = java.nio.file.Files; +var PosixFilePermission = java.nio.file.attribute.PosixFilePermission; + +var chmodAddExec = (file) => { + file = $$.file(file); + var perms = $$.set(); + perms.addAll(Files.getPosixFilePermissions(file.toPath())); + perms.add(PosixFilePermission.OWNER_EXECUTE); + Files.setPosixFilePermissions(file.toPath(), perms); +}; + +if (typeof exports == 'object') { + exports.chmodAddExec = chmodAddExec; +} diff --git a/components/component-geohash.js b/components/component-geohash.js new file mode 100644 index 0000000..69dd573 --- /dev/null +++ b/components/component-geohash.js @@ -0,0 +1,243 @@ +// https://github.com/chrisveness/latlon-geohash/blob/master/latlon-geohash.js +(() => { + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + /* Geohash encoding/decoding and associated functions (c) Chris Veness 2014-2016 / MIT Licence */ + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + 'use strict'; + + + /** + * Geohash encode, decode, bounds, neighbours. + * + * @namespace + */ + var Geohash = {}; + + /* (Geohash-specific) Base32 map */ + Geohash.base32 = '0123456789bcdefghjkmnpqrstuvwxyz'; + + /** + * Encodes latitude/longitude to geohash, either to specified precision or to automatically + * evaluated precision. + * + * @param {number} lat - Latitude in degrees. + * @param {number} lon - Longitude in degrees. + * @param {number} [precision] - Number of characters in resulting geohash. + * @returns {string} Geohash of supplied latitude/longitude. + * @throws Invalid geohash. + * + * @example + * var geohash = Geohash.encode(52.205, 0.119, 7); // geohash: 'u120fxw' + */ + exports.encode = Geohash.encode = function (lat, lon, precision) { + // infer precision? + if (typeof precision == 'undefined') { + // refine geohash until it matches precision of supplied lat/lon + for (var p = 1; p <= 12; p++) { + var hash = Geohash.encode(lat, lon, p); + var posn = Geohash.decode(hash); + if (posn.lat == lat && posn.lon == lon) return hash; + } + precision = 12; // set to maximum + } + + lat = Number(lat); + lon = Number(lon); + precision = Number(precision); + + if (isNaN(lat) || isNaN(lon) || isNaN(precision)) throw new Error('Invalid geohash'); + + var idx = 0; // index into base32 map + var bit = 0; // each char holds 5 bits + var evenBit = true; + var geohash = ''; + + var latMin = -90, latMax = 90; + var lonMin = -180, lonMax = 180; + + while (geohash.length < precision) { + if (evenBit) { + // bisect E-W longitude + var lonMid = (lonMin + lonMax) / 2; + if (lon >= lonMid) { + idx = idx * 2 + 1; + lonMin = lonMid; + } else { + idx = idx * 2; + lonMax = lonMid; + } + } else { + // bisect N-S latitude + var latMid = (latMin + latMax) / 2; + if (lat >= latMid) { + idx = idx * 2 + 1; + latMin = latMid; + } else { + idx = idx * 2; + latMax = latMid; + } + } + evenBit = !evenBit; + + if (++bit == 5) { + // 5 bits gives us a character: append it and start over + geohash += Geohash.base32.charAt(idx); + bit = 0; + idx = 0; + } + } + + return geohash; + }; + + + /** + * Decode geohash to latitude/longitude (location is approximate centre of geohash cell, + * to reasonable precision). + * + * @param {string} geohash - Geohash string to be converted to latitude/longitude. + * @returns {{lat:number, lon:number}} (Center of) geohashed location. + * @throws Invalid geohash. + * + * @example + * var latlon = Geohash.decode('u120fxw'); // latlon: { lat: 52.205, lon: 0.1188 } + */ + exports.decode = Geohash.decode = function (geohash) { + + var bounds = Geohash.bounds(geohash); // <-- the hard work + // now just determine the centre of the cell... + + var latMin = bounds.sw.lat, lonMin = bounds.sw.lon; + var latMax = bounds.ne.lat, lonMax = bounds.ne.lon; + + // cell centre + var lat = (latMin + latMax) / 2; + var lon = (lonMin + lonMax) / 2; + + // round to close to centre without excessive precision: ⌊2-log10(Δ°)⌋ decimal places + lat = lat.toFixed(Math.floor(2 - Math.log(latMax - latMin) / Math.LN10)); + lon = lon.toFixed(Math.floor(2 - Math.log(lonMax - lonMin) / Math.LN10)); + + return { lat: Number(lat), lon: Number(lon) }; + }; + + + /** + * Returns SW/NE latitude/longitude bounds of specified geohash. + * + * @param {string} geohash - Cell that bounds are required of. + * @returns {{sw: {lat: number, lon: number}, ne: {lat: number, lon: number}}} + * @throws Invalid geohash. + */ + exports.bounds = Geohash.bounds = function (geohash) { + if (geohash.length === 0) throw new Error('Invalid geohash'); + + geohash = geohash.toLowerCase(); + + var evenBit = true; + var latMin = -90, latMax = 90; + var lonMin = -180, lonMax = 180; + + for (var i = 0; i < geohash.length; i++) { + var chr = geohash.charAt(i); + var idx = Geohash.base32.indexOf(chr); + if (idx == -1) throw new Error('Invalid geohash'); + + for (var n = 4; n >= 0; n--) { + var bitN = idx >> n & 1; + if (evenBit) { + // longitude + var lonMid = (lonMin + lonMax) / 2; + if (bitN == 1) { + lonMin = lonMid; + } else { + lonMax = lonMid; + } + } else { + // latitude + var latMid = (latMin + latMax) / 2; + if (bitN == 1) { + latMin = latMid; + } else { + latMax = latMid; + } + } + evenBit = !evenBit; + } + } + + var bounds = { + sw: { lat: latMin, lon: lonMin }, + ne: { lat: latMax, lon: lonMax }, + }; + + return bounds; + }; + + + /** + * Determines adjacent cell in given direction. + * + * @param geohash - Cell to which adjacent cell is required. + * @param direction - Direction from geohash (N/S/E/W). + * @returns {string} Geocode of adjacent cell. + * @throws Invalid geohash. + */ + exports.adjacent = Geohash.adjacent = function (geohash, direction) { + // based on github.com/davetroy/geohash-js + + geohash = geohash.toLowerCase(); + direction = direction.toLowerCase(); + + if (geohash.length === 0) throw new Error('Invalid geohash'); + if ('nsew'.indexOf(direction) == -1) throw new Error('Invalid direction'); + + var neighbour = { + n: ['p0r21436x8zb9dcf5h7kjnmqesgutwvy', 'bc01fg45238967deuvhjyznpkmstqrwx'], + s: ['14365h7k9dcfesgujnmqp0r2twvyx8zb', '238967debc01fg45kmstqrwxuvhjyznp'], + e: ['bc01fg45238967deuvhjyznpkmstqrwx', 'p0r21436x8zb9dcf5h7kjnmqesgutwvy'], + w: ['238967debc01fg45kmstqrwxuvhjyznp', '14365h7k9dcfesgujnmqp0r2twvyx8zb'], + }; + var border = { + n: ['prxz', 'bcfguvyz'], + s: ['028b', '0145hjnp'], + e: ['bcfguvyz', 'prxz'], + w: ['0145hjnp', '028b'], + }; + + var lastCh = geohash.slice(-1); // last character of hash + var parent = geohash.slice(0, -1); // hash without last character + + var type = geohash.length % 2; + + // check for edge-cases which don't share common prefix + if (border[direction][type].indexOf(lastCh) != -1 && parent !== '') { + parent = Geohash.adjacent(parent, direction); + } + + // append letter for direction to parent + return parent + Geohash.base32.charAt(neighbour[direction][type].indexOf(lastCh)); + }; + + + /** + * Returns all 8 adjacent cells to specified geohash. + * + * @param {string} geohash - Geohash neighbours are required of. + * @returns {{n,ne,e,se,s,sw,w,nw: string}} + * @throws Invalid geohash. + */ + exports.neighbours = Geohash.neighbours = function (geohash) { + return { + 'n': Geohash.adjacent(geohash, 'n'), + 'ne': Geohash.adjacent(Geohash.adjacent(geohash, 'n'), 'e'), + 'e': Geohash.adjacent(geohash, 'e'), + 'se': Geohash.adjacent(Geohash.adjacent(geohash, 's'), 'e'), + 's': Geohash.adjacent(geohash, 's'), + 'sw': Geohash.adjacent(Geohash.adjacent(geohash, 's'), 'w'), + 'w': Geohash.adjacent(geohash, 'w'), + 'nw': Geohash.adjacent(Geohash.adjacent(geohash, 'n'), 'w'), + }; + }; +})(); \ No newline at end of file diff --git a/components/component-git.js b/components/component-git.js new file mode 100644 index 0000000..7cdec7b --- /dev/null +++ b/components/component-git.js @@ -0,0 +1,9 @@ + +exports.isGitUptodate = () => { + var r = $STR($$.shell().commands('git', 'fetch', '--dry-run').mergeError().start()[0]); + if (r.trim().length > 0) { + return false; + } + return true; +}; + diff --git a/components/component-gpg-ex.js b/components/component-gpg-ex.js new file mode 100644 index 0000000..1b3889b --- /dev/null +++ b/components/component-gpg-ex.js @@ -0,0 +1,32 @@ + +exports.encrypt = (keyId, srcFile, destFile) => { + $$.shell().commands('sh', '-c', 'gpg -r ' + keyId + ' -e -o "' + destFile + '" ' + srcFile).start(); +}; + +exports.encryptArmor = (keyId, srcFile, destFile) => { + $$.shell().commands('sh', '-c', 'gpg -r ' + keyId + ' -e -a --no-comment --comment "https://hatter.in/key" -o "' + destFile + '" ' + srcFile).start(); +}; + +exports.decryptArmor = (gpgArmor) => { + var gpgArmorB64 = __.bytes.from(gpgArmor).asBase64(); + var result = $$.shell().commands('sh', '-c', "echo '" + gpgArmorB64 + "' | base64 -D | gpg").start(); + return __decrypt(result); +}; + +exports.decrypt = (gpgFile) => { + var result = $$.shell().commands('sh', '-c', 'cat ' + gpgFile + ' | gpg').start(); + __decrypt(result); +}; + +var __decrypt = (result) => { + var out = result[0].string(); + var err = result[1].string(); + if (err.contains('public key decryption failed')) { + println('+ Decrypt file FAILED: ' + gpgFile); + if (!Packages.me.hatter.tools.jssp.main.StandaloneMain.JSSP_MAIN_MUTE) { + println("ERROR detail:\n" + err); + } + throw 'decrypt file failed: ' + gpgFile; + } + return out; +} diff --git a/components/component-gpg.js b/components/component-gpg.js new file mode 100644 index 0000000..f25056b --- /dev/null +++ b/components/component-gpg.js @@ -0,0 +1,15 @@ +(() => { + var gpg = require('component-gpg-ex.js'); + + gpgEncrypt = (keyId, srcFile, destFile) => { + return gpg.encrypt(keyId, srcFile, destFile); + }; + + gpgEncryptArmor = (keyId, srcFile, destFile) => { + return gpg.encryptArmor(keyId, srcFile, destFile); + }; + + gpgDecrypt = (gpgFile) => { + return gpg.decrypt(gpgFile); + }; +})(); diff --git a/components/component-hashjs.js b/components/component-hashjs.js new file mode 100644 index 0000000..ce7cbff --- /dev/null +++ b/components/component-hashjs.js @@ -0,0 +1,48 @@ +var COMMENT_HASH_TAG = '//HASH:'; + +var CollectionUtil = Packages.me.hatter.tools.commons.collection.CollectionUtil; + +var __containsCommentHashTag = (list) => { + for (var i = (list.size() - 1); i >= 0; i--) { + var l = list.get(i); + if ($STR(l.trim()) != '') { + return l.startsWith(COMMENT_HASH_TAG); + } + } + return false; +}; + +var __getDigestJSON = (list) => { + var digestJSON = null; + if (__containsCommentHashTag(list)) { + var last = CollectionUtil.last(list); + while ((list.size() > 0) && ($STR(last.trim()) == '')) { CollectionUtil.removeLast(list); last = CollectionUtil.last(list); } + if (last.startsWith(COMMENT_HASH_TAG)) { + CollectionUtil.removeLast(list); + digestJSON = JSON.parse(last.substring(COMMENT_HASH_TAG.length)); + } + } + return digestJSON; +}; + +var __calcSHA256Hex = (list) => { + return $STR($$.digests().sha256().digest($$.bytes($ARR(list).join('\n'))).asHex()); +}; + +var __checkSHA256 = (list) => { + var digestJSON = __getDigestJSON(list); + return digestJSON && (digestJSON.SHA256 == __calcSHA256Hex(list)); +}; + +var checkBytesSHA256 = (bytes) => { + return __checkSHA256(bytes.list()); +}; + +if (typeof exports == 'object') { + exports.__containsCommentHashTag = __containsCommentHashTag; + exports.__getDigestJSON = __getDigestJSON; + exports.__calcSHA256Hex = __calcSHA256Hex; + exports.__checkSHA256 = __checkSHA256; + exports.COMMENT_HASH_TAG = COMMENT_HASH_TAG; + exports.checkBytesSHA256 = checkBytesSHA256; +} diff --git a/components/component-helloworld.js b/components/component-helloworld.js new file mode 100644 index 0000000..6a3c410 --- /dev/null +++ b/components/component-helloworld.js @@ -0,0 +1,6 @@ +(() => { + helloworld = () => { + return 'Hello World!!!'; + }; +})(); + diff --git a/components/component-helloworld2.js b/components/component-helloworld2.js new file mode 100644 index 0000000..d4751aa --- /dev/null +++ b/components/component-helloworld2.js @@ -0,0 +1,5 @@ + +exports.sayhello = () => { + return 'Say Hello World!'; +}; + diff --git a/components/component-httpserver-ex.js b/components/component-httpserver-ex.js new file mode 100644 index 0000000..d5551f7 --- /dev/null +++ b/components/component-httpserver-ex.js @@ -0,0 +1,415 @@ + +var bytes = require('component-bytes.js'); + +var Runtime = java.lang.Runtime; +var InetSocketAddress = java.net.InetSocketAddress; +var Executors = java.util.concurrent.Executors; +var Bytes = Packages.me.hatter.tools.commons.bytes.Bytes; +var IOUtil = Packages.me.hatter.tools.commons.io.IOUtil; +var HttpServer = Packages.com.sun.net.httpserver.HttpServer; + +// { +// "status": 200, +// "headers": [[key, value],[key2, value2]], +// "responseJSON": ..., +// "responseBytes": ..., +// "responseText": ..., +// } +var writeResponse = (httpExchange, response) => { + response.status = (response.status == null) ? 200 : response.status; + var resBs; + var resStream = null; + if (response.responseJSON != null) { + resBs = Bytes.from(JSON.stringify(response.responseJSON)).getBytes(); + } else if (response.responseText != null) { + resBs = Bytes.from(response.responseText).getBytes(); + } else if (response.responseStream != null) { + resBs = null; + resStream = response.responseStream; + } else { + resBs = response.responseBytes; + } + var responseHeaders = httpExchange.getResponseHeaders(); + if ((response.headers != null) && (response.headers.length > 0)) { + for (var i = 0; i < response.headers.length; i++) { + var keyValue = response.headers[i]; + responseHeaders.set(keyValue[0], keyValue[1]); + } + } else { + responseHeaders.set('Content-Type', 'text/plain;charset=UTF-8'); + } + httpExchange.sendResponseHeaders(response.status, (resBs != null) ? resBs.length : 0); + var resBody = httpExchange.getResponseBody(); + if (resBs != null) { + resBody.write(resBs); + } else { + IOUtil.copy(response.responseStream, resBody); + IOUtil.closeQuietly(response.responseStream); + } + resBody.close(); +}; + +exports.parseQuery = exports.parseQueryParameterMap = (httpExchange) => { + var queryParameterMap = {}; + var rawQuery = $STR(httpExchange.getRequestURI().getRawQuery()); + if (rawQuery != null) { + rawQuery.split('&').forEach((kv) => { + var indexOfE = kv.indexOf('='); + var key = ''; + var value = ''; + if (indexOfE >= 0) { + key = kv.substring(0, indexOfE); + value = kv.substring(indexOfE + 1); + } else { + key = kv; + } + queryParameterMap[key] = queryParameterMap[key] || []; + queryParameterMap[key].push(value); + }); + } + return queryParameterMap; +}; + +var handleDir = (httpExchange, dir, opts) => { + var sb = []; + if (opts.handleDir) { + if (!httpExchange.getRequestURI().getPath().endsWith('/')) { + return { + status: 302, + "headers": [ + ["Location", httpExchange.getRequestURI().getPath() + '/'], + ["Content-Type", 'text/html;charset=UTF-8'], + ], + bytes: new java.lang.String('\n' + + '
\n' + + '